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.

912 lines
30 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_title.h"
  7. #include "mm_title_richpresence.h"
  8. #include "portal2.spa.h"
  9. #ifdef _PS3
  10. #include <netex/net.h>
  11. #include <netex/libnetctl.h>
  12. #endif
  13. #include "fmtstr.h"
  14. #include "matchmaking/portal2/imatchext_portal2.h"
  15. #include "matchmaking/mm_helpers.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. CMatchTitle::CMatchTitle()
  19. {
  20. ;
  21. }
  22. CMatchTitle::~CMatchTitle()
  23. {
  24. ;
  25. }
  26. //
  27. // Init / shutdown
  28. //
  29. InitReturnVal_t CMatchTitle::Init()
  30. {
  31. if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
  32. {
  33. mgr->AddListener( this, "server_pre_shutdown", false );
  34. mgr->AddListener( this, "game_newmap", false );
  35. mgr->AddListener( this, "finale_start", false );
  36. mgr->AddListener( this, "round_start", false );
  37. mgr->AddListener( this, "round_end", false );
  38. mgr->AddListener( this, "difficulty_changed", false );
  39. }
  40. #ifndef SWDS
  41. // Initialize Title Update version
  42. extern ConVar mm_tu_string;
  43. mm_tu_string.SetValue( "20110805" );
  44. #endif
  45. return INIT_OK;
  46. }
  47. void CMatchTitle::Shutdown()
  48. {
  49. if ( IGameEventManager2 *mgr = g_pMatchExtensions->GetIGameEventManager2() )
  50. {
  51. mgr->RemoveListener( this );
  52. }
  53. }
  54. //
  55. // Implementation
  56. //
  57. uint64 CMatchTitle::GetTitleID()
  58. {
  59. #ifdef _X360
  60. #ifndef _DEMO
  61. return TITLEID_PORTAL_2_DISC_XBOX_360;
  62. #else
  63. return TITLEID_PORTAL_2_DEMO;
  64. #endif
  65. #elif !defined( SWDS ) && !defined( NO_STEAM )
  66. static uint64 uiAppID = 0ull;
  67. if ( !uiAppID && steamapicontext && steamapicontext->SteamUtils() )
  68. {
  69. uiAppID = steamapicontext->SteamUtils()->GetAppID();
  70. }
  71. return uiAppID;
  72. #else
  73. return 0ull;
  74. #endif
  75. }
  76. uint64 CMatchTitle::GetTitleServiceID()
  77. {
  78. #ifdef _X360
  79. return 0x45410880ull; // Left 4 Dead 1 Service ID
  80. #else
  81. return 0ull;
  82. #endif
  83. }
  84. uint64 CMatchTitle::GetTitleSettingsFlags()
  85. {
  86. return MATCHTITLE_SETTING_MULTIPLAYER
  87. | MATCHTITLE_SETTING_NODEDICATED
  88. | MATCHTITLE_PLAYERMGR_DISABLED
  89. | MATCHTITLE_SERVERMGR_DISABLED
  90. | MATCHTITLE_INVITE_ONLY_SINGLE_USER
  91. ;
  92. }
  93. #ifdef _PS3
  94. void *g_pMatchTitle_NetMemory;
  95. #endif
  96. void CMatchTitle::PrepareNetStartupParams( void *pNetStartupParams )
  97. {
  98. #ifdef _X360
  99. XNetStartupParams &xnsp = *( XNetStartupParams * ) pNetStartupParams;
  100. xnsp.cfgQosDataLimitDiv4 = 128; // 512 bytes
  101. xnsp.cfgSockDefaultRecvBufsizeInK = 64; // Increase receive size for UDP to 64k
  102. xnsp.cfgSockDefaultSendBufsizeInK = 64; // Keep send size at 64k too
  103. int numGamePlayersMax = GetTotalNumPlayersSupported();
  104. int numConnections = 4 * ( numGamePlayersMax - 1 );
  105. // - the max number of connections to members of your game party
  106. // - the max number of connections to members of your social party
  107. // - the max number of connections to a pending game party (if you are joining a new one ).
  108. // - matchmakings client info structure also creates a connection per client for the lobby.
  109. // 1 - the main game session
  110. int numTotalConnections = 1 + numConnections;
  111. // 29 - total Connections (XNADDR/XNKID pairs) ,using 5 sessions (XNKID/XNKEY pairs).
  112. xnsp.cfgKeyRegMax = 16; //adding some extra room because of lazy dealocation of these pairs.
  113. xnsp.cfgSecRegMax = MAX( 64, numTotalConnections ); //adding some extra room because of lazy dealocation of these pairs.
  114. xnsp.cfgSockMaxDgramSockets = xnsp.cfgSecRegMax;
  115. xnsp.cfgSockMaxStreamSockets = xnsp.cfgSecRegMax;
  116. #endif
  117. #if defined( _PS3 ) && defined( NO_STEAM )
  118. MEM_ALLOC_CREDIT_( "NO_STEAM: CMatchTitle::PrepareNetStartupParams" );
  119. sys_net_initialize_parameter_t &snip = *( sys_net_initialize_parameter_t * ) pNetStartupParams;
  120. snip.memory_size = 512 * 1024;
  121. snip.memory = malloc( snip.memory_size ); // alternatively this can be a global array
  122. g_pMatchTitle_NetMemory = snip.memory; // bookmark the memory address for later inspection if necessary
  123. #endif
  124. }
  125. int CMatchTitle::GetTotalNumPlayersSupported()
  126. {
  127. // Portal 2 is a 2-player game
  128. return 2;
  129. }
  130. // Get a guest player name
  131. char const * CMatchTitle::GetGuestPlayerName( int iUserIndex )
  132. {
  133. if ( vgui::IVGUILocalize *pLocalize = g_pMatchExtensions->GetILocalize() )
  134. {
  135. if ( wchar_t* wStringTableEntry = pLocalize->Find( "#L4D360UI_Character_Guest" ) )
  136. {
  137. static char szName[ MAX_PLAYER_NAME_LENGTH ] = {0};
  138. pLocalize->ConvertUnicodeToANSI( wStringTableEntry, szName, ARRAYSIZE( szName ) );
  139. return szName;
  140. }
  141. }
  142. return "";
  143. }
  144. // Sets up all necessary client-side convars and user info before
  145. // connecting to server
  146. void CMatchTitle::PrepareClientForConnect( KeyValues *pSettings )
  147. {
  148. #ifndef SWDS
  149. int numPlayers = 1;
  150. #ifdef _GAMECONSOLE
  151. numPlayers = XBX_GetNumGameUsers();
  152. #endif
  153. //
  154. // Now we set the convars
  155. //
  156. for ( int k = 0; k < numPlayers; ++ k )
  157. {
  158. int iController = k;
  159. #ifdef _GAMECONSOLE
  160. iController = XBX_GetUserId( k );
  161. #endif
  162. IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController );
  163. if ( !pPlayerLocal )
  164. continue;
  165. // Set "name"
  166. static SplitScreenConVarRef s_cl_name( "name" );
  167. char const *szName = pPlayerLocal->GetName();
  168. s_cl_name.SetValue( k, szName );
  169. // Set "networkid_force"
  170. if ( IsX360() )
  171. {
  172. static SplitScreenConVarRef s_networkid_force( "networkid_force" );
  173. uint64 xid = pPlayerLocal->GetXUID();
  174. s_networkid_force.SetValue( k, CFmtStr( "%08X:%08X:", uint32( xid >> 32 ), uint32( xid ) ) );
  175. }
  176. }
  177. #endif
  178. }
  179. bool CMatchTitle::StartServerMap( KeyValues *pSettings )
  180. {
  181. int numPlayers = 1;
  182. #ifdef _GAMECONSOLE
  183. numPlayers = XBX_GetNumGameUsers();
  184. #endif
  185. char const *szMap = pSettings->GetString( "game/map", NULL );
  186. if ( !szMap )
  187. return false;
  188. // Check that we have the server interface and that the map is valid
  189. if ( !g_pMatchExtensions->GetIVEngineServer() )
  190. return false;
  191. if ( !g_pMatchExtensions->GetIVEngineServer()->IsMapValid( szMap ) )
  192. return false;
  193. //
  194. // Prepare game dll reservation package
  195. //
  196. KeyValues *pGameDllReserve = g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( pSettings );
  197. KeyValues::AutoDelete autodelete( pGameDllReserve );
  198. pGameDllReserve->SetString( "map/mapcommand", ( numPlayers <= 1 ) ? "map" : "ss_map" );
  199. char const *szPlayOptions = pSettings->GetString( "options/play", "" );
  200. if ( !Q_stricmp( "commentary", szPlayOptions ) )
  201. {
  202. pGameDllReserve->SetString( "map/mapcommand", "map_commentary" );
  203. }
  204. else if ( !Q_stricmp( "challenge", szPlayOptions ) )
  205. {
  206. pGameDllReserve->SetString( "options/play", "challenge" );
  207. }
  208. // Run map based off the faked reservation packet
  209. g_pMatchExtensions->GetIServerGameDLL()->ApplyGameSettings( pGameDllReserve );
  210. return true;
  211. }
  212. void CMatchTitle::RunFrame()
  213. {
  214. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  215. if ( !pIMatchSession )
  216. return;
  217. if ( pIMatchSession->GetSessionSettings()->GetBool( "game/sv_cheats" ) )
  218. return; // already flagged as cheats session
  219. // capture either currently set or has been set during this session
  220. static ConVarRef ref_sv_cheats_flagged( "sv_cheats_flagged" );
  221. if ( ( ref_sv_cheats_flagged.IsValid() && !ref_sv_cheats_flagged.GetBool() ) || !g_pMatchExtensions->GetIVEngineClient()->IsConnected() )
  222. return;
  223. // Bypassing session update rules, each client can flag sv_cheats
  224. // separately once they see it before server session sees sv_cheats
  225. pIMatchSession->GetSessionSettings()->SetInt( "game/sv_cheats", 1 );
  226. }
  227. static KeyValues * GetCurrentMatchSessionSettings()
  228. {
  229. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  230. return pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
  231. }
  232. #ifndef SWDS
  233. static void SendPreConnectClientDataToServer( int nSlot )
  234. {
  235. // We have just connected to the server,
  236. // send our avatar information
  237. int iController = nSlot;
  238. #ifdef _GAMECONSOLE
  239. iController = XBX_GetUserId( nSlot );
  240. #endif
  241. // Portal 2 for now has no preconnect data
  242. if ( 1 )
  243. return;
  244. KeyValues *pPreConnectData = new KeyValues( "preconnectdata" );
  245. //
  246. // Now we prep the keyvalues for server
  247. //
  248. if ( IPlayerLocal *pPlayerLocal = g_pPlayerManager->GetLocalPlayer( iController ) )
  249. {
  250. // Set session-specific user info
  251. XUID xuid = pPlayerLocal->GetXUID();
  252. pPreConnectData->SetUint64( "xuid", xuid );
  253. KeyValues *pSettings = GetCurrentMatchSessionSettings();
  254. KeyValues *pMachine = NULL;
  255. if ( KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, xuid, &pMachine ) )
  256. {
  257. }
  258. }
  259. // Deliver the keyvalues to the server
  260. int nRestoreSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  261. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nSlot );
  262. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pPreConnectData );
  263. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nRestoreSlot );
  264. }
  265. static bool MatchSessionIsSinglePlayerOnline( char const *szGameType )
  266. {
  267. if ( XBX_GetNumGameUsers() != 1 )
  268. return false; // not playing with a single committed profile
  269. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  270. if ( !pIMatchSession )
  271. return false; // don't have a valid session
  272. KeyValues *pSettings = pIMatchSession->GetSessionSettings();
  273. if ( Q_stricmp( pSettings->GetString( "system/network" ), "LIVE" ) )
  274. return false; // session is not online
  275. if ( szGameType )
  276. {
  277. if ( Q_stricmp( pSettings->GetString( "game/type" ), szGameType ) )
  278. return false; // session is not correct game type
  279. }
  280. return true;
  281. }
  282. static bool MatchSessionPlayersAreFriends( int iLocalController, XUID xuidPartner )
  283. {
  284. if ( !xuidPartner )
  285. return false;
  286. #ifdef _X360
  287. BOOL bFriend = FALSE;
  288. if ( ERROR_SUCCESS != XUserAreUsersFriends( iLocalController, &xuidPartner, 1, &bFriend, NULL ) )
  289. return false;
  290. if ( !bFriend )
  291. return false;
  292. #else
  293. #ifndef NO_STEAM
  294. if ( !steamapicontext->SteamFriends()->HasFriend( xuidPartner, /*k_EFriendFlagImmediate*/ 0x04 ) )
  295. #endif
  296. return false;
  297. #endif
  298. return true;
  299. }
  300. static void MatchSessionUpdateSinglePlayerProgress( char const *szMap )
  301. {
  302. if ( XBX_GetNumGameUsers() != 1 )
  303. return;
  304. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  305. if ( !pIMatchSession )
  306. return;
  307. KeyValues *pSettings = pIMatchSession->GetSessionSettings();
  308. if ( Q_stricmp( pSettings->GetString( "system/network" ), "offline" ) )
  309. return;
  310. if ( Q_stricmp( pSettings->GetString( "game/mode" ), "sp" ) )
  311. return;
  312. // Ok, we've got a single player offline session with one user
  313. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() );
  314. if ( !pPlayer )
  315. return;
  316. static ContextValue_t s_SP_MAP_2_PROGRESS[] = {
  317. #define CFG( spmapname, chapternum, subchapter ) { #spmapname, chapternum },
  318. #include "inc_sp_maps.inc"
  319. #undef CFG
  320. { NULL, 0 },
  321. };
  322. uint32 uiChapterNum = s_SP_MAP_2_PROGRESS->ScanValues( szMap );
  323. if ( !uiChapterNum )
  324. return;
  325. // Locate the single player progress field
  326. TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
  327. fields = TitleDataFieldsDescriptionFindByString( fields, "SP.progress" );
  328. if ( !fields )
  329. return;
  330. uint32 uiCurrentlyMaxChapter = TitleDataFieldsDescriptionGetValue<uint32>( fields, pPlayer );
  331. if ( uiChapterNum <= uiCurrentlyMaxChapter )
  332. return;
  333. // Update the single player progress
  334. TitleDataFieldsDescriptionSetValue<uint32>( fields, pPlayer, uiChapterNum );
  335. }
  336. template< typename T >
  337. static bool MatchSessionSetAchievementBasedOnComponents( T valComponent, char const *szAchievement, int numComponentFields, IPlayerLocal *pPlayerLocal )
  338. {
  339. TitleDataFieldsDescription_t const *field1 = TitleDataFieldsDescriptionFindByString(
  340. g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage(), CFmtStr( "%s[1]", szAchievement ) );
  341. if ( !field1 )
  342. return false;
  343. int iComponent = 0;
  344. for ( ; iComponent < numComponentFields; ++ iComponent, ++ field1 )
  345. {
  346. T valSlot = TitleDataFieldsDescriptionGetValue<T>( field1, pPlayerLocal );
  347. if ( valSlot == valComponent )
  348. return false; // already have such component
  349. if ( !valSlot )
  350. {
  351. TitleDataFieldsDescriptionSetValue<T>( field1, pPlayerLocal, valComponent );
  352. ++ iComponent;
  353. break;
  354. }
  355. }
  356. if ( iComponent < numComponentFields )
  357. return false; // not enough components met yet
  358. // Awesome, we are eligible for achievement
  359. {
  360. KeyValues *kvAwardAch = new KeyValues( "" );
  361. KeyValues::AutoDelete autodelete_kvAwardAch( kvAwardAch );
  362. kvAwardAch->SetInt( szAchievement, 1 );
  363. pPlayerLocal->UpdateAwardsData( kvAwardAch );
  364. }
  365. return true;
  366. }
  367. #endif
  368. void CMatchTitle::OnEvent( KeyValues *pEvent )
  369. {
  370. char const *szEvent = pEvent->GetName();
  371. if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) ||
  372. !Q_stricmp( "OnPlayerUpdated", szEvent ) )
  373. {
  374. MM_Title_RichPresence_PlayersChanged( GetCurrentMatchSessionSettings() );
  375. }
  376. else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) ||
  377. !Q_stricmp( "OnPlayerMachinesDisconnected", szEvent ) )
  378. {
  379. // Player counts changed on host, update aggregate fields
  380. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  381. if ( !pMatchSession )
  382. return;
  383. KeyValues *kvPackage = new KeyValues( "Update" );
  384. if ( KeyValues *kvUpdate = kvPackage->FindKey( "update", true ) )
  385. {
  386. void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate );
  387. UpdateAggregateMembersSettings( pMatchSession->GetSessionSettings(), kvUpdate );
  388. }
  389. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( kvPackage ) );
  390. }
  391. else if ( !Q_stricmp( "OnProfileDataSaved", szEvent ) )
  392. {
  393. // Player profile data updated, recompute skills
  394. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  395. if ( !pMatchSession )
  396. return;
  397. IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( pEvent->GetInt( "iController" ) );
  398. if ( !player )
  399. return;
  400. // Initialize member settings on a temporary copy of session settings
  401. KeyValues *kvPlayerUpdate = pMatchSession->GetSessionSettings()->MakeCopy();
  402. KeyValues::AutoDelete autodelete_kvPlayerUpdate( kvPlayerUpdate );
  403. void InitializeMemberSettings( KeyValues *pSettings );
  404. InitializeMemberSettings( kvPlayerUpdate );
  405. // Find the updated player info
  406. KeyValues *pPlayerData = SessionMembersFindPlayer( kvPlayerUpdate, player->GetXUID() );
  407. if ( !pPlayerData || !pPlayerData->FindKey( "game" ) )
  408. return;
  409. // Send the request to the host to update our player info
  410. KeyValues *pRequest = new KeyValues( "Game::PlayerInfo" );
  411. KeyValues::AutoDelete autodelete( pRequest );
  412. pRequest->SetString( "run", "host" );
  413. pRequest->SetUint64( "xuid", player->GetXUID() );
  414. pRequest->AddSubKey( pPlayerData->FindKey( "game" )->MakeCopy() );
  415. pMatchSession->Command( pRequest );
  416. }
  417. else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
  418. {
  419. if ( !Q_stricmp( pEvent->GetString( "state" ), "updated" ) )
  420. {
  421. if ( KeyValues *kvUpdate = pEvent->FindKey( "update" ) )
  422. {
  423. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), kvUpdate );
  424. }
  425. }
  426. else if ( !Q_stricmp( pEvent->GetString( "state" ), "created" ) ||
  427. !Q_stricmp( pEvent->GetString( "state" ), "ready" ) )
  428. {
  429. MM_Title_RichPresence_Update( GetCurrentMatchSessionSettings(), NULL );
  430. }
  431. else if ( !Q_stricmp( pEvent->GetString( "state" ), "closed" ) )
  432. {
  433. MM_Title_RichPresence_Update( NULL, NULL );
  434. }
  435. }
  436. #ifndef SWDS
  437. else if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
  438. {
  439. int nSlot = pEvent->GetInt( "slot" );
  440. int iOldState = pEvent->GetInt( "old" );
  441. int iNewState = pEvent->GetInt( "new" );
  442. if ( iOldState < SIGNONSTATE_CONNECTED &&
  443. iNewState >= SIGNONSTATE_CONNECTED )
  444. {
  445. SendPreConnectClientDataToServer( nSlot );
  446. }
  447. }
  448. else if ( !Q_stricmp( "OnEngineSplitscreenClientAdded", szEvent ) )
  449. {
  450. int nSlot = pEvent->GetInt( "slot" );
  451. SendPreConnectClientDataToServer( nSlot );
  452. }
  453. else if ( !Q_stricmp( szEvent, "OnPlayerAward" ) )
  454. {
  455. int iCtrlr = pEvent->GetInt( "iController" );
  456. IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr );
  457. if ( !pPlayerLocal )
  458. return;
  459. char const *szAward = pEvent->GetString( "award" );
  460. // ACH.TEACHER implementation is based off ACH.HI_FIVE_YOUR_PARTNER
  461. if ( !Q_stricmp( szAward, "ACH.HI_FIVE_YOUR_PARTNER" ) )
  462. {
  463. // We are being awarded a calibration success achievement
  464. // check that we are in an online session, that we haven't
  465. // yet completed any maps and haven't gotten any other
  466. // achievements except for ACH.HI_FIVE_YOUR_PARTNER that we
  467. // have just been awarded
  468. if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
  469. return;
  470. TitleData1 const *td1 = ( TitleData1 const * ) pPlayerLocal->GetPlayerTitleData( TitleDataFieldsDescription_t::DB_TD1 );
  471. if ( !td1 )
  472. return; // failed to get TD1
  473. else
  474. {
  475. TitleData1 td1copy = *td1;
  476. // we don't care whether the newbie played mp_coop_lobby_2 map
  477. uint8 *pBits = ( uint8 * ) td1copy.coop.mapbits;
  478. COMPILE_TIME_ASSERT( TitleData1::CoopData_t::mp_coop_lobby_2 < 8*sizeof( uint8 ) );
  479. pBits[0] &=~ ( 1 << TitleData1::CoopData_t::mp_coop_lobby_2 );
  480. // compare our newbie mapbits with all zeroes
  481. uint32 allzeroes[ ARRAYSIZE( td1copy.coop.mapbits ) ];
  482. COMPILE_TIME_ASSERT( sizeof( allzeroes ) == sizeof( td1copy.coop.mapbits ) );
  483. Q_memset( allzeroes, 0, sizeof( allzeroes ) );
  484. if ( Q_memcmp( allzeroes, td1copy.coop.mapbits, sizeof( allzeroes ) ) )
  485. return; // we aren't a newbie, played some other maps already
  486. }
  487. // We are a newbie who just received the achievement, let our potential teacher know
  488. int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  489. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( XBX_GetSlotByUserId( iCtrlr ) );
  490. KeyValues *pServerEvent = new KeyValues( "OnPlayerAward" );
  491. pServerEvent->SetString( "award", "ACH.HI_FIVE_YOUR_PARTNER" );
  492. pServerEvent->SetUint64( "xuid", pPlayerLocal->GetXUID() );
  493. #if defined( _PS3 ) && !defined( NO_STEAM )
  494. pServerEvent->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() );
  495. #endif
  496. pServerEvent->SetInt( "newbie", 1 );
  497. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pServerEvent );
  498. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
  499. }
  500. else if ( !Q_stricmp( szAward, "ACH.SHOOT_THE_MOON" ) )
  501. {
  502. // Unlock player progress towards the credits map
  503. MatchSessionUpdateSinglePlayerProgress( "sp_a5_credits" );
  504. }
  505. }
  506. else if ( !Q_stricmp( szEvent, "Client::CmdKeyValues" ) )
  507. {
  508. KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
  509. if ( !pCmd )
  510. return;
  511. int nSlot = pEvent->GetInt( "slot" );
  512. int iCtrlr = XBX_GetUserId( nSlot );
  513. IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr );
  514. if ( !pPlayerLocal )
  515. return;
  516. char const *szCmd = pCmd->GetName();
  517. if ( !Q_stricmp( "OnPlayerAward", szCmd ) )
  518. {
  519. //
  520. // ACH.TEACHER implementation
  521. //
  522. // our partner received an award
  523. if ( !Q_stricmp( pCmd->GetString( "award" ), "ACH.HI_FIVE_YOUR_PARTNER" ) )
  524. {
  525. if ( pCmd->GetInt( "newbie" ) != 1 )
  526. return;
  527. // Our newbie partner is being awarded a calibration success achievement
  528. // check that we are in an online session and that we have
  529. // already completed all maps
  530. if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
  531. return;
  532. TitleData1 const *td1 = ( TitleData1 const * ) pPlayerLocal->GetPlayerTitleData( TitleDataFieldsDescription_t::DB_TD1 );
  533. if ( !td1 )
  534. return; // failed to get TD1
  535. {
  536. // We must have the HiFive and NewBlood achievements
  537. KeyValues *kvAchievementsEarned = new KeyValues( "" );
  538. KeyValues::AutoDelete autodelete_kvAchievementsEarned( kvAchievementsEarned );
  539. kvAchievementsEarned->SetInt( "ACH.HI_FIVE_YOUR_PARTNER", 0 );
  540. kvAchievementsEarned->SetInt( "ACH.NEW_BLOOD", 0 );
  541. pPlayerLocal->GetAwardsData( kvAchievementsEarned );
  542. if ( !kvAchievementsEarned->GetInt( "ACH.HI_FIVE_YOUR_PARTNER" ) )
  543. return;
  544. if ( !kvAchievementsEarned->GetInt( "ACH.NEW_BLOOD" ) )
  545. return;
  546. }
  547. {
  548. // we must have completed all coop maps
  549. uint8 *pBits = ( uint8 * ) td1->coop.mapbits;
  550. for ( int k = 0; k < TitleData1::CoopData_t::mapbits_total_basegame; ++ k )
  551. {
  552. if ( k != TitleData1::CoopData_t::mp_coop_start && k != TitleData1::CoopData_t::mp_coop_lobby_2 && k != TitleData1::CoopData_t::mp_coop_credits )
  553. {
  554. if ( !( pBits[ k/8 ] & ( 1 << (k%8) ) ) )
  555. return;
  556. }
  557. }
  558. }
  559. // We must be friends with this newbie
  560. #if defined( _PS3 ) && !defined( NO_STEAM )
  561. if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pCmd->GetUint64( "psnid" ) ) )
  562. #endif
  563. if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pCmd->GetUint64( "xuid" ) ) )
  564. return;
  565. // Awesome, we are eligible for ACH.TEACHER
  566. {
  567. KeyValues *kvAwardTeacher = new KeyValues( "" );
  568. KeyValues::AutoDelete autodelete_kvAwardTeacher( kvAwardTeacher );
  569. kvAwardTeacher->SetInt( "ACH.TEACHER", 1 );
  570. pPlayerLocal->UpdateAwardsData( kvAwardTeacher );
  571. }
  572. }
  573. //
  574. // ACH.SPREAD_THE_LOVE implementation
  575. //
  576. else if ( !Q_stricmp( pCmd->GetString( "award" ), "ACH.SPREAD_THE_LOVE" ) )
  577. {
  578. if ( pCmd->GetInt( "hugged" ) != 1 )
  579. return;
  580. if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
  581. return;
  582. // We must be friends with the person we've hugged
  583. XUID xuidHugged = pCmd->GetUint64( "xuid" );
  584. if ( xuidHugged == pPlayerLocal->GetXUID() )
  585. return;
  586. #if defined( _PS3 ) && !defined( NO_STEAM )
  587. if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pCmd->GetUint64( "psnid" ) ) )
  588. #endif
  589. if ( !MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), xuidHugged ) )
  590. return;
  591. // Set achievement component
  592. bool bAchieved = MatchSessionSetAchievementBasedOnComponents( xuidHugged, "ACH.SPREAD_THE_LOVE",
  593. TitleData2::kAchievement_SpreadTheLove_FriendsHuggedCount, pPlayerLocal );
  594. if ( bAchieved )
  595. {
  596. KeyValues *kvAwardShirt2 = new KeyValues( "" );
  597. KeyValues::AutoDelete autodelete_kvAwardShirt2( kvAwardShirt2 );
  598. kvAwardShirt2->SetInt( "AV_SHIRT2", 1 );
  599. pPlayerLocal->UpdateAwardsData( kvAwardShirt2 );
  600. }
  601. }
  602. }
  603. else if ( !Q_stricmp( "OnCoopBotTaunt", szCmd ) )
  604. {
  605. if ( !Q_stricmp( "teamhug", pCmd->GetString( "taunt" ) ) )
  606. {
  607. if ( !MatchSessionIsSinglePlayerOnline( "friends" ) )
  608. return;
  609. // A hug has happened, let's spread the love
  610. int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  611. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( XBX_GetSlotByUserId( iCtrlr ) );
  612. KeyValues *pServerEvent = new KeyValues( "OnPlayerAward" );
  613. pServerEvent->SetString( "award", "ACH.SPREAD_THE_LOVE" );
  614. pServerEvent->SetUint64( "xuid", pPlayerLocal->GetXUID() );
  615. #if defined( _PS3 ) && !defined( NO_STEAM )
  616. pServerEvent->SetUint64( "psnid", steamapicontext->SteamUser()->GetConsoleSteamID().ConvertToUint64() );
  617. #endif
  618. pServerEvent->SetInt( "hugged", 1 );
  619. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( pServerEvent );
  620. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
  621. }
  622. }
  623. else if ( !Q_stricmp( "OnSpeedRunCoopEvent", szCmd ) )
  624. {
  625. //
  626. // A qualifying speedrun has happened
  627. //
  628. // Determine the map context
  629. char const *szMapSpeedRun = pCmd->GetString( "map" );
  630. if ( !szMapSpeedRun || !*szMapSpeedRun )
  631. return;
  632. TitleDataFieldsDescription_t const *fieldMapComplete = TitleDataFieldsDescriptionFindByString( DescribeTitleDataStorage(), CFmtStr( "MP.complete.%s", szMapSpeedRun ) );
  633. if ( !fieldMapComplete )
  634. return;
  635. uint16 uiMapCompleteId = (uint16)(uint32) fieldMapComplete->m_numBytesOffset;
  636. // Set achievement component
  637. MatchSessionSetAchievementBasedOnComponents<uint16>( uiMapCompleteId, "ACH.SPEED_RUN_COOP",
  638. TitleData2::kAchievement_SpeedRunCoop_QualifiedRunsCount, pPlayerLocal );
  639. }
  640. }
  641. else if ( !Q_stricmp( szEvent, "OnDowloadableContentInstalled" ) )
  642. {
  643. if ( !IsX360() )
  644. {
  645. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  646. if ( !pIMatchSession )
  647. return;
  648. IPlayerLocal *playerPrimary = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
  649. if ( !playerPrimary )
  650. return;
  651. //
  652. // Find local machine in session settings
  653. //
  654. KeyValues *pSettings = pIMatchSession->GetSessionSettings();
  655. KeyValues *pMachine = NULL;
  656. KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, playerPrimary->GetXUID(), &pMachine );
  657. pPlayer;
  658. if ( !pMachine )
  659. return;
  660. // Initialize DLC for the machine
  661. extern void InitializeDlcMachineSettings( KeyValues *pSettings, KeyValues *pMachine );
  662. InitializeDlcMachineSettings( pSettings, pMachine );
  663. }
  664. }
  665. #endif
  666. }
  667. //
  668. //
  669. //
  670. int CMatchTitle::GetEventDebugID( void )
  671. {
  672. return EVENT_DEBUG_ID_INIT;
  673. }
  674. void CMatchTitle::FireGameEvent( IGameEvent *pIGameEvent )
  675. {
  676. #ifndef SWDS
  677. // Check if the current match session is on an active game server
  678. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  679. if ( !pMatchSession )
  680. return;
  681. KeyValues *pSessionSettings = pMatchSession->GetSessionSettings();
  682. char const *szGameServer = pSessionSettings->GetString( "server/server", "" );
  683. char const *szSystemLock = pSessionSettings->GetString( "system/lock", "" );
  684. if ( ( !szGameServer || !*szGameServer ) &&
  685. ( !szSystemLock || !*szSystemLock ) )
  686. return;
  687. // Also don't run on the client when there's a host
  688. char const *szSessionType = pMatchSession->GetSessionSystemData()->GetString( "type", NULL );
  689. if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
  690. return;
  691. // Parse the game event
  692. char const *szGameEvent = pIGameEvent->GetName();
  693. if ( !szGameEvent || !*szGameEvent )
  694. return;
  695. if ( !Q_stricmp( "round_start", szGameEvent ) )
  696. {
  697. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  698. "update",
  699. " update { "
  700. " game { "
  701. " state game "
  702. " } "
  703. " } "
  704. ) ) );
  705. }
  706. else if ( !Q_stricmp( "round_end", szGameEvent ) )
  707. {
  708. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  709. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  710. ) );
  711. }
  712. else if ( !Q_stricmp( "finale_start", szGameEvent ) )
  713. {
  714. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  715. "update",
  716. " update { "
  717. " game { "
  718. " state finale "
  719. " } "
  720. " } "
  721. ) ) );
  722. }
  723. else if ( !Q_stricmp( "game_newmap", szGameEvent ) )
  724. {
  725. const char *szMapName = pIGameEvent->GetString( "mapname", "" );
  726. KeyValues *kvUpdate = KeyValues::FromString(
  727. "update",
  728. " update { "
  729. " game { "
  730. " state game "
  731. " } "
  732. " } "
  733. );
  734. KeyValues::AutoDelete autodelete( kvUpdate );
  735. Assert( szMapName && *szMapName );
  736. if ( szMapName && *szMapName )
  737. {
  738. kvUpdate->SetString( "update/game/map", szMapName );
  739. MatchSessionUpdateSinglePlayerProgress( szMapName );
  740. if ( !pSessionSettings->GetString( "game/type", NULL ) )
  741. {
  742. bool bFriends = false;
  743. if ( MatchSessionIsSinglePlayerOnline( NULL ) )
  744. {
  745. IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() );
  746. if ( pPlayerLocal )
  747. {
  748. XUID xuidLocal = pPlayerLocal->GetXUID();
  749. int numSessionFriends = 0, numSessionPlayers = 0;
  750. for ( int iMachine = 0, numMachines = pSessionSettings->GetInt( "members/numMachines" ); iMachine < numMachines; ++ iMachine )
  751. {
  752. KeyValues *pMachine = pSessionSettings->FindKey( CFmtStr( "members/machine%d", iMachine ) );
  753. for ( int iPlayer = 0, numPlayers = pMachine->GetInt( "numPlayers" ); iPlayer < numPlayers; ++ iPlayer )
  754. {
  755. KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", iPlayer ) );
  756. if ( !pPlayer )
  757. continue;
  758. ++ numSessionPlayers;
  759. XUID xuidPlayer = pPlayer->GetUint64( "xuid" );
  760. if ( xuidPlayer == xuidLocal )
  761. continue;
  762. if ( MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), xuidPlayer ) )
  763. {
  764. ++ numSessionFriends;
  765. }
  766. #if defined( _PS3 ) && !defined( NO_STEAM )
  767. else if ( MatchSessionPlayersAreFriends( pPlayerLocal->GetPlayerIndex(), pMachine->GetUint64( "psnid" ) ) )
  768. {
  769. ++ numSessionFriends;
  770. }
  771. #endif
  772. }
  773. }
  774. if ( numSessionFriends && numSessionPlayers && ( numSessionFriends + 1 == numSessionPlayers ) )
  775. bFriends = true;
  776. }
  777. }
  778. kvUpdate->SetString( "update/game/type", bFriends ? "friends" : "default" );
  779. }
  780. }
  781. pMatchSession->UpdateSessionSettings( kvUpdate );
  782. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  783. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  784. ) );
  785. }
  786. else if ( !Q_stricmp( "server_pre_shutdown", szGameEvent ) )
  787. {
  788. char const *szReason = pIGameEvent->GetString( "reason", "quit" );
  789. if ( !Q_stricmp( szReason, "quit" ) )
  790. {
  791. DevMsg( "Received server_pre_shutdown notification - server is shutting down...\n" );
  792. // Transform the server shutdown event into game end event
  793. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  794. "OnEngineDisconnectReason", "reason", "Server shutting down"
  795. ) );
  796. }
  797. }
  798. #endif
  799. }