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.

1676 lines
46 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. #include "netmessages_signon.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. // Disable the creation of a listen server for online play.
  12. ConVar mm_disable_listen_server( "mm_disable_listen_server", "0", FCVAR_DEVELOPMENTONLY );
  13. //
  14. // CMatchSessionOnlineHost
  15. //
  16. // Implementation of an online session of a host machine
  17. //
  18. CMatchSessionOnlineHost::CMatchSessionOnlineHost( KeyValues *pSettings ) :
  19. m_pSettings( pSettings->MakeCopy() ),
  20. m_autodelete_pSettings( m_pSettings ),
  21. m_pSysData( new KeyValues( "SysSessionData", "type", "host" ) ),
  22. m_autodelete_pSysData( m_pSysData ),
  23. m_eState( STATE_INIT ),
  24. m_pSysSession( NULL ),
  25. m_pDsSearcher( NULL ),
  26. m_pTeamSearcher( NULL ),
  27. m_pMatchSearcher( NULL )
  28. {
  29. DevMsg( "Created CMatchSessionOnlineHost:\n" );
  30. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  31. // Generate an encryption cookie
  32. unsigned char chEncryptionCookie[ sizeof( uint64 ) ] = {};
  33. for ( int j = 0; j < ARRAYSIZE( chEncryptionCookie ); ++ j )
  34. chEncryptionCookie[j] = RandomInt( 1, 254 );
  35. m_pSysData->SetUint64( "crypt", * reinterpret_cast< uint64 * >( chEncryptionCookie ) );
  36. InitializeGameSettings();
  37. }
  38. CMatchSessionOnlineHost::CMatchSessionOnlineHost( CSysSessionClient *pSysSession, KeyValues *pExtendedSettings ) :
  39. m_pSettings( pExtendedSettings->FindKey( "settings" ) ),
  40. m_autodelete_pSettings( m_pSettings ),
  41. m_pSysData( new KeyValues( "SysSessionData", "type", "host" ) ),
  42. m_autodelete_pSysData( m_pSysData ),
  43. m_eState( STATE_LOBBY ), // it's at least lobby, we'll figure out later
  44. m_pSysSession( NULL ),
  45. m_pDsSearcher( NULL ),
  46. m_pTeamSearcher( NULL ),
  47. m_pMatchSearcher( NULL )
  48. {
  49. Assert( m_pSettings );
  50. KeyValues::AutoDelete autodelete( pExtendedSettings );
  51. pExtendedSettings->RemoveSubKey( m_pSettings ); // it's now our settings
  52. // Carry over encryption cookie
  53. uint64 ullCrypt = pExtendedSettings->GetUint64( "crypt" );
  54. if ( ullCrypt )
  55. m_pSysData->SetUint64( "crypt", ullCrypt );
  56. // Install our session
  57. g_pMMF->SetCurrentMatchSession( this );
  58. // Check if the game is in a non-lobby state
  59. char const *szState = pExtendedSettings->GetString( "state", "" );
  60. if ( !Q_stricmp( szState, "game" ) )
  61. m_eState = STATE_GAME;
  62. else if ( !Q_stricmp( szState, "ending" ) )
  63. m_eState = STATE_ENDING;
  64. // Now we need to create the system session to reflect the client session passed
  65. m_pSysSession = new CSysSessionHost( pSysSession, m_pSettings );
  66. if ( ullCrypt )
  67. m_pSysSession->SetCryptKey( ullCrypt );
  68. pSysSession->Destroy();
  69. // Now we need to clean up some transient leftovers from incomplete operations
  70. // started by previous host
  71. MigrateGameSettings();
  72. // Show the state
  73. DevMsg( "Migrated into CMatchSessionOnlineHost:\n" );
  74. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  75. }
  76. CMatchSessionOnlineHost::~CMatchSessionOnlineHost()
  77. {
  78. DevMsg( "Destroying CMatchSessionOnlineHost:\n" );
  79. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  80. }
  81. KeyValues * CMatchSessionOnlineHost::GetSessionSystemData()
  82. {
  83. // Setup our sys data
  84. m_pSysData->SetUint64( "xuidReserve", m_pSysSession ? m_pSysSession->GetReservationCookie() : 0ull );
  85. m_pSysData->SetUint64( "xuidHost", m_pSysSession ? m_pSysSession->GetHostXuid() : 0ull );
  86. switch ( m_eState )
  87. {
  88. case STATE_LOBBY:
  89. m_pSysData->SetString( "state", "lobby" );
  90. break;
  91. case STATE_GAME:
  92. m_pSysData->SetString( "state", "game" );
  93. break;
  94. default:
  95. m_pSysData->SetString( "state", "" );
  96. break;
  97. }
  98. return m_pSysData;
  99. }
  100. KeyValues * CMatchSessionOnlineHost::GetSessionSettings()
  101. {
  102. return m_pSettings;
  103. }
  104. void CMatchSessionOnlineHost::UpdateSessionSettings( KeyValues *pSettings )
  105. {
  106. if ( m_eState < STATE_LOBBY )
  107. {
  108. Warning( "CMatchSessionOnlineHost::UpdateSessionSettings is unavailable in state %d!\n", m_eState );
  109. Assert( !"CMatchSessionOnlineHost::UpdateSessionSettings is unavailable!\n" );
  110. return;
  111. }
  112. // Let the title extend the game update keys
  113. g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsUpdateKeys( m_pSettings, pSettings );
  114. m_pSettings->MergeFrom( pSettings );
  115. DevMsg( "CMatchSessionOnlineHost::UpdateSessionSettings\n");
  116. KeyValuesDumpAsDevMsg( m_pSettings );
  117. if ( m_pSysSession )
  118. {
  119. m_pSysSession->UpdateMembersInfo();
  120. m_pSysSession->OnUpdateSessionSettings( pSettings );
  121. }
  122. // Broadcast the update to everybody interested
  123. MatchSession_BroadcastSessionSettingsUpdate( pSettings );
  124. }
  125. void CMatchSessionOnlineHost::UpdateTeamProperties( KeyValues *pSettings )
  126. {
  127. m_pSysSession->UpdateTeamProperties( pSettings );
  128. }
  129. void CMatchSessionOnlineHost::Command( KeyValues *pCommand )
  130. {
  131. char const *szCommand, *szRun;
  132. szCommand = pCommand->GetName();
  133. szRun = pCommand->GetString( "run", "" );
  134. if ( !Q_stricmp( szRun, "all" ) || !Q_stricmp( szRun, "clients" ) || !Q_stricmp( szRun, "xuid" ) )
  135. {
  136. if ( m_pSysSession )
  137. {
  138. m_pSysSession->Command( pCommand );
  139. return;
  140. }
  141. }
  142. else if ( !*szRun || !Q_stricmp( szRun, "local" ) || !Q_stricmp( szRun, "host" ) )
  143. {
  144. OnRunCommand( pCommand );
  145. return;
  146. }
  147. Warning( "CMatchSessionOnlineClient::Command( %s ) unhandled!\n", szCommand );
  148. Assert( !"CMatchSessionOnlineClient::Command" );
  149. }
  150. void CMatchSessionOnlineHost::OnRunCommand( KeyValues *pCommand )
  151. {
  152. char const *szCommand = pCommand->GetName();
  153. if ( !Q_stricmp( "Start", szCommand ) )
  154. {
  155. if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) )
  156. {
  157. OnRunCommand_Start();
  158. return;
  159. }
  160. }
  161. if ( !Q_stricmp( "Match", szCommand ) )
  162. {
  163. if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) )
  164. {
  165. OnRunCommand_Match();
  166. return;
  167. }
  168. }
  169. if ( !Q_stricmp( "Cancel", szCommand ) )
  170. {
  171. if ( !pCommand->GetUint64( "_remote_xuidsrc" ) )
  172. {
  173. switch ( m_eState )
  174. {
  175. case STATE_STARTING:
  176. OnRunCommand_Cancel_DsSearch();
  177. return;
  178. case STATE_MATCHING:
  179. OnRunCommand_Cancel_Match();
  180. return;
  181. }
  182. }
  183. }
  184. if ( !Q_stricmp( "Kick", szCommand ) )
  185. {
  186. if ( m_pSysSession && !pCommand->GetUint64( "_remote_xuidsrc" ) )
  187. {
  188. m_pSysSession->KickPlayer( pCommand );
  189. return;
  190. }
  191. }
  192. if ( !Q_stricmp( "Migrate", szCommand ) )
  193. {
  194. if ( m_pSysSession ) // TODO: research who sends the "Migrate" command and how secure it is?
  195. {
  196. m_pSysSession->Migrate( pCommand );
  197. return;
  198. }
  199. }
  200. if ( !Q_stricmp( "QueueConnect", szCommand ) )
  201. {
  202. if ( ( m_eState == STATE_LOBBY ) && !pCommand->GetUint64( "_remote_xuidsrc" ) )
  203. {
  204. OnRunCommand_QueueConnect( pCommand );
  205. return;
  206. }
  207. }
  208. //
  209. // Let the title-specific matchmaking handle the command
  210. //
  211. CUtlVector< KeyValues * > arrPlayersUpdated;
  212. arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
  213. memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
  214. g_pMMF->GetMatchTitleGameSettingsMgr()->ExecuteCommand( pCommand, GetSessionSystemData(), m_pSettings, arrPlayersUpdated.Base() );
  215. // Now notify the framework about player updated
  216. for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
  217. {
  218. if ( !arrPlayersUpdated[k] )
  219. break;
  220. Assert( m_pSysSession );
  221. if ( m_pSysSession )
  222. {
  223. m_pSysSession->OnPlayerUpdated( arrPlayersUpdated[k] );
  224. }
  225. }
  226. //
  227. // Send the command as event for handling
  228. //
  229. KeyValues *pEvent = pCommand->MakeCopy();
  230. pEvent->SetName( CFmtStr( "Command::%s", pCommand->GetName() ) );
  231. g_pMatchEventsSubscription->BroadcastEvent( pEvent );
  232. }
  233. void CMatchSessionOnlineHost::OnRunCommand_Start()
  234. {
  235. // First of all flip our state
  236. m_eState = STATE_STARTING;
  237. // Now modify the state of our game
  238. UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString(
  239. "update",
  240. " update { "
  241. " system { "
  242. " lock starting "
  243. " } "
  244. " } "
  245. ) ) );
  246. // Prepare lobby for game
  247. OnGamePrepareLobbyForGame();
  248. // Search for a server
  249. m_pDsSearcher = new CDsSearcher( m_pSettings, m_pSysSession->GetReservationCookie(), this );
  250. }
  251. void CMatchSessionOnlineHost::OnRunCommand_Match()
  252. {
  253. // First of all flip our state
  254. m_eState = STATE_MATCHING;
  255. // Now modify the state of our game
  256. UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString(
  257. "update",
  258. " update { "
  259. " system { "
  260. " lock matching "
  261. " } "
  262. " } "
  263. ) ) );
  264. // Prepare lobby for game
  265. OnGamePrepareLobbyForGame();
  266. // Search for opponents
  267. if ( m_pTeamSearcher )
  268. {
  269. m_pTeamSearcher->Destroy();
  270. m_pTeamSearcher = NULL;
  271. }
  272. if ( m_pMatchSearcher )
  273. {
  274. m_pMatchSearcher->Destroy();
  275. m_pMatchSearcher = NULL;
  276. }
  277. KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
  278. if ( teamMatch )
  279. {
  280. const char *serverType = m_pSettings->GetString( "options/server", "official" );
  281. if ( Q_stricmp( serverType, "listen" ) )
  282. {
  283. m_pMatchSearcher = new CMatchSessionOnlineSearch( m_pSettings );
  284. }
  285. else
  286. {
  287. // Transition into loading state
  288. m_eState = STATE_LOADING;
  289. StartListenServerMap();
  290. }
  291. }
  292. else
  293. {
  294. m_pTeamSearcher = new CMatchSessionOnlineTeamSearch( m_pSettings, this );
  295. }
  296. }
  297. void CMatchSessionOnlineHost::OnRunCommand_Cancel_Match()
  298. {
  299. // Destroy the matchmaking search state
  300. Assert( m_pTeamSearcher );
  301. if ( m_pTeamSearcher )
  302. {
  303. m_pTeamSearcher->Destroy();
  304. m_pTeamSearcher = NULL;
  305. }
  306. if ( m_pMatchSearcher )
  307. {
  308. m_pMatchSearcher->Destroy();
  309. m_pMatchSearcher = NULL;
  310. }
  311. // Flip the state back to lobby
  312. m_eState = STATE_LOBBY;
  313. // Now unlock the state of our game
  314. UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString(
  315. "update",
  316. " update { "
  317. " system { "
  318. " lock #empty# "
  319. " } "
  320. " } "
  321. ) ) );
  322. }
  323. void CMatchSessionOnlineHost::OnRunCommand_Cancel_DsSearch()
  324. {
  325. // Destroy the dedicated search state
  326. Assert( m_pDsSearcher );
  327. if ( m_pDsSearcher )
  328. {
  329. m_pDsSearcher->Destroy();
  330. m_pDsSearcher = NULL;
  331. }
  332. // Flip the state back to lobby
  333. m_eState = STATE_LOBBY;
  334. // Now unlock the state of our game
  335. UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString(
  336. "update",
  337. " update { "
  338. " system { "
  339. " lock #empty# "
  340. " } "
  341. " } "
  342. ) ) );
  343. }
  344. void CMatchSessionOnlineHost::OnRunCommand_StartDsSearchFinished()
  345. {
  346. // Retrieve the result and destroy the searcher
  347. Assert( m_pDsSearcher );
  348. CDsSearcher::DsResult_t dsResult = m_pDsSearcher->GetResult();
  349. if ( dsResult.m_bAborted )
  350. {
  351. OnRunCommand_Cancel_DsSearch();
  352. return;
  353. }
  354. m_pDsSearcher->Destroy();
  355. m_pDsSearcher = NULL;
  356. // Handle console team matchmaking case here - if we did not find a ds then
  357. // just go back to the lobby
  358. KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
  359. if ( teamMatch && !dsResult.m_bDedicated )
  360. {
  361. OnRunCommand_Cancel_DsSearch();
  362. OnRunCommand_Match(); // restart the search
  363. return;
  364. }
  365. #if defined _GAMECONSOLE
  366. if ( !dsResult.m_bDedicated &&
  367. m_pSettings->GetString( "server/server", NULL ) )
  368. {
  369. // We should be connecting to the dedicated server, but we
  370. // failed to reserve it, just bail out with an error
  371. KeyValues *notify = new KeyValues( "mmF->SysSessionUpdate" );
  372. notify->SetPtr( "syssession", m_pSysSession );
  373. notify->SetString( "error", "n/a" );
  374. g_pMatchEventsSubscription->BroadcastEvent( notify );
  375. return;
  376. }
  377. if ( !dsResult.m_bDedicated )
  378. {
  379. // Transition into loading state
  380. m_eState = STATE_LOADING;
  381. StartListenServerMap();
  382. return;
  383. }
  384. #else
  385. // On PC if we fail to find or reserve a DS then bail
  386. if ( !dsResult.m_bDedicated )
  387. {
  388. // We should be connecting to the dedicated server, but we
  389. // failed to reserve it, just bail out with an error
  390. KeyValues *notify = new KeyValues( "mmF->SysSessionUpdate" );
  391. notify->SetPtr( "syssession", m_pSysSession );
  392. notify->SetString( "error", "Could not find or connect to a DS" );
  393. g_pMatchEventsSubscription->BroadcastEvent( notify );
  394. return;
  395. }
  396. #endif
  397. //
  398. // We have reserved a dedicated server
  399. //
  400. // Prepare the update - creating the "server" key signals that
  401. // a game server is available
  402. KeyValues *kvUpdate = KeyValues::FromString(
  403. "update",
  404. " update { "
  405. " system { "
  406. " lock #empty# "
  407. " } "
  408. " server { "
  409. " server dedicated "
  410. " } "
  411. " } "
  412. );
  413. KeyValues::AutoDelete autodelete( kvUpdate );
  414. KeyValues *kvServer = kvUpdate->FindKey( "update/server" );
  415. //
  416. // Publish the addresses of the server
  417. //
  418. bool bWasEncrypted = ( '$' == m_pSettings->GetString( "server/adronline" )[0] );
  419. dsResult.CopyToServerKey( kvServer, bWasEncrypted ? m_pSysData->GetUint64( "crypt" ) : 0ull );
  420. // Remove the lock from the session and allow joins and trigger clients connect
  421. UpdateSessionSettings( kvUpdate );
  422. // Add server info to lobby settings
  423. m_pSysSession->UpdateServerInfo( m_pSettings );
  424. //
  425. // Actually connect to game server
  426. //
  427. ConnectGameServer( &dsResult );
  428. }
  429. void CMatchSessionOnlineHost::OnRunCommand_StartListenServerStarted( uint32 externalIP )
  430. {
  431. // If this is a team match and we do not yet have a public IP, because the server
  432. // logon is not complete, then return and wait for another call when the
  433. // server public IP is available
  434. // KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
  435. // if ( teamMatch != NULL ) // <vitaliy - we probably don't care here, we'll use P2P anyways in public> && externalIP == 0)
  436. // {
  437. // return;
  438. // }
  439. // Switch the state
  440. m_eState = STATE_GAME;
  441. // Prepare the update - creating the "server" key signals that
  442. // a game server is available
  443. KeyValues *kvUpdate = KeyValues::FromString(
  444. "update",
  445. " update { "
  446. " system { "
  447. " lock #empty# "
  448. " } "
  449. " server { "
  450. " server listen "
  451. " } "
  452. " } "
  453. );
  454. KeyValues::AutoDelete autodelete( kvUpdate );
  455. KeyValues *kvServer = kvUpdate->FindKey( "update/server" );
  456. if ( !IsX360() )
  457. {
  458. // Let everybody know server info of the server that they should join
  459. INetSupport::ServerInfo_t si;
  460. memset( &si, 0, sizeof( si ) );
  461. g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
  462. if ( externalIP != 0)
  463. {
  464. si.m_netAdrOnline.SetIP( externalIP );
  465. }
  466. kvServer->SetString( "adrlocal", MatchSession_EncryptAddressString( si.m_netAdr.ToString(), m_pSysData->GetUint64( "crypt" ) ) );
  467. kvServer->SetString( "adronline", MatchSession_EncryptAddressString( si.m_netAdrOnline.ToString(), m_pSysData->GetUint64( "crypt" ) ) );
  468. // For listen servers we also expose our Steam ID for libjingle
  469. kvServer->SetUint64( "xuid", g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID() );
  470. }
  471. // Set the server reservation appropriately for clients to connect
  472. uint64 uiReservationCookie = m_pSysSession->GetReservationCookie();
  473. g_pMatchExtensions->GetINetSupport()->UpdateServerReservation( uiReservationCookie );
  474. kvServer->SetUint64( "reservationid", uiReservationCookie );
  475. if ( m_pTeamSearcher ) // for team-on-team game the listen server is team 1
  476. kvServer->SetInt( "team", 1 );
  477. if ( m_pTeamSearcher )
  478. {
  479. if ( CSysSessionHost *pSysSessionHost = dynamic_cast< CSysSessionHost * >( m_pTeamSearcher->LinkSysSession() ) )
  480. {
  481. #ifdef _X360
  482. char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
  483. pSysSessionHost->GetHostSessionInfo( chSessionInfo );
  484. kvServer->SetString( "sessioninfo", chSessionInfo );
  485. #endif
  486. }
  487. }
  488. // Remove the lock from the session and allow joins and trigger clients connect
  489. UpdateSessionSettings( kvUpdate );
  490. // Add server info to lobby settings
  491. m_pSysSession->UpdateServerInfo( m_pSettings );
  492. // Mark the local session as active
  493. SetSessionActiveGameplayState( true, NULL );
  494. // Run the extra Update on the teamsearcher if it is alive
  495. if ( m_pTeamSearcher )
  496. m_pTeamSearcher->Update();
  497. InviteTeam();
  498. }
  499. void CMatchSessionOnlineHost::OnRunCommand_QueueConnect( KeyValues *pCommand )
  500. {
  501. char const *szConnectAddress = pCommand->GetString( "adronline", "0.0.0.0" );
  502. uint64 uiReservationId = pCommand->GetUint64( "reservationid" );
  503. bool bAutoCloseSession = pCommand->GetBool( "auto_close_session" );
  504. // Switch the state
  505. m_eState = STATE_GAME;
  506. MatchSession_PrepareClientForConnect( m_pSettings, uiReservationId );
  507. // Mark gameplay state as active
  508. SetSessionActiveGameplayState( true, szConnectAddress );
  509. // Close the session, potentially resetting a bunch of state
  510. if ( bAutoCloseSession )
  511. g_pMatchFramework->CloseSession();
  512. // Determine reservation settings required
  513. g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( uiReservationId, 0ull );
  514. // Issue the connect command
  515. g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( CFmtStr( "connect %s", szConnectAddress ) );
  516. }
  517. void CMatchSessionOnlineHost::ConnectGameServer( CDsSearcher::DsResult_t *pDsResult )
  518. {
  519. // Switch the state
  520. m_eState = STATE_GAME;
  521. MatchSession_PrepareClientForConnect( m_pSettings );
  522. //
  523. // Resolve server information
  524. //
  525. MatchSessionServerInfo_t msInfo = {0};
  526. if ( pDsResult )
  527. msInfo.m_dsResult = *pDsResult;
  528. // Flags
  529. uint uiResolveServerInfoFlags = msInfo.RESOLVE_CONNECTSTRING | msInfo.RESOLVE_QOS_RATE_PROBE;
  530. if ( !pDsResult )
  531. uiResolveServerInfoFlags |= msInfo.RESOLVE_DSRESULT;
  532. if ( m_pTeamSearcher )
  533. uiResolveServerInfoFlags |= msInfo.RESOLVE_ALLOW_EXTPEER;
  534. // Client session
  535. CSysSessionBase *pSysSessionForGameServer = m_pSysSession;
  536. if ( m_pTeamSearcher )
  537. pSysSessionForGameServer = m_pTeamSearcher->LinkSysSession();
  538. if ( !MatchSession_ResolveServerInfo( m_pSettings, pSysSessionForGameServer, msInfo, uiResolveServerInfoFlags, m_pSysData->GetUint64( "crypt" ) ) )
  539. {
  540. if ( m_pTeamSearcher )
  541. {
  542. m_eState = STATE_MATCHINGRESTART;
  543. return;
  544. }
  545. // Destroy the session
  546. m_pSysSession->Destroy();
  547. m_pSysSession = NULL;
  548. // Handle error
  549. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate",
  550. "state", "error", "error", "connect" ) );
  551. return;
  552. }
  553. // Determine reservation settings required
  554. g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( msInfo.m_uiReservationCookie, msInfo.m_xuidJingle );
  555. // Mark gameplay state as active
  556. SetSessionActiveGameplayState( true, msInfo.m_szSecureServerAddress );
  557. // Issue the connect command
  558. g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( msInfo.m_szConnectCmd );
  559. // Tell the rest of the team
  560. InviteTeam();
  561. }
  562. void CMatchSessionOnlineHost::StartListenServerMap()
  563. {
  564. UpdateSessionSettings( KeyValues::AutoDeleteInline ( KeyValues::FromString(
  565. "update",
  566. " update { "
  567. " system { "
  568. " lock loading "
  569. " } "
  570. " } "
  571. " delete { "
  572. " server delete "
  573. " } "
  574. ) ) );
  575. // Note: in case of a team-on-team game we don't yet have "server" key
  576. // and don't put the explicit team nomination, but the title must designate
  577. // the listen server host to be on team 1.
  578. MatchSession_PrepareClientForConnect( m_pSettings );
  579. // Before starting a listen server map ensure we have the map name set
  580. g_pMMF->GetMatchTitleGameSettingsMgr()->SetBspnameFromMapgroup( m_pSettings );
  581. if ( !mm_disable_listen_server.GetBool() )
  582. {
  583. bool bResult = g_pMatchFramework->GetMatchTitle()->StartServerMap( m_pSettings );
  584. if ( !bResult )
  585. {
  586. Warning( "Failed to start server map!\n" );
  587. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  588. Assert( 0 );
  589. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "nomap" ) );
  590. }
  591. Msg( "Succeeded in starting server map!\n" );
  592. }
  593. else
  594. {
  595. Msg( "Failed to start server map because mm_disable_listen_server was enabled.\n" );
  596. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "listen server disabled" ) );
  597. }
  598. }
  599. void CMatchSessionOnlineHost::OnEndGameToLobby()
  600. {
  601. m_eState = STATE_ENDING;
  602. // Remove server information
  603. UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  604. "updatedelete",
  605. " update { "
  606. " system { "
  607. " lock endgame "
  608. " } "
  609. " } "
  610. " delete { "
  611. " server delete "
  612. " } "
  613. ) ) );
  614. // Issue the disconnect command
  615. g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
  616. g_pMatchExtensions->GetINetSupport()->UpdateServerReservation( 0ull );
  617. // Mark gameplay state as inactive
  618. SetSessionActiveGameplayState( false, NULL );
  619. }
  620. void CMatchSessionOnlineHost::SetSessionActiveGameplayState( bool bActive, char const *szSecureServerAddress )
  621. {
  622. if ( m_pTeamSearcher )
  623. {
  624. // Mark gameplay state as inactive
  625. if ( !bActive )
  626. {
  627. m_pTeamSearcher->Destroy();
  628. m_pTeamSearcher = NULL;
  629. }
  630. else
  631. {
  632. // TODO: m_pTeamSearcher->SetSessionActiveGameplayState
  633. }
  634. }
  635. m_pSysSession->SetSessionActiveGameplayState( bActive, szSecureServerAddress );
  636. }
  637. void CMatchSessionOnlineHost::OnGamePrepareLobbyForGame()
  638. {
  639. // Remember which players will get updated
  640. CUtlVector< KeyValues * > arrPlayersUpdated;
  641. arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
  642. memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
  643. g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareLobbyForGame( m_pSettings, arrPlayersUpdated.Base() );
  644. //
  645. // Now notify the framework about player updated
  646. //
  647. for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
  648. {
  649. if ( !arrPlayersUpdated[k] )
  650. break;
  651. Assert( m_pSysSession );
  652. if ( m_pSysSession )
  653. {
  654. m_pSysSession->OnPlayerUpdated( arrPlayersUpdated[k] );
  655. }
  656. }
  657. }
  658. void CMatchSessionOnlineHost::OnGamePlayerMachinesConnected( int numMachines )
  659. {
  660. if ( m_eState != STATE_GAME )
  661. return;
  662. // Remember which players will get updated
  663. CUtlVector< KeyValues * > arrPlayersUpdated;
  664. arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
  665. memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
  666. g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareLobbyForGame( m_pSettings, arrPlayersUpdated.Base() );
  667. #ifdef _DEBUG
  668. // Theoretically only new machines should be affected by the callback,
  669. // so in debug mode we are verifying that.
  670. // The logic is actually somewhat complicated - see sys_session implementation
  671. // of order in which OnPlayerMachinesConnected and OnPlayerUpdated are fired.
  672. // All adjustments to the connecting players should be made before OnPlayerUpdated
  673. // gets fired so that OnPlayerUpdated("joined") was fired with all valid settings.
  674. // Current total number of machines
  675. int numMachinesTotal = m_pSettings->GetInt( "members/numMachines", 0 );
  676. // For the players from the old machines we would need to send updates
  677. for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
  678. {
  679. if ( !arrPlayersUpdated[k] )
  680. break;
  681. bool bNewMachine = false;
  682. XUID xuidPlayer = arrPlayersUpdated[k]->GetUint64( "xuid" );
  683. KeyValues *pMachine = NULL;
  684. SessionMembersFindPlayer( m_pSettings, xuidPlayer, &pMachine );
  685. if ( pMachine )
  686. {
  687. char const *szMachine = pMachine->GetName();
  688. if ( char const *szMachineNumber = StringAfterPrefix( szMachine, "machine" ) )
  689. {
  690. int iMachineNumber = atoi( szMachineNumber );
  691. if ( iMachineNumber >= numMachinesTotal - numMachines )
  692. bNewMachine = true;
  693. }
  694. }
  695. Assert( bNewMachine );
  696. if ( !bNewMachine )
  697. {
  698. Assert( m_pSysSession );
  699. if ( m_pSysSession )
  700. {
  701. m_pSysSession->OnPlayerUpdated( arrPlayersUpdated[k] );
  702. }
  703. }
  704. }
  705. #endif
  706. }
  707. uint64 CMatchSessionOnlineHost::GetSessionID()
  708. {
  709. if( m_pSysSession )
  710. {
  711. return m_pSysSession->GetSessionID();
  712. }
  713. return 0;
  714. }
  715. void CMatchSessionOnlineHost::InviteTeam()
  716. {
  717. return; // this path is no longer needed
  718. DevMsg( "InviteTeam\n" );
  719. KeyValuesDumpAsDevMsg( m_pSettings );
  720. KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
  721. if ( teamMatch == NULL )
  722. {
  723. return;
  724. }
  725. // Reserve session for team
  726. int numTeamPlayers = m_pSettings->GetInt( "members/numPlayers" );
  727. uint64 teamResKey = m_pSettings->GetUint64( "members/machine0/id", 0 );
  728. m_pSysSession->ReserveTeamSession( teamResKey, numTeamPlayers - 1);
  729. // Update lobby settings so that non-team members can join via matchmaking
  730. KeyValues *pUpdate = g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendTeamLobbyToGame( m_pSettings );
  731. UpdateSessionSettings( pUpdate );
  732. pUpdate->deleteThis();
  733. g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareForSessionCreate( m_pSettings );
  734. // Set all the properties of the new session and send them across
  735. KeyValues *pSettings = KeyValues::FromString(
  736. "update",
  737. " update { "
  738. " options { "
  739. " action joinsession "
  740. " } "
  741. " } "
  742. );
  743. KeyValues::AutoDelete autodelete_settings( pSettings );
  744. pUpdate = pSettings->FindKey( "update" );
  745. KeyValues *pOptions = pUpdate->FindKey( "options" );
  746. pOptions->SetUint64( "teamResKey", teamResKey );
  747. #ifdef _X360
  748. char chSessionInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
  749. m_pSysSession->GetHostSessionInfo( chSessionInfo );
  750. pOptions->SetString( "sessioninfo", chSessionInfo );
  751. pOptions->SetUint64( "sessionid", m_pSysSession->GetHostSessionId() );
  752. #else
  753. pOptions->SetUint64( "sessionid", m_pSysSession->GetSessionID() );
  754. #endif
  755. // Set up "teammembers" key
  756. KeyValues *pTeamMembers = pUpdate->CreateNewKey();
  757. pTeamMembers->SetName( "teamMembers" );
  758. // Iterate "members" key
  759. KeyValues *pSessionMembers = m_pSettings->FindKey( "members" );
  760. pTeamMembers->SetInt( "numPlayers", numTeamPlayers );
  761. // Choose a side to join - in keyvalues, team CT = 1, team T = 2
  762. int team = RandomInt(1, 2);
  763. int otherTeam = 3 - team;
  764. for ( int i = 0; i < numTeamPlayers; i++ )
  765. {
  766. if ( i == 5)
  767. {
  768. // Switch teams
  769. team = otherTeam;
  770. }
  771. KeyValues *pTeamPlayer = pTeamMembers->CreateNewKey();
  772. pTeamPlayer->SetName( CFmtStr( "player%d", i ) );
  773. KeyValues *pSessionMember = pSessionMembers->FindKey( CFmtStr( "machine%d", i ) );
  774. uint64 playerId = pSessionMember->GetUint64( "id" );
  775. pTeamPlayer->SetUint64( "xuid", playerId );
  776. pTeamPlayer->SetInt( "team", team );
  777. }
  778. KeyValues *notify = new KeyValues( "SysSession::OnUpdate" );
  779. KeyValues::AutoDelete autodelete_notify( notify );
  780. notify->AddSubKey( pUpdate->MakeCopy() );
  781. m_pSysSession->SendMessage( notify );
  782. // Make sure we update host settings to include "conteam" otherwise host
  783. // won't know what side to join
  784. m_pSettings->SetInt( "conteam", team );
  785. }
  786. void CMatchSessionOnlineHost::Update()
  787. {
  788. switch ( m_eState )
  789. {
  790. case STATE_INIT:
  791. m_eState = STATE_CREATING;
  792. // Session is creating
  793. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues(
  794. "OnMatchSessionUpdate",
  795. "state", "progress",
  796. "progress", "creating"
  797. ) );
  798. // Trigger session creation
  799. m_pSysSession = new CSysSessionHost( m_pSettings );
  800. m_pSysSession->SetCryptKey( m_pSysData->GetUint64( "crypt" ) );
  801. break;
  802. case STATE_STARTING:
  803. Assert( m_pDsSearcher );
  804. if ( m_pDsSearcher )
  805. {
  806. m_pDsSearcher->Update();
  807. if ( m_pDsSearcher->IsFinished() )
  808. {
  809. OnRunCommand_StartDsSearchFinished();
  810. return;
  811. }
  812. }
  813. break;
  814. case STATE_MATCHING:
  815. Assert( m_pTeamSearcher );
  816. if ( m_pTeamSearcher )
  817. {
  818. m_pTeamSearcher->Update();
  819. }
  820. if ( m_pMatchSearcher )
  821. {
  822. m_pMatchSearcher->Update();
  823. CMatchSessionOnlineSearch::Result result = m_pMatchSearcher->GetResult();
  824. if ( result == CMatchSessionOnlineSearch::RESULT_FAIL )
  825. {
  826. KeyValues *teamMatch = m_pSettings->FindKey( "options/conteammatch" );
  827. if ( teamMatch )
  828. {
  829. OnRunCommand_Cancel_Match();
  830. OnRunCommand_Start();
  831. }
  832. }
  833. else if (result == CMatchSessionOnlineSearch::RESULT_SUCCESS )
  834. {
  835. m_pMatchSearcher->Destroy();
  836. m_pMatchSearcher = NULL;
  837. }
  838. }
  839. break;
  840. case STATE_MATCHINGRESTART:
  841. OnRunCommand_Match();
  842. break;
  843. case STATE_LOBBY:
  844. // If we're in the lobby and are setup to bypass the lobby, then send a start command now.
  845. if ( m_pSettings->GetBool( "options/bypasslobby", false ) )
  846. {
  847. OnRunCommand_Start();
  848. }
  849. break;
  850. }
  851. if ( m_pSysSession )
  852. {
  853. m_pSysSession->Update();
  854. }
  855. }
  856. void CMatchSessionOnlineHost::Destroy()
  857. {
  858. g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( 0ull, 0ull );
  859. if ( m_eState > STATE_LOBBY )
  860. {
  861. char const *szServerType = m_pSettings->GetString( "server/server", "listen" );
  862. if ( !Q_stricmp( szServerType, "listen" ) )
  863. {
  864. if ( IGameEvent *pEvent = g_pMatchExtensions->GetIGameEventManager2()->CreateEvent( "server_pre_shutdown" ) )
  865. {
  866. pEvent->SetString( "reason", "quit" );
  867. g_pMatchExtensions->GetIGameEventManager2()->FireEvent( pEvent );
  868. }
  869. }
  870. g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
  871. }
  872. if ( m_pMatchSearcher )
  873. {
  874. m_pMatchSearcher->Destroy();
  875. m_pMatchSearcher = NULL;
  876. }
  877. if ( m_pTeamSearcher )
  878. {
  879. m_pTeamSearcher->Destroy();
  880. m_pTeamSearcher = NULL;
  881. }
  882. if ( m_pDsSearcher )
  883. {
  884. m_pDsSearcher->Destroy();
  885. m_pDsSearcher = NULL;
  886. }
  887. if ( m_pSysSession )
  888. {
  889. m_pSysSession->Destroy();
  890. m_pSysSession = NULL;
  891. }
  892. delete this;
  893. g_pMatchExtensions->GetINetSupport()->UpdateServerReservation( 0ull );
  894. }
  895. void CMatchSessionOnlineHost::DebugPrint()
  896. {
  897. DevMsg( "CMatchSessionOnlineHost [ state=%d ]\n", m_eState );
  898. DevMsg( "System data:\n" );
  899. KeyValuesDumpAsDevMsg( GetSessionSystemData(), 1 );
  900. DevMsg( "Settings data:\n" );
  901. KeyValuesDumpAsDevMsg( GetSessionSettings(), 1 );
  902. if ( m_pDsSearcher )
  903. DevMsg( "Dedicated search in progress\n" );
  904. else
  905. DevMsg( "Dedicated search not active\n" );
  906. if ( m_pSysSession )
  907. m_pSysSession->DebugPrint();
  908. else
  909. DevMsg( "SysSession is NULL\n" );
  910. if ( m_pTeamSearcher )
  911. m_pTeamSearcher->DebugPrint();
  912. else
  913. DevMsg( "TeamSearch is NULL\n" );
  914. }
  915. bool CMatchSessionOnlineHost::IsAnotherSessionJoinable( const char *pszAnotherSessionInfo )
  916. {
  917. #ifdef _X360
  918. if ( m_pSysSession )
  919. {
  920. char chHostInfo[ XSESSION_INFO_STRING_LENGTH ] = {0};
  921. m_pSysSession->GetHostSessionInfo( chHostInfo );
  922. XSESSION_INFO xsi, xsiAnother;
  923. MMX360_SessionInfoFromString( xsi, chHostInfo );
  924. MMX360_SessionInfoFromString( xsiAnother, pszAnotherSessionInfo );
  925. if ( !memcmp( &xsiAnother.sessionID, &xsi.sessionID, sizeof( xsi.sessionID ) ) )
  926. return false;
  927. }
  928. #endif
  929. return true;
  930. }
  931. void CMatchSessionOnlineHost::OnEvent( KeyValues *pEvent )
  932. {
  933. char const *szEvent = pEvent->GetName();
  934. if ( m_pDsSearcher )
  935. m_pDsSearcher->OnEvent( pEvent );
  936. if ( m_pTeamSearcher )
  937. m_pTeamSearcher->OnEvent( pEvent );
  938. if ( m_pMatchSearcher )
  939. m_pMatchSearcher->OnEvent( pEvent );
  940. if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
  941. {
  942. int iOldState = pEvent->GetInt( "old", 0 );
  943. int iNewState = pEvent->GetInt( "new", 0 );
  944. if ( iOldState >= SIGNONSTATE_CONNECTED &&
  945. iNewState < SIGNONSTATE_CONNECTED )
  946. {
  947. if ( m_eState == STATE_LOADING || m_eState == STATE_GAME )
  948. {
  949. // Lost connection from server or explicit disconnect
  950. DevMsg( "OnEngineClientSignonStateChange\n" );
  951. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
  952. }
  953. else if ( m_eState == STATE_ENDING )
  954. {
  955. // The game has successfully ended and we are back to lobby
  956. m_eState = STATE_LOBBY;
  957. KeyValues *pUpdate = KeyValues::FromString(
  958. "update",
  959. " update { "
  960. " system { "
  961. " lock #empty# "
  962. " } "
  963. " } "
  964. " delete { "
  965. " game { "
  966. " mmqueue #empty# "
  967. " } "
  968. " } "
  969. );
  970. g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsForLobbyTransition(
  971. m_pSettings, pUpdate->FindKey( "update" ), true );
  972. UpdateSessionSettings( KeyValues::AutoDeleteInline( pUpdate ) );
  973. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "hostendgame" ) );
  974. }
  975. }
  976. }
  977. else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) )
  978. {
  979. if ( m_eState == STATE_LOADING || m_eState == STATE_GAME )
  980. {
  981. // Lost connection from server or explicit disconnect
  982. char const *szReason = pEvent->GetString( "reason", "" );
  983. DevMsg( "OnEngineDisconnectReason %s\n", szReason );
  984. bool bLobbySalvagable =
  985. StringHasPrefix( szReason, "Connection to server timed out" ) ||
  986. StringHasPrefix( szReason, "Server shutting down" );
  987. if ( KeyValues *pDisconnectHdlr = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareClientLobbyForGameDisconnect( m_pSettings, pEvent ) )
  988. {
  989. KeyValues::AutoDelete autodelete( pDisconnectHdlr );
  990. char const *szDisconnectHdlr = pDisconnectHdlr->GetString( "disconnecthdlr", "" );
  991. if ( !Q_stricmp( szDisconnectHdlr, "destroy" ) )
  992. bLobbySalvagable = false;
  993. else if ( !Q_stricmp( szDisconnectHdlr, "lobby" ) )
  994. bLobbySalvagable = true;
  995. }
  996. if ( !bLobbySalvagable )
  997. {
  998. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
  999. }
  1000. else
  1001. {
  1002. // Server shutting down, try to retain the lobby
  1003. pEvent->SetString( "disconnecthdlr", "lobby" );
  1004. g_pMatchEventsSubscription->RegisterEventData( pEvent->MakeCopy() );
  1005. OnEndGameToLobby();
  1006. }
  1007. }
  1008. }
  1009. else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
  1010. {
  1011. if ( m_eState == STATE_LOADING || m_eState == STATE_GAME )
  1012. {
  1013. OnEndGameToLobby();
  1014. }
  1015. }
  1016. else if ( !Q_stricmp( "OnEngineListenServerStarted", szEvent ) )
  1017. {
  1018. if ( m_eState == STATE_LOADING )
  1019. {
  1020. uint32 externalIP = pEvent->GetInt( "externalIP", 0 );
  1021. OnRunCommand_StartListenServerStarted( externalIP );
  1022. }
  1023. }
  1024. else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) )
  1025. {
  1026. OnGamePlayerMachinesConnected( pEvent->GetInt( "numMachines" ) );
  1027. }
  1028. else if ( !Q_stricmp( "OnNetLanConnectionlessPacket", szEvent ) )
  1029. {
  1030. char const *szPacketType = pEvent->GetFirstTrueSubKey()->GetName();
  1031. if ( m_pSysSession && m_eState > STATE_CREATING &&
  1032. !Q_stricmp( szPacketType, "LanSearch" ) )
  1033. {
  1034. m_pSysSession->ReplyLanSearch( pEvent );
  1035. }
  1036. }
  1037. else if ( !Q_stricmp( "OnSysMuteListChanged", szEvent ) )
  1038. {
  1039. if ( m_pSysSession )
  1040. m_pSysSession->Voice_UpdateMutelist();
  1041. }
  1042. else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
  1043. {
  1044. const char *state = pEvent->GetString( "state", "" );
  1045. if ( !Q_stricmp( "progress", state) &&
  1046. ( m_pTeamSearcher || m_pMatchSearcher) && ( m_eState == STATE_MATCHING ) )
  1047. {
  1048. char const *szProgress = pEvent->GetString( "progress" );
  1049. int numResults = pEvent->GetInt( "numResults", 0 );
  1050. // Special case when communication between team gets aborted and the process needs
  1051. // to be restarted
  1052. if ( !Q_stricmp( "restart", szProgress ) )
  1053. {
  1054. m_eState = STATE_MATCHINGRESTART;
  1055. return;
  1056. }
  1057. KeyValues *pUpdate = KeyValues::FromString(
  1058. "update",
  1059. " update { "
  1060. " system { "
  1061. " lock = "
  1062. " } "
  1063. " } "
  1064. );
  1065. if ( numResults > 0 )
  1066. {
  1067. pUpdate->SetString( "update/system/lock", CFmtStr( "matching%s%d", szProgress, numResults ) );
  1068. }
  1069. else
  1070. {
  1071. pUpdate->SetString( "update/system/lock", CFmtStr( "matching%s", szProgress ) );
  1072. }
  1073. // Now modify the state of our game
  1074. UpdateSessionSettings( KeyValues::AutoDeleteInline ( pUpdate ) );
  1075. }
  1076. else if ( !Q_stricmp( "joinconteamsession", state) )
  1077. {
  1078. KeyValues *pSettings = KeyValues::FromString(
  1079. "update",
  1080. " update { "
  1081. " system { "
  1082. " network LIVE "
  1083. " } "
  1084. " options { "
  1085. " action joinsession "
  1086. " } "
  1087. " } "
  1088. );
  1089. KeyValues *pUpdate = pSettings->FindKey( "update" );
  1090. uint64 sessionId = pEvent->GetUint64( "sessionid", 0 );
  1091. const char *sessionInfo = pEvent->GetString( "sessioninfo", "" );
  1092. KeyValues *pOptions = pUpdate->FindKey( "options" );
  1093. pOptions->SetUint64( "sessionid", sessionId );
  1094. pOptions->SetString( "sessioninfo", sessionInfo );
  1095. uint64 teamResKey = m_pSettings->GetUint64( "members/machine0/id", 0 );
  1096. pOptions->SetUint64( "teamResKey", teamResKey );
  1097. KeyValues *pSessionHostDataSrc = pEvent->FindKey( "sessionHostDataUnpacked" );
  1098. if ( pSessionHostDataSrc )
  1099. {
  1100. KeyValues *pSessionHostDataDst = pUpdate->CreateNewKey();
  1101. pSessionHostDataDst->SetName( "sessionHostDataUnpacked" );
  1102. pSessionHostDataSrc->CopySubkeys( pSessionHostDataDst );
  1103. }
  1104. KeyValues *pTeamMembersSrc = pEvent->FindKey( "teamMembers" );
  1105. KeyValues *pTeamMembersDst = pUpdate->CreateNewKey();
  1106. pTeamMembersDst->SetName( "teamMembers" );
  1107. pTeamMembersSrc->CopySubkeys( pTeamMembersDst );
  1108. // Now modify the state of our game
  1109. UpdateSessionSettings( KeyValues::AutoDeleteInline ( pSettings ) );
  1110. }
  1111. }
  1112. else if ( !Q_stricmp( "TeamSearchResult::ListenHost", szEvent ) )
  1113. {
  1114. m_eState = STATE_LOADING;
  1115. StartListenServerMap();
  1116. }
  1117. else if ( !Q_stricmp( "TeamSearchResult::Dedicated", szEvent ) ||
  1118. !Q_stricmp( "TeamSearchResult::ListenClient", szEvent ) )
  1119. {
  1120. KeyValues *pServerInfo = pEvent->FindKey( "server", false );
  1121. if ( ( m_eState == STATE_MATCHING ) &&
  1122. pServerInfo && m_pTeamSearcher )
  1123. {
  1124. // We found our dedicated server to play on, let the game commence!
  1125. // Prepare the update - creating the "server" key signals that
  1126. // a game server is available
  1127. KeyValues *kvUpdate = KeyValues::FromString(
  1128. "update",
  1129. " update { "
  1130. " system { "
  1131. " lock #empty# "
  1132. " } "
  1133. " server { "
  1134. " server dedicated "
  1135. " } "
  1136. " } "
  1137. );
  1138. KeyValues::AutoDelete autodelete( kvUpdate );
  1139. kvUpdate->FindKey( "update/server" )->MergeFrom( pServerInfo, KeyValues::MERGE_KV_UPDATE );
  1140. // Remove the lock from the session and allow joins and trigger clients connect
  1141. UpdateSessionSettings( kvUpdate );
  1142. //
  1143. // Actually connect to game server
  1144. //
  1145. if ( void *pDsResult = pEvent->GetPtr( "dsresult" ) )
  1146. {
  1147. // we have a resolved secure server XNADDR
  1148. ConnectGameServer( reinterpret_cast< CDsSearcher::DsResult_t * >( pDsResult ) );
  1149. }
  1150. else
  1151. {
  1152. // we need to resolve secure server XNADDR just like clients do
  1153. ConnectGameServer( NULL );
  1154. }
  1155. }
  1156. }
  1157. else if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
  1158. {
  1159. if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession )
  1160. {
  1161. // We had a session error
  1162. if ( char const *szError = pEvent->GetString( "error", NULL ) )
  1163. {
  1164. State_t eSavedState = m_eState;
  1165. // Destroy the session
  1166. m_pSysSession->Destroy();
  1167. m_pSysSession = NULL;
  1168. // Handle error
  1169. m_eState = STATE_CREATING;
  1170. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", szError ) );
  1171. // If we were in active gameplay state during migration then also disconnect
  1172. if ( !Q_stricmp( szError, "migrate" ) &&
  1173. ( eSavedState == STATE_GAME || eSavedState == STATE_ENDING ) )
  1174. {
  1175. g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
  1176. }
  1177. return;
  1178. }
  1179. // This is our session
  1180. switch ( m_eState )
  1181. {
  1182. case STATE_CREATING:
  1183. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "created" ) );
  1184. // Session created successfully and we were straight on the way to a server
  1185. if ( char const *szServer = m_pSettings->GetString( "server/server", NULL ) )
  1186. {
  1187. OnRunCommand_Start();
  1188. }
  1189. // Session created successfully and we are in the lobby
  1190. else
  1191. {
  1192. m_eState = STATE_LOBBY;
  1193. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "hostinit" ) );
  1194. }
  1195. break;
  1196. default:
  1197. if ( char const *szAction = pEvent->GetString( "action", NULL ) )
  1198. {
  1199. if ( !Q_stricmp( "client", szAction ) )
  1200. {
  1201. KeyValues *pExtendedSettings = new KeyValues( "ExtendedSettings" );
  1202. char const *szMigrateState = "lobby";
  1203. switch ( m_eState )
  1204. {
  1205. case STATE_GAME:
  1206. szMigrateState = "game";
  1207. break;
  1208. case STATE_ENDING:
  1209. szMigrateState = "ending";
  1210. break;
  1211. }
  1212. pExtendedSettings->SetString( "state", szMigrateState );
  1213. pExtendedSettings->AddSubKey( m_pSettings );
  1214. // Release ownership of the resources since new match session now owns them
  1215. m_pSettings = NULL;
  1216. m_autodelete_pSettings.Assign( NULL );
  1217. CSysSessionHost *pSysSession = m_pSysSession;
  1218. m_pSysSession = NULL;
  1219. // Destroy our instance and create the new match interface
  1220. m_eState = STATE_MIGRATE;
  1221. g_pMMF->SetCurrentMatchSession( NULL );
  1222. this->Destroy();
  1223. // Now we need to create the new client session that will install itself
  1224. IMatchSession *pNewClient = new CMatchSessionOnlineClient( pSysSession, pExtendedSettings );
  1225. Assert( g_pMMF->GetMatchSession() == pNewClient );
  1226. pNewClient;
  1227. return;
  1228. }
  1229. }
  1230. break;
  1231. }
  1232. }
  1233. }
  1234. else if ( !Q_stricmp( "mmF->SysSessionCommand", szEvent ) )
  1235. {
  1236. if ( m_pSysSession && pEvent->GetPtr( "syssession", NULL ) == m_pSysSession )
  1237. {
  1238. KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
  1239. if ( pCommand )
  1240. {
  1241. OnRunCommand( pCommand );
  1242. }
  1243. }
  1244. }
  1245. }
  1246. void CMatchSessionOnlineHost::MigrateGameSettings()
  1247. {
  1248. // Check if we have not been destroyed due to a failed migration
  1249. if ( g_pMatchFramework->GetMatchSession() != this )
  1250. {
  1251. DevWarning( "CMatchSessionOnlineHost::MigrateGameSettings cannot run because our session is not the active session!\n" );
  1252. return;
  1253. }
  1254. switch ( m_eState )
  1255. {
  1256. case STATE_LOBBY:
  1257. {
  1258. // Previous host was in LOBBY - then nothing to do
  1259. // Previous host was in STARTING / LOADING - remove the "lock" field
  1260. // on the session
  1261. // Previous host was in ENDING, but this client reached lobby after
  1262. // reacting to game event from game server - host had the "server"
  1263. // key and could have set "lock = endgame" - remove the "lock" field
  1264. // and "server" key on the session.
  1265. // Check if the server or endgame information was set
  1266. char const *szServer = m_pSettings->GetString( "server/server", NULL );
  1267. char const *szLockType = m_pSettings->GetString( "system/lock", "" );
  1268. // Remove server information
  1269. KeyValues *kvFix = KeyValues::FromString(
  1270. "updatedelete",
  1271. " update { "
  1272. " system { "
  1273. " lock #empty# "
  1274. " } "
  1275. " } "
  1276. " delete { "
  1277. " game { mmqueue #empty# } "
  1278. " server delete "
  1279. " } "
  1280. );
  1281. // Let the title apply title-specific adjustments
  1282. bool bEndGameTransition = ( szServer || !Q_stricmp( szLockType, "endgame" ) );
  1283. g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsForLobbyTransition(
  1284. m_pSettings, kvFix->FindKey( "update" ), bEndGameTransition );
  1285. UpdateSessionSettings( KeyValues::AutoDeleteInline( kvFix ) );
  1286. }
  1287. break;
  1288. case STATE_GAME:
  1289. {
  1290. // Game state really just transfers straightly
  1291. }
  1292. break;
  1293. case STATE_ENDING:
  1294. {
  1295. // Fix up the system since this machine is the new host and haven't yet
  1296. // reached the lobby
  1297. // Remove server information
  1298. UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  1299. "updatedelete",
  1300. " update { "
  1301. " system { "
  1302. " lock endgame "
  1303. " } "
  1304. " } "
  1305. " delete { "
  1306. " server delete "
  1307. " } "
  1308. ) ) );
  1309. }
  1310. break;
  1311. default:
  1312. Assert( !"CMatchSessionOnlineHost::MigrateGameSettings - unreachable!" );
  1313. break;
  1314. }
  1315. }
  1316. void CMatchSessionOnlineHost::InitializeGameSettings()
  1317. {
  1318. //
  1319. // We need to establish correct keys
  1320. // because data coming from callers can contain all sorts
  1321. // of auxiliary settings
  1322. //
  1323. m_pSettings->SetName( "settings" );
  1324. int numSlotsCreated = m_pSettings->GetInt( "members/numSlots", 0 );
  1325. char const *arrKeys[] = { "system", "game", "options", "server", "conteamlobby" };
  1326. for ( KeyValues *valRoot = m_pSettings->GetFirstSubKey(); valRoot; )
  1327. {
  1328. KeyValues *pRootSubkey = valRoot;
  1329. valRoot = pRootSubkey->GetNextKey();
  1330. bool bValidKey = false;
  1331. char const *szRootSubkeyName = pRootSubkey->GetName();
  1332. for ( int k = 0; k < ARRAYSIZE( arrKeys ); ++ k )
  1333. {
  1334. if ( !Q_stricmp( arrKeys[k], szRootSubkeyName ) )
  1335. {
  1336. bValidKey = true;
  1337. break;
  1338. }
  1339. }
  1340. if ( !bValidKey )
  1341. {
  1342. m_pSettings->RemoveSubKey( pRootSubkey );
  1343. pRootSubkey->deleteThis();
  1344. }
  1345. }
  1346. // Since the session can be created with a minimal amount of data available
  1347. // the session object is responsible for initializing the missing data to defaults
  1348. // or saved values or values from gamer progress/profile or etc...
  1349. if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) )
  1350. {
  1351. KeyValuesAddDefaultString( kv, "network", "LIVE" );
  1352. KeyValuesAddDefaultString( kv, "access", "public" );
  1353. }
  1354. bool bEncryptedServerAddressRequired = false;
  1355. if ( KeyValues *kv = m_pSettings->FindKey( "options", true ) )
  1356. {
  1357. char const *szServerType = kv->GetString( "server", NULL );
  1358. if ( !szServerType || Q_stricmp( szServerType, "official") )
  1359. szServerType = "official";
  1360. if ( Q_stricmp( "LIVE", m_pSettings->GetString( "system/network" ) ) )
  1361. szServerType = "listen";
  1362. KeyValuesAddDefaultString( kv, "server", szServerType );
  1363. // Remove "action" key if it doesn't hold useful data
  1364. if ( KeyValues *kvAction = kv->FindKey( "action" ) )
  1365. {
  1366. char const *szAction = kvAction->GetString();
  1367. if ( szAction && !Q_stricmp( szAction, "crypt" ) )
  1368. bEncryptedServerAddressRequired = true;
  1369. if ( !szAction || !*szAction ||
  1370. !Q_stricmp( "create", szAction ) ||
  1371. !Q_stricmp( "crypt", szAction ) )
  1372. {
  1373. kv->RemoveSubKey( kvAction );
  1374. kvAction->deleteThis();
  1375. }
  1376. }
  1377. }
  1378. // Reset the members key
  1379. if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) )
  1380. {
  1381. pMembers->SetInt( "numMachines", 1 );
  1382. int numPlayers = 1;
  1383. #ifdef _GAMECONSOLE
  1384. numPlayers = XBX_GetNumGameUsers();
  1385. #endif
  1386. pMembers->SetInt( "numPlayers", numPlayers );
  1387. pMembers->SetInt( "numSlots", MAX( numSlotsCreated, numPlayers ) );
  1388. if ( KeyValues *pMachine = pMembers->FindKey( "machine0", true ) )
  1389. {
  1390. XUID machineid = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
  1391. pMachine->SetUint64( "id", machineid );
  1392. #if defined( _PS3 ) && !defined( NO_STEAM )
  1393. pMachine->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() );
  1394. #endif
  1395. pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() );
  1396. pMachine->SetInt( "numPlayers", numPlayers );
  1397. pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
  1398. pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() );
  1399. pMachine->SetInt( "ping", 0 );
  1400. for ( int k = 0; k < numPlayers; ++ k )
  1401. {
  1402. if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) )
  1403. {
  1404. int iController = 0;
  1405. #ifdef _GAMECONSOLE
  1406. iController = XBX_GetUserId( k );
  1407. #endif
  1408. IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
  1409. pPlayer->SetUint64( "xuid", player->GetXUID() );
  1410. pPlayer->SetString( "name", player->GetName() );
  1411. }
  1412. }
  1413. }
  1414. }
  1415. if ( bEncryptedServerAddressRequired )
  1416. {
  1417. // We need to encrypt the address if there's one
  1418. if ( char const *szAdrOnline = MatchSession_EncryptAddressString( m_pSettings->GetString( "server/adronline" ), m_pSysData->GetUint64( "crypt" ) ) )
  1419. m_pSettings->SetString( "server/adronline", szAdrOnline );
  1420. if ( char const *szAdrOnline = MatchSession_EncryptAddressString( m_pSettings->GetString( "server/adrlocal" ), m_pSysData->GetUint64( "crypt" ) ) )
  1421. m_pSettings->SetString( "server/adrlocal", szAdrOnline );
  1422. }
  1423. // Let the title extend the game settings
  1424. g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "host" );
  1425. DevMsg( "CMatchSessionOnlineHost::InitializeGameSettings adjusted settings:\n" );
  1426. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  1427. }