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.

1645 lines
59 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 "matchmaking/cstrike15/imatchext_cstrike15.h"
  9. #include "vstdlib/random.h"
  10. #include "fmtstr.h"
  11. #include "../engine/filesystem_engine.h"
  12. #include "filesystem.h"
  13. #include "gametypes/igametypes.h"
  14. #include "mathlib/expressioncalculator.h"
  15. #include "csgo.spa.h"
  16. #include "mm_title_contextvalues.h"
  17. #include "inputsystem/iinputsystem.h"
  18. #if !defined (NO_STEAM)
  19. #include "steam/steam_api.h"
  20. extern CSteamAPIContext *steamapicontext;
  21. #endif
  22. #include "csgo_limits.h"
  23. #include "csgo_limits.inl"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. extern IGameTypes *g_pGameTypes;
  27. struct AggregateSkillProperty
  28. {
  29. char const *szSkill;
  30. unsigned int dwSkillPropertyId;
  31. unsigned int dwSkillMinPropertyId;
  32. unsigned int dwSkillMaxPropertyId;
  33. };
  34. static AggregateSkillProperty g_AggregateSkillProperties[] =
  35. {
  36. { "skill0", PROPERTY_CSS_AGGREGATE_SKILL0, PROPERTY_CSS_SEARCH_SKILL0_MIN, PROPERTY_CSS_SEARCH_SKILL0_MAX },
  37. { "skill1", PROPERTY_CSS_AGGREGATE_SKILL1, PROPERTY_CSS_SEARCH_SKILL1_MIN, PROPERTY_CSS_SEARCH_SKILL1_MAX },
  38. { "skill2", PROPERTY_CSS_AGGREGATE_SKILL2, PROPERTY_CSS_SEARCH_SKILL2_MIN, PROPERTY_CSS_SEARCH_SKILL2_MAX },
  39. { "skill3", PROPERTY_CSS_AGGREGATE_SKILL3, PROPERTY_CSS_SEARCH_SKILL3_MIN, PROPERTY_CSS_SEARCH_SKILL3_MAX },
  40. { "skill4", PROPERTY_CSS_AGGREGATE_SKILL4, PROPERTY_CSS_SEARCH_SKILL4_MIN, PROPERTY_CSS_SEARCH_SKILL4_MAX },
  41. NULL,
  42. };
  43. #define MATCH_MAX_SKILL_FIELDS 5
  44. ConVar mm_sv_load_test( "mm_sv_load_test", "0", FCVAR_DEVELOPMENTONLY );
  45. ConVar mm_title_debug_version( "mm_title_debug_version", "0", FCVAR_DEVELOPMENTONLY, "This matchmaking version will override .res file version for isolating matchmaking" );
  46. ConVar mm_title_debug_dccheck( "mm_title_debug_dccheck", "0", FCVAR_DEVELOPMENTONLY, "This matchmaking query will override datacenter connectivity: -1 for local, 1 for dedicated" );
  47. ConVar mm_title_debug_minquery( "mm_title_debug_minquery", "0", FCVAR_DEVELOPMENTONLY, "This matchmaking query will run with minimal set of parameters" );
  48. ConVar mm_csgo_community_search_players_min( "mm_csgo_community_search_players_min", "3", FCVAR_RELEASE | FCVAR_ARCHIVE, "When performing CSGO community matchmaking look for servers with at least so many human players" );
  49. class CMatchTitleGameSettingsMgr : public IMatchTitleGameSettingsMgr
  50. {
  51. public:
  52. CMatchTitleGameSettingsMgr()
  53. {
  54. m_pMatchSystemData = NULL;
  55. }
  56. ~CMatchTitleGameSettingsMgr()
  57. {
  58. if ( m_pMatchSystemData )
  59. {
  60. m_pMatchSystemData->deleteThis();
  61. }
  62. }
  63. // Extends server game details
  64. virtual void ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest );
  65. // Adds the essential part of game details to be broadcast
  66. virtual void ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings );
  67. // Extends game settings update packet for lobby transition,
  68. // either due to a migration or due to an endgame condition
  69. virtual void ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame );
  70. // Allows title to migrate data cached in client sys session data
  71. // into host sys session data
  72. virtual void MigrateSysSessionData( IMatchSession *pNewMatchSession, KeyValues *pSysSessionData );
  73. // Adds data for datacenter reporting
  74. virtual void ExtendDatacenterReport( KeyValues *pReportMsg, char const *szReason );
  75. // Rolls up game details for matches grouping
  76. virtual KeyValues * RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery );
  77. // Defines session search keys for matchmaking
  78. virtual KeyValues * DefineSessionSearchKeys( KeyValues *pSettings );
  79. // Defines dedicated server search key
  80. virtual KeyValues * DefineDedicatedSearchKeys( KeyValues *pSettings, bool bNeedOfficialServer, int nSearchPass );
  81. // Extends game settings update packet before it gets merged with
  82. // session settings and networked to remote clients
  83. virtual void ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys );
  84. // Update a team session to be a game session by filling in map name, updating number of slots etc
  85. virtual KeyValues * ExtendTeamLobbyToGame( KeyValues *pSettings );
  86. // Prepares system for session creation
  87. virtual KeyValues * PrepareForSessionCreate( KeyValues *pSettings );
  88. // Executes the command on the session settings, this function on host
  89. // is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues
  90. // When running on a remote client "ppPlayersUpdated" is NULL and players cannot
  91. // be modified
  92. virtual void ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated );
  93. // Prepares the lobby for game or adjust settings of new players who
  94. // join a game in progress, this function is allowed to modify
  95. // Members/Game subkeys and has to fill in modified players KeyValues
  96. virtual void PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated );
  97. // Prepares the host team lobby for game adjusting the game settings
  98. // this function is allowed to prepare modification package to update
  99. // Game subkeys.
  100. // Returns the update/delete package to be applied to session settings
  101. // and pushed to dependent two sesssion of the two teams.
  102. virtual KeyValues * PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote );
  103. // Initializes full game settings from potentially abbreviated game settings
  104. virtual void InitializeGameSettings( KeyValues *pSettings, char const *szReason );
  105. // Sets the bspname key given a mapgroup
  106. virtual void SetBspnameFromMapgroup( KeyValues *pSettings );
  107. // Prepares the client lobby for migration
  108. // this function is called when the client session is still in the state
  109. // of "client" while handling the original host disconnection and decision
  110. // has been made that local machine will be elected as new "host"
  111. // Returns NULL if migration should proceed normally
  112. // Returns [ kvroot { "error" "n/a" } ] if migration should be aborted.
  113. virtual KeyValues * PrepareClientLobbyForMigration( KeyValues *pSettingsLocal, KeyValues *pMigrationInfo ) { return NULL; }
  114. // Prepares the session for server disconnect
  115. // this function is called when the session is still in the active gameplay
  116. // state and while localhost is handling the disconnection from game server.
  117. // Returns NULL to allow default flow
  118. // Returns [ kvroot { "disconnecthdlr" "<opt>" } ] where <opt> can be:
  119. // "destroy" : to trigger a disconnection error and destroy the session
  120. // "lobby" : to initiate a "salvaging" lobby transition
  121. virtual KeyValues * PrepareClientLobbyForGameDisconnect( KeyValues *pSettingsLocal, KeyValues *pDisconnectInfo )
  122. {
  123. // Every event that causes a disconnection from game server is unsalvagable in CS:GO
  124. // in other products it should be possible to keep all players that were playing on game server
  125. // in the lobby together and send them to lobby UI
  126. return new KeyValues( "disconnecthdrl", "disconnecthdlr", "destroy" );
  127. }
  128. // Retrieves the indexed formula from the match system settings file. (MatchSystem.360.res)
  129. virtual char const * GetFormulaAverage( int index );
  130. // Called by the client to notify matchmaking that it should update matchmaking properties based
  131. // on player distribution among the teams.
  132. virtual void UpdateTeamProperties( KeyValues *pCurrentSettings, KeyValues *pTeamProperties );
  133. // Validates if client profile can set a stat or get awarded an achievement
  134. virtual bool AllowClientProfileUpdate( KeyValues *kvUpdate )
  135. {
  136. return true; // Always all profile to be updated for CStrike15, all platforms
  137. }
  138. protected:
  139. // Loads the games match settings from the KeyValues object. The match settings contain
  140. // data on how to expand the search passes formulas to use for computing skill.
  141. void LoadMatchSettings( void );
  142. // Add filters necessary to implement matchmaking rule on Steam
  143. void AddSteamMatchmakingRule( KeyValues *pResult, bool bAllSessions, KeyValues *pSettings,
  144. bool bCssMatchVersion, bool bCssLevel, bool bCssGameType, bool bCssGameMode,
  145. bool bTeamMatch );
  146. KeyValues *m_pMatchSystemData;
  147. CUtlVector< CUtlString > m_FormulaAverage;
  148. CUtlVector< CUtlString > m_FormulaExperience;
  149. int m_nFormulaExperienceRangeMin;
  150. int m_nFormulaExperienceRangeMax;
  151. struct SkillFormulas
  152. {
  153. CUtlVector< CUtlString > formulas;
  154. int rangeMin;
  155. int rangeMax;
  156. };
  157. CUtlVector< SkillFormulas* > m_FormulaSkill;
  158. struct SearchPass
  159. {
  160. bool checkExperience;
  161. int experienceRange;
  162. CUtlVector< bool > checkSkill;
  163. CUtlVector< int > skillRange;
  164. };
  165. CUtlVector< SearchPass* > m_SearchPass;
  166. };
  167. CMatchTitleGameSettingsMgr g_MatchTitleGameSettingsMgr;
  168. IMatchTitleGameSettingsMgr *g_pIMatchTitleGameSettingsMgr = &g_MatchTitleGameSettingsMgr;
  169. //
  170. // Implementation of CMatchTitleGameSettingsMgr
  171. //
  172. // Extends server game details
  173. void CMatchTitleGameSettingsMgr::ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest )
  174. {
  175. // Query server info
  176. INetSupport::ServerInfo_t si;
  177. g_pMatchExtensions->GetINetSupport()->GetServerInfo( &si );
  178. // Server is always in game
  179. pDetails->SetString( "game/state", "game" );
  180. //
  181. // Determine map info
  182. //
  183. {
  184. int mode = g_pGameTypes->GetCurrentGameMode();
  185. int type = g_pGameTypes->GetCurrentGameType();
  186. const char *modeName = g_pGameTypes->GetGameModeFromInt( type, mode );
  187. const char *typeName = g_pGameTypes->GetGameTypeFromInt( type );
  188. int numHumanSlots = g_pGameTypes->GetMaxPlayersForTypeAndMode( type, mode );
  189. pDetails->SetInt( "members/numSlots", numHumanSlots );
  190. pDetails->SetString( "game/map", si.m_szMapName );
  191. pDetails->SetString( "game/mapgroupname", si.m_szMapGroupName );
  192. pDetails->SetString( "game/mode", modeName );
  193. pDetails->SetString( "game/type", typeName );
  194. if ( !g_pMatchExtensions->GetIServerGameDLL()->IsValveDS() )
  195. {
  196. pDetails->SetInt( "game/hosted", 1 );
  197. pDetails->SetString( "options/server", "dedicated" );
  198. }
  199. }
  200. }
  201. // Adds the essential part of game details to be broadcast
  202. void CMatchTitleGameSettingsMgr::ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings )
  203. {
  204. static KeyValues *pkvExt = KeyValues::FromString(
  205. "settings",
  206. " game { "
  207. " mapgroupname #empty# "
  208. " map #empty# "
  209. " mode #empty# "
  210. " type #empty# "
  211. " state #empty# "
  212. " hosted 0 "
  213. " spectate 0 "
  214. " apr 0 "
  215. " ark 0 "
  216. " loc #empty# "
  217. " clanid #empty# "
  218. " clantag #empty# "
  219. " } "
  220. );
  221. // TODO: Is this the appropriate spot to add in game values to initialize the dedicated server state?
  222. pDetails->MergeFrom( pkvExt, KeyValues::MERGE_KV_UPDATE );
  223. }
  224. // Extends game settings update packet for lobby transition,
  225. // either due to a migration or due to an endgame condition
  226. void CMatchTitleGameSettingsMgr::ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame )
  227. {
  228. pSettingsUpdate->SetString( "game/state", "lobby" );
  229. extern void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate );
  230. UpdateAggregateMembersSettings( pSettings, pSettingsUpdate );
  231. }
  232. // Allows title to migrate data cached in client sys session data
  233. // into host sys session data
  234. void CMatchTitleGameSettingsMgr::MigrateSysSessionData( IMatchSession *pNewMatchSession, KeyValues *pSysSessionData )
  235. {
  236. #ifdef _PS3
  237. KeyValues *kvSystemData = pNewMatchSession->GetSessionSystemData();
  238. if ( !kvSystemData )
  239. return;
  240. if ( KeyValues *kvTimeout = pSysSessionData->FindKey( "timeout", false ) )
  241. {
  242. int avgRank = kvTimeout->GetInt();
  243. kvSystemData->SetInt( "timeout", avgRank );
  244. DevMsg( "Session timeout value=%d (%s)\n", avgRank, "migrated" );
  245. steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:timeout", CFmtStr( "%u", avgRank ) );
  246. }
  247. if ( KeyValues *kvNumOpenSlots = pSysSessionData->FindKey( "numOpenSlots", false ) )
  248. {
  249. int numOpenSlots = kvNumOpenSlots->GetInt();
  250. kvSystemData->SetInt( "numOpenSlots", numOpenSlots );
  251. DevMsg( "Session numOpenSlots=%d (%s)\n", numOpenSlots, "migrated" );
  252. steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:numOpenSlots", CFmtStr( "%u", numOpenSlots ) );
  253. }
  254. #endif
  255. }
  256. // Adds data for datacenter reporting
  257. void CMatchTitleGameSettingsMgr::ExtendDatacenterReport( KeyValues *cmd, char const *szReason )
  258. {
  259. #ifdef _X360
  260. //if ( XBX_GetPrimaryUserId() == XBX_INVALID_USER_ID )
  261. // return;
  262. //if ( !XBX_GetNumGameUsers() || XBX_GetPrimaryUserIsGuest() )
  263. // return;
  264. //IPlayerLocal *pLocalPlayer = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
  265. //if ( !pLocalPlayer )
  266. // return;
  267. //// Achievements info
  268. //uint64 uiAchMask1 = 0; // 100 bits for achievements
  269. //uint64 uiAchMask2 = 0; // 27 bits for asset awards
  270. //{
  271. // KeyValues *kvAchInfo = new KeyValues( "", "@achievements", 0, "@awards", 0 );
  272. // KeyValues::AutoDelete autodelete_kvAchInfo( kvAchInfo );
  273. // pLocalPlayer->GetAwardsData( kvAchInfo );
  274. // for ( KeyValues *val = kvAchInfo->FindKey( "@achievements" )->GetFirstValue(); val; val = val->GetNextValue() )
  275. // {
  276. // int iVal = val->GetInt( "", 0 );
  277. // if ( iVal <= 0 )
  278. // continue;
  279. // else if ( iVal < 64 )
  280. // uiAchMask1 |= ( 1ull << iVal );
  281. // else if ( iVal <= 100 )
  282. // uiAchMask2 |= ( 1ull << ( iVal - 64 ) );
  283. // }
  284. // for ( KeyValues *val = kvAchInfo->FindKey( "@awards" )->GetFirstValue(); val; val = val->GetNextValue() )
  285. // {
  286. // int iVal = val->GetInt( "", 0 );
  287. // if ( iVal <= 0 )
  288. // continue;
  289. // else if ( iVal < ( 128 - 100 ) )
  290. // uiAchMask2 |= ( 1ull << ( iVal + 100 - 64 ) );
  291. // }
  292. //}
  293. //cmd->SetUint64( "ach1", uiAchMask1 );
  294. //cmd->SetUint64( "ach2", uiAchMask2 );
  295. //if ( !V_stricmp( szReason, "datarequest" ) )
  296. //{
  297. // // Add game information
  298. // TitleData1 * td1 = ( TitleData1 * ) pLocalPlayer->GetPlayerTitleData( TitleDataFieldsDescription_t::DB_TD1 );
  299. // TitleData3 * td3 = ( TitleData3 * ) pLocalPlayer->GetPlayerTitleData( TitleDataFieldsDescription_t::DB_TD3 );
  300. // // sp progress
  301. // cmd->SetInt( "map_s", td1->uiSinglePlayerProgressChapter );
  302. // // coop completion
  303. // int numMapBitsFields = sizeof( td1->coop.mapbits ) / sizeof( uint64 );
  304. // for ( int imap = 0; imap < numMapBitsFields; ++ imap )
  305. // {
  306. // cmd->SetUint64( CFmtStr( "map_%d", imap ), ( reinterpret_cast< uint64 * >( td1->coop.mapbits ) )[imap] );
  307. // }
  308. // if ( td3->cvUser.version )
  309. // {
  310. // // profile settings
  311. // cmd->SetFloat( "cfg_pitch", td3->cvUser.joy_pitchsensitivity );
  312. // cmd->SetFloat( "cfg_yaw", td3->cvUser.joy_yawsensitivity );
  313. // cmd->SetInt( "cfg_joy", td3->cvUser.joy_cfg_preset );
  314. // cmd->SetInt( "cfg_bit", td3->cvUser.bitfields[0] );
  315. // }
  316. // if ( td3->cvSystem.version )
  317. // {
  318. // // audio/video
  319. // cmd->SetFloat( "sys_vol", td3->cvSystem.volume );
  320. // cmd->SetFloat( "sys_mus", td3->cvSystem.snd_musicvolume );
  321. // cmd->SetFloat( "sys_gam", td3->cvSystem.mat_monitorgamma );
  322. // cmd->SetInt( "sys_ssm", td3->cvSystem.ss_splitmode );
  323. // cmd->SetInt( "sys_bit", td3->cvSystem.bitfields[0] );
  324. // }
  325. // if ( g_pXboxInstaller )
  326. // {
  327. // cmd->SetInt( "inst",
  328. // ( g_pXboxInstaller->IsFullyInstalled() ? 1 : 0 ) |
  329. // ( g_pXboxInstaller->IsInstallEnabled() ? 2 : 0 )
  330. // );
  331. // }
  332. //}
  333. #endif
  334. }
  335. // Rolls up game details for matches grouping
  336. KeyValues * CMatchTitleGameSettingsMgr::RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery )
  337. {
  338. // TODO: keep each individual results, roll up to the party leader XUID
  339. return NULL;
  340. }
  341. // Defines dedicated server search key
  342. KeyValues * CMatchTitleGameSettingsMgr::DefineDedicatedSearchKeys( KeyValues *pSettings, bool bNeedOfficialServer, int nSearchPass )
  343. {
  344. #if defined ( _X360 )
  345. return NULL;
  346. #else
  347. static ConVarRef sv_search_key( "sv_search_key" );
  348. KeyValues *pKeys = new KeyValues( "SearchKeys" );
  349. CFmtStr fmtExtraGameData;
  350. if ( bNeedOfficialServer )
  351. {
  352. pKeys->SetString( "gametype", "valve_ds,empty" );
  353. }
  354. else
  355. {
  356. pKeys->SetString( "gametype", "empty" );
  357. if ( char const *szMapGroup = pSettings->GetString( "game/mapgroupname", NULL ) )
  358. {
  359. if ( char const *szWorkshop = Q_stristr( szMapGroup, "@workshop" ) )
  360. {
  361. if ( szWorkshop != szMapGroup )
  362. { // When searching for a workshop map only consider servers that are empty and advertise support for it
  363. // and are running the same game type and mode as requested
  364. // First search will be for explicitly supported map in rotation, but second search will be for
  365. // servers that are willing to host public players
  366. if ( 0 == ( nSearchPass % 2 ) )
  367. { // First search pass: request map ID tag
  368. fmtExtraGameData.AppendFormat( "%.*s,", szWorkshop - szMapGroup, szMapGroup );
  369. }
  370. else
  371. { // Second search pass: request any map support
  372. fmtExtraGameData.AppendFormat( "wks:1," );
  373. }
  374. // if ( !pSettings->GetBool( "options/anytypemode" ) ) -- maybe allow reserving community servers regardless of game modes?
  375. {
  376. int nGameType = 0, nGameMode = 0;
  377. g_pGameTypes->GetGameModeAndTypeIntsFromStrings( pSettings->GetString( "game/type" ), pSettings->GetString( "game/mode" ),
  378. nGameType, nGameMode );
  379. fmtExtraGameData.AppendFormat( "gt:%u,gm:%u,", nGameType, nGameMode );
  380. }
  381. }
  382. }
  383. }
  384. }
  385. pKeys->SetString( "gamedata", CFmtStr( "%s%skey:%s%d",
  386. fmtExtraGameData.Access(),
  387. bNeedOfficialServer ? "v" : "c",
  388. sv_search_key.GetString(),
  389. g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() ) );
  390. return pKeys;
  391. #endif
  392. }
  393. static void DescribeX360QueryDefineSessionSearchKeys( KeyValues *pkv )
  394. {
  395. #ifdef _X360
  396. DevMsg( "======== DescribeX360QueryDefineSessionSearchKeys ==============\n" );
  397. DevMsg( " numPlayers = %d\n", pkv->GetInt( "numPlayers" ) );
  398. DevMsg( " rule = %s\n", ( pkv->GetInt( "rule" ) == SESSION_MATCH_QUERY_PLAYER_MATCH ) ? "playermatch" : "UNKNOWN" );
  399. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Contexts/%d", CONTEXT_CSS_GAME_TYPE ) ) )
  400. {
  401. char const *szVal = "UNKNOWN";
  402. switch ( val->GetInt() )
  403. {
  404. case CONTEXT_CSS_GAME_TYPE_CLASSIC: szVal = "classic"; break;
  405. case CONTEXT_CSS_GAME_TYPE_GUNGAME: szVal = "gungame"; break;
  406. default: Assert( false ); break;
  407. }
  408. DevMsg( " CONTEXT_CSS_GAME_TYPE = %s (%d)\n", szVal, val->GetInt() );
  409. }
  410. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Contexts/%d", CONTEXT_CSS_GAME_MODE ) ) )
  411. {
  412. char const *szVal = "UNKNOWN";
  413. switch ( val->GetInt() )
  414. {
  415. case CONTEXT_CSS_GAME_MODE_CASUAL: szVal = "casual"; break;
  416. case CONTEXT_CSS_GAME_MODE_COMPETITIVE: szVal = "competitive"; break;
  417. case CONTEXT_CSS_GAME_MODE_FREESTYLE: szVal = "freestyle"; break;
  418. case CONTEXT_CSS_GAME_MODE_GUNGAMEPROGRESSIVE: szVal = "gungameprogressive"; break;
  419. case CONTEXT_CSS_GAME_MODE_GUNGAMEBOMB: szVal = "gungamebomb"; break;
  420. default: Assert( false ); break;
  421. }
  422. DevMsg( " CONTEXT_CSS_GAME_MODE = %s (%d)\n", szVal, val->GetInt() );
  423. }
  424. else if ( KeyValues *val = pkv->FindKey( CFmtStr( "Properties/%d", PROPERTY_CSS_GAME_MODE_AS_NUMBER ) ) )
  425. {
  426. DevMsg( " PROPERTY_CSS_GAME_MODE_AS_NUMBER near 0x%X (%d)\n", val->GetInt(), val->GetInt() );
  427. }
  428. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Contexts/%d", CONTEXT_GAME_STATE ) ) )
  429. {
  430. char const *szVal = "UNKNOWN";
  431. switch ( val->GetInt() )
  432. {
  433. case CONTEXT_GAME_STATE_IN_MENUS: szVal = "in_menus"; Assert( false ); break;
  434. case CONTEXT_GAME_STATE_SINGLE_PLAYER: szVal = "singleplayer"; Assert( false ); break;
  435. case CONTEXT_GAME_STATE_MULTIPLAYER: szVal = "multiplayer"; break;
  436. default: Assert( false ); break;
  437. }
  438. DevMsg( " CONTEXT_GAME_STATE = %s (%d)\n", szVal, val->GetInt() );
  439. }
  440. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Contexts/%d", CONTEXT_CSS_MAP_GROUP ) ) )
  441. {
  442. DevMsg( " CONTEXT_CSS_MAP_GROUP = %s (%d)\n", "", val->GetInt() );
  443. }
  444. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Properties/%d", PROPERTY_CSS_MATCH_VERSION ) ) )
  445. {
  446. DevMsg( " PROPERTY_CSS_MATCH_VERSION = 0x%X (%d)\n", val->GetInt(), val->GetInt() );
  447. }
  448. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Properties/%d", PROPERTY_CSS_SEARCH_LISTEN_SERVER ) ) )
  449. {
  450. DevMsg( " PROPERTY_CSS_SEARCH_LISTEN_SERVER = 0x%X (%d)\n", val->GetInt(), val->GetInt() );
  451. }
  452. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Properties/%d", PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS ) ) )
  453. {
  454. DevMsg( " PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS >= 0x%X (%d)\n", val->GetInt(), val->GetInt() );
  455. }
  456. if ( KeyValues *val = pkv->FindKey( CFmtStr( "Properties/%d", PROPERTY_CSS_AGGREGATE_SKILL0 ) ) )
  457. {
  458. DevMsg( " PROPERTY_CSS_AGGREGATE_SKILL0 near 0x%X (%d)\n", val->GetInt(), val->GetInt() );
  459. }
  460. DWORD validsettings[] = { X_CONTEXT_GAME_TYPE, X_CONTEXT_GAME_MODE, CONTEXT_CSS_GAME_TYPE, CONTEXT_CSS_GAME_MODE, PROPERTY_CSS_GAME_MODE_AS_NUMBER,
  461. CONTEXT_GAME_STATE, CONTEXT_CSS_MAP_GROUP, PROPERTY_CSS_MATCH_VERSION, PROPERTY_CSS_SEARCH_LISTEN_SERVER, PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS,
  462. PROPERTY_CSS_AGGREGATE_SKILL0 };
  463. char const *categories[] = { "Contexts", "Properties" };
  464. for ( int iCategory = 0; iCategory < Q_ARRAYSIZE( categories ); ++ iCategory )
  465. {
  466. for ( KeyValues *val = pkv->FindKey( categories[iCategory] )->GetFirstSubKey(); val; val = val->GetNextKey() )
  467. {
  468. DWORD idx = atoi( val->GetName() );
  469. bool bValid = false;
  470. for ( int jj = 0; jj < Q_ARRAYSIZE( validsettings ); ++ jj )
  471. {
  472. if ( validsettings[jj] == idx )
  473. {
  474. bValid = true;
  475. break;
  476. }
  477. }
  478. if ( !bValid )
  479. {
  480. DevMsg( "Unexpected entry in query %s: %s (0x%X)\n", categories[iCategory], val->GetName(), idx );
  481. }
  482. Assert( bValid );
  483. }
  484. }
  485. DevMsg( "======== ********X360Query*********************** ==============\n" );
  486. #endif
  487. }
  488. // Defines session search keys for matchmaking
  489. KeyValues * CMatchTitleGameSettingsMgr::DefineSessionSearchKeys( KeyValues *pSettings )
  490. {
  491. MEM_ALLOC_CREDIT();
  492. DevMsg( "DefineSessionSearchKeys settings:\n" );
  493. KeyValuesDumpAsDevMsg( pSettings, 1 );
  494. // Process the match system data here. This will bail early without loading if the
  495. // input file from above doesn't bump it's version number.
  496. LoadMatchSettings();
  497. KeyValues *pResult = new KeyValues( "SessionSearch" );
  498. int numPlayers = pSettings->GetInt( "members/numPlayers", XBX_GetNumGameUsers() );
  499. pResult->SetInt( "numPlayers", numPlayers);
  500. // Certain contexts and properties can not be part of the search parameters unless they are explictly
  501. // required by the matchmaking query. Setting contexts/parameters that the query doesn't expect will
  502. // cause the query to fail.
  503. bool bCssMatchVersion = true;
  504. bool bCssLevel = true;
  505. bool bCssGameType = true;
  506. bool bCssGameMode = true;
  507. #if defined _X360
  508. bool bCssGameModeAsNumber = true;
  509. #endif
  510. bool bTeamMatch = true;
  511. bool bTeamMatchTypeClan = false;
  512. bool bTeamMatchTypeClanPreferred = false;
  513. // Determine if this is a team matchmaking query:
  514. // On Consoles we define "conteammatch" for team lobbies;
  515. // On PC, we do NOT set bypasslobby for team lobbies
  516. #if defined( _GAMECONSOLE )
  517. bTeamMatch = ( pSettings->GetString( "options/conteammatch", NULL ) != NULL );
  518. #else
  519. bTeamMatch = ( pSettings->GetBool( "options/bypasslobby", false ) == false );
  520. #endif
  521. #if defined _X360
  522. pResult->SetInt( "rule", rule );
  523. #endif
  524. // Set the appropriate query based on if we're quickmatching or custommatching.
  525. char const *szAction = pSettings->GetString( "options/action", "" );
  526. bool bMatchmakingQueryForGameInProgress = true;
  527. if ( !Q_stricmp( "quickmatch", szAction ) )
  528. {
  529. bCssLevel = false;
  530. bMatchmakingQueryForGameInProgress = true;
  531. }
  532. else if ( !Q_stricmp( "custommatch", szAction ) )
  533. {
  534. bMatchmakingQueryForGameInProgress = true;
  535. // If a mapgroup is specified use the player match query, but if no mapgroup is specified,
  536. // use the player query that doesn't filter based on mapgroup name.
  537. const char *pszMapGroupName = pSettings->GetString( "game/mapgroupname", NULL );
  538. if ( pszMapGroupName != NULL && Q_strlen( pszMapGroupName ) > 0 )
  539. {
  540. }
  541. else
  542. {
  543. bCssLevel = false;
  544. }
  545. }
  546. #if defined _X360
  547. // X_CONTEXT_GAME_TYPE
  548. pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_TYPE ), X_CONTEXT_GAME_TYPE_STANDARD );
  549. pResult->SetInt( CFmtStr( "Contexts/%d", X_CONTEXT_GAME_MODE ), CONTEXT_GAME_MODE_CSS_GAME_MODE_MULTIPLAYER );
  550. // X_CONTEXT_GAME_MODE
  551. if ( char const *szValue = pSettings->GetString( "game/mode", NULL ) )
  552. {
  553. DWORD dwValue = g_GameModeContexts->ScanValues( szValue );
  554. if ( bCssGameMode && dwValue != 0xFFFF )
  555. {
  556. pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_CSS_GAME_MODE ), dwValue );
  557. }
  558. // Omit this property based on the rule.
  559. // Set the PROPERTY_CSS_GAME_MODE_AS_NUMBER value as the square of the CONTEXT_CSS_GAME_MODE Difficulty setting.
  560. // The resulting sequence of numbers (0, 1, 4, 9, ...) ensures that if an exact match can't be found
  561. // then the 'near' sort operation will prefer the next easier match to the next harder match. For example,
  562. // if COMPETITIVE is requested (1), then CASUAL matches (0) would be preferred to PRO matches (4).
  563. dwValue = g_GameModeAsNumberContexts->ScanValues( szValue );
  564. if ( bCssGameModeAsNumber && dwValue != 0xFFFF )
  565. {
  566. pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_CSS_GAME_MODE_AS_NUMBER ), dwValue );
  567. }
  568. }
  569. // Set the game type (classic or gungame)
  570. if ( char const *szGameType = pSettings->GetString( "game/type", NULL ) )
  571. {
  572. DWORD dwValue = g_GameTypeContexts->ScanValues( szGameType );
  573. if ( bCssGameType && dwValue != 0xFFFF )
  574. {
  575. pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_CSS_GAME_TYPE ), dwValue );
  576. }
  577. }
  578. // Set the matchmaking version.
  579. if ( bCssMatchVersion )
  580. {
  581. pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_CSS_MATCH_VERSION ), mm_title_debug_version.GetInt() );
  582. }
  583. if ( 0 && mm_title_debug_minquery.GetBool() )
  584. {
  585. DescribeX360QueryDefineSessionSearchKeys( pResult );
  586. return pResult; // don't run the rest of filters, run with minimal set of parameters
  587. }
  588. // Set the mapgroup name we're using, if any.
  589. if ( char const *szMapGroupName = pSettings->GetString( "game/mapgroupname", NULL ) )
  590. {
  591. // First check if the rich presence context for this map was set in gamemodes.txt
  592. DWORD dwValue = dwValue = pSettings->GetInt( "game/mapRichPresence", 0xFFFF );
  593. if ( dwValue == 0xFFFF )
  594. {
  595. dwValue = g_MapGroupContexts->ScanValues( szMapGroupName );
  596. }
  597. if ( bCssLevel && dwValue != 0xFFFF )
  598. {
  599. pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_CSS_MAP_GROUP ), dwValue );
  600. }
  601. }
  602. // Set desire to find games in lobbies
  603. if ( bMatchmakingQueryForGameInProgress )
  604. {
  605. pResult->SetInt( CFmtStr( "Contexts/%d", CONTEXT_GAME_STATE ), CONTEXT_GAME_STATE_MULTIPLAYER );
  606. }
  607. #endif
  608. // Determine if we're playing gungameprogressive mode so we can use the proper matchmaking data fields.
  609. MatchmakingDataType mmDataType = MMDATA_TYPE_GENERAL;
  610. if ( !V_stricmp( "gungameprogressive", pSettings->GetString( "game/mode" ) ) )
  611. {
  612. mmDataType = MMDATA_TYPE_GGPROGRESSIVE;
  613. }
  614. // Add steam version of rule
  615. AddSteamMatchmakingRule(pResult, !bMatchmakingQueryForGameInProgress, pSettings, bCssMatchVersion, bCssLevel, bCssGameType, bCssGameMode, bTeamMatch);
  616. // If we want a clan-preferred match, duplicate search keys. First search for clan only
  617. // matches and then regular matches
  618. const char *szOppTeamType = pSettings->GetString( "game/opp_team_type", "");
  619. bTeamMatchTypeClan = !Q_stricmp( "clan", szOppTeamType );
  620. if ( !bTeamMatchTypeClan )
  621. {
  622. bTeamMatchTypeClanPreferred = !Q_stricmp( "clan_preferred", szOppTeamType );
  623. }
  624. KeyValues *pTemplate = pResult->MakeCopy();
  625. KeyValues::AutoDelete autoDeleteEvent( pTemplate );
  626. if ( IsX360() )
  627. {
  628. #ifdef _X360
  629. if ( bConTeamMatch )
  630. {
  631. pResult->SetInt( CFmtStr( "Properties/%d", PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS ), numPlayers );
  632. }
  633. #endif
  634. }
  635. else
  636. {
  637. if ( uint64 uiDependentLobby = pSettings->GetUint64( "System/dependentlobby", 0ull ) )
  638. {
  639. pResult->SetUint64( "DependentLobby", uiDependentLobby );
  640. }
  641. }
  642. DescribeX360QueryDefineSessionSearchKeys( pResult );
  643. return pResult;
  644. }
  645. // Initializes full game settings from potentially abbreviated game settings
  646. void CMatchTitleGameSettingsMgr::InitializeGameSettings( KeyValues *pSettings, const char *szReason )
  647. {
  648. // GS - Make sure match settings are loaded. For team lobbies we create a lobby without first
  649. // searching for one, so this function will be called before others (like DefineSessionSearchKeys)
  650. // that also call LoadMatchSettings()
  651. LoadMatchSettings();
  652. //char const *szNetwork = pSettings->GetString( "system/network", "LIVE" );
  653. if ( KeyValues *kv = pSettings->FindKey( "game", true ) )
  654. {
  655. kv->SetString( "state", "lobby" );
  656. }
  657. const char *pMapGroupName = pSettings->GetString( "game/mapgroupname", NULL );
  658. // if no mapgroup specified, randomly select a mapgroup based on type and mode
  659. if ( !pMapGroupName )
  660. {
  661. const char *pGameTypeName = pSettings->GetString( "game/type", NULL );
  662. const char *pGameModeName = pSettings->GetString( "game/mode", NULL );
  663. if ( pGameTypeName && pGameModeName )
  664. {
  665. pMapGroupName = g_pGameTypes->GetRandomMapGroup( pGameTypeName, pGameModeName );
  666. if ( pMapGroupName )
  667. {
  668. pSettings->SetString( "game/mapgroupname", pMapGroupName );
  669. }
  670. }
  671. }
  672. // map name should not be coming in here, only mapgroup, so set mapname from mapgroupname
  673. SetBspnameFromMapgroup( pSettings );
  674. // Set the number of slots and the rich presence context based on the map.
  675. const char *szMap = pSettings->GetString( "game/map", NULL );
  676. int numSlots = pSettings->GetInt( "members/numSlots", 10 );
  677. uint32 dwRichPresenceContext = 0xFFFF;
  678. if ( szMap )
  679. {
  680. g_pGameTypes->GetMapInfo( szMap, dwRichPresenceContext );
  681. }
  682. // If this is an official game, then we know how many slots we will force
  683. if ( !Q_stricmp( "searchempty", pSettings->GetString( "options/createreason" ) ) &&
  684. !pSettings->GetBool( "game/hosted" ) )
  685. {
  686. const char *pGameTypeName = pSettings->GetString( "game/type" );
  687. const char *pGameModeName = pSettings->GetString( "game/mode" );
  688. int iType, iMode;
  689. if ( g_pGameTypes->GetGameModeAndTypeIntsFromStrings( pGameTypeName, pGameModeName, iType, iMode ) )
  690. numSlots = g_pGameTypes->GetMaxPlayersForTypeAndMode( iType, iMode );
  691. }
  692. pSettings->SetInt( "members/numSlots", numSlots );
  693. #ifdef _X360
  694. int extraSpectators = 2;
  695. pSettings->SetInt( "members/numExtraSpectatorSlots", extraSpectators );
  696. pSettings->SetInt( "game/mapRichPresence", dwRichPresenceContext );
  697. pSettings->SetInt( "game/matchversion", mm_title_debug_version.GetInt() );
  698. pSettings->SetInt("game/experience", 0);
  699. #endif
  700. if ( !IsX360() )
  701. {
  702. // Add search key as a filter
  703. static ConVarRef sv_search_key( "sv_search_key" );
  704. CFmtStr searchKey( "k%s%d",
  705. sv_search_key.GetString(),
  706. g_pMatchExtensions->GetINetSupport()->GetEngineBuildNumber() );
  707. pSettings->SetString( "game/search_key", searchKey.Access());
  708. // Temp: Check for sv_load_test
  709. #if !defined (NO_STEAM)
  710. if ( IsPC() && mm_sv_load_test.GetBool() )
  711. {
  712. const char* playerCountry = steamapicontext->SteamUtils()->GetIPCountry();
  713. if ( !Q_stricmp( playerCountry, "US") )
  714. {
  715. pSettings->SetInt( "options/sv_load_test", 1 );
  716. }
  717. }
  718. #endif
  719. }
  720. if ( KeyValues *kvLocalPlayer = pSettings->FindKey( "members/machine0/player0" ) )
  721. {
  722. #if !defined (NO_STEAM)
  723. //
  724. // Clan information
  725. //
  726. SplitScreenConVarRef varOption( "cl_clanid" );
  727. const char *pClanID = varOption.GetString( 0 );
  728. uint32 iPlayerClanID = atoi( pClanID );
  729. kvLocalPlayer->SetInt( "game/clanID", iPlayerClanID );
  730. ISteamFriends *pFriends = steamapicontext->SteamFriends();
  731. if ( pFriends )
  732. {
  733. int iGroupCount = pFriends->GetClanCount();
  734. for ( int k = 0; k < iGroupCount; ++ k )
  735. {
  736. CSteamID clanID = pFriends->GetClanByIndex( k );
  737. if ( clanID.GetAccountID() == iPlayerClanID )
  738. {
  739. CSteamID clanID( iPlayerClanID, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeClan );
  740. // valid clan, accept the change
  741. const char *szClanTag = pFriends->GetClanTag( clanID );
  742. char chLimitedTag[ MAX_CLAN_TAG_LENGTH ];
  743. CopyStringTruncatingMalformedUTF8Tail( chLimitedTag, szClanTag, MAX_CLAN_TAG_LENGTH );
  744. const char *szClanName = pFriends->GetClanName( clanID );
  745. kvLocalPlayer->SetString( "game/clantag", chLimitedTag );
  746. kvLocalPlayer->SetString( "game/clanname", szClanName );
  747. }
  748. }
  749. }
  750. //
  751. // Ticketing information for friends
  752. //
  753. if ( g_pMatchExtensions->GetIBaseClientDLL() )
  754. {
  755. g_pMatchExtensions->GetIBaseClientDLL()->DetermineSubscriptionKvToAdvertise( kvLocalPlayer );
  756. }
  757. // If we are joining via friend discovery then pass it to the host
  758. if ( uint64 xuidFriendJoin = pSettings->GetUint64( "options/friendxuid" ) )
  759. {
  760. kvLocalPlayer->SetUint64( "game/jfriend", xuidFriendJoin );
  761. }
  762. // If we are joining via nearby discovery then pass it to the host
  763. if ( int nNearbyJoin = pSettings->GetInt( "options/nby" ) )
  764. {
  765. kvLocalPlayer->SetInt( "game/nby", nNearbyJoin );
  766. }
  767. // If we are joining via clan discovery then pass it to the host
  768. if ( char const *szClanIdOption = pSettings->GetString( "options/clanid", NULL ) )
  769. {
  770. kvLocalPlayer->SetString( "game/jclanid", szClanIdOption );
  771. if ( pFriends )
  772. {
  773. int iGroupCount = pFriends->GetClanCount();
  774. for ( int k = 0; k < iGroupCount; ++k )
  775. {
  776. CSteamID clanID = pFriends->GetClanByIndex( k );
  777. if ( clanID.GetAccountID() == iPlayerClanID )
  778. {
  779. CSteamID clanID( iPlayerClanID, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeClan );
  780. // valid clan, accept the change
  781. const char *szClanTag = pFriends->GetClanTag( clanID );
  782. char chLimitedTag[ MAX_CLAN_TAG_LENGTH ];
  783. CopyStringTruncatingMalformedUTF8Tail( chLimitedTag, szClanTag, MAX_CLAN_TAG_LENGTH );
  784. kvLocalPlayer->SetString( "game/jclantag", chLimitedTag );
  785. }
  786. }
  787. }
  788. }
  789. // After we initialized local player transfer that data into session
  790. pSettings->SetInt( "game/ark", kvLocalPlayer->GetInt( "game/ranking" )*10 );
  791. pSettings->SetInt( "game/apr", kvLocalPlayer->GetInt( "game/prime" ) );
  792. pSettings->SetString( "game/loc", kvLocalPlayer->GetString( "game/loc" ) );
  793. #endif
  794. }
  795. }
  796. void CMatchTitleGameSettingsMgr::SetBspnameFromMapgroup( KeyValues *pSettings )
  797. {
  798. const char *pMapGroupName = pSettings->GetString( "game/mapgroupname", NULL );
  799. const char *pMapName = pSettings->GetString( "game/map", NULL );
  800. if ( !pMapName && pMapGroupName )
  801. {
  802. pMapName = g_pGameTypes->GetRandomMap( pMapGroupName );
  803. if ( pMapName && pMapName[0] )
  804. {
  805. pSettings->SetString( "game/map", pMapName );
  806. }
  807. }
  808. }
  809. // Extends game settings update packet before it gets merged with
  810. // session settings and networked to remote clients
  811. void CMatchTitleGameSettingsMgr::ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys )
  812. {
  813. if ( char const *szClanIdUpdate = pUpdateDeleteKeys->GetString( "update/game/clanid", NULL ) )
  814. { // Ensure that clantag is also set when setting clanid
  815. if ( uint64 xuid = V_atoui64( szClanIdUpdate ) )
  816. {
  817. CSteamID steamIdClan( xuid );
  818. const char *pTag = steamapicontext->SteamFriends()->GetClanTag( steamIdClan );
  819. if ( pTag && *pTag )
  820. {
  821. char chLimitedTag[ MAX_CLAN_TAG_LENGTH ];
  822. CopyStringTruncatingMalformedUTF8Tail( chLimitedTag, pTag, MAX_CLAN_TAG_LENGTH );
  823. pUpdateDeleteKeys->SetString( "update/game/clantag", chLimitedTag );
  824. }
  825. else
  826. {
  827. pUpdateDeleteKeys->SetString( "update/game/clantag", "" );
  828. }
  829. }
  830. }
  831. if ( char const *szClanIdUpdate = pUpdateDeleteKeys->GetString( "delete/game/clanid", NULL ) )
  832. {
  833. pUpdateDeleteKeys->SetString( "delete/game/clantag", "" );
  834. }
  835. if ( char const *szMmQueueUpdate = pUpdateDeleteKeys->GetString( "update/game/mmqueue", NULL ) )
  836. {
  837. int nAPR = pSettings->GetInt( "game/apr" );
  838. int nUpdateAlready = pUpdateDeleteKeys->GetInt( "update/game/apr", -1 );
  839. if ( nUpdateAlready >= 0 )
  840. nAPR = nUpdateAlready;
  841. pUpdateDeleteKeys->SetInt( "update/game/apr", nAPR | 0x2 );
  842. }
  843. if ( char const *szMmQueueUpdate = pUpdateDeleteKeys->GetString( "delete/game/mmqueue", NULL ) )
  844. {
  845. int nAPR = pSettings->GetInt( "game/apr" );
  846. int nUpdateAlready = pUpdateDeleteKeys->GetInt( "update/game/apr", -1 );
  847. if ( nUpdateAlready >= 0 )
  848. nAPR = nUpdateAlready;
  849. pUpdateDeleteKeys->SetInt( "update/game/apr", nAPR & ~0x2 );
  850. }
  851. }
  852. KeyValues *CMatchTitleGameSettingsMgr::ExtendTeamLobbyToGame( KeyValues *pSettings )
  853. {
  854. KeyValues *pUpdate = KeyValues::FromString(
  855. "update",
  856. " update { "
  857. " system { "
  858. " network LIVE "
  859. " netFlag #empty#"
  860. " } "
  861. " options { "
  862. " bypasslobby 1"
  863. " } "
  864. " game {"
  865. " } "
  866. " members {"
  867. " } "
  868. " } "
  869. );
  870. // Add in bsp name from map group name
  871. const char *pMapGroupName = pSettings->GetString( "game/mapgroupname", NULL );
  872. Assert( pMapGroupName );
  873. const char *pMapName = pSettings->GetString( "game/map", NULL );
  874. Assert( pMapName );
  875. if ( !pMapName )
  876. {
  877. pMapName = g_pGameTypes->GetRandomMap( pMapGroupName );
  878. pUpdate->SetString( "udpate/game/map", pMapName );
  879. }
  880. DevMsg( "CMatchTitleGameSettingsMgr::ExtendTeamLobbyToGame\n" );
  881. KeyValuesDumpAsDevMsg( pUpdate );
  882. return pUpdate;
  883. }
  884. // Prepares system for session creation
  885. KeyValues * CMatchTitleGameSettingsMgr::PrepareForSessionCreate( KeyValues *pSettings )
  886. {
  887. // It's at this point that we need to set all of the appropriate properties on the local machine
  888. // for matchmaking searches.
  889. return MM_Title_RichPresence_PrepareForSessionCreate( pSettings );
  890. }
  891. // Prepares the lobby for game or adjust settings of new players who
  892. // join a game in progress, this function is allowed to modify
  893. // Members/Game subkeys and has to write modified players XUIDs
  894. void CMatchTitleGameSettingsMgr::PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated )
  895. {
  896. // set player avatar/teams, etc
  897. }
  898. // Prepares the host team lobby for game adjusting the game settings
  899. // this function is allowed to prepare modification package to update
  900. // Game subkeys.
  901. // Returns the update/delete package to be applied to session settings
  902. // and pushed to dependent two sesssion of the two teams.
  903. KeyValues * CMatchTitleGameSettingsMgr::PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote )
  904. {
  905. return NULL;
  906. }
  907. void UpdateAggregateMembersSettings( KeyValues *pFullGameSettings, KeyValues *pUpdate )
  908. {
  909. bool bAllPrime = true;
  910. int nAvgRank = 0;
  911. int nHaveRank = 0;
  912. int nTotalPlayers = 0;
  913. char const *szBestCountry = "";
  914. float flBestCountryWeight = 0.0f;
  915. CUtlStringMap< float > mapPlayerCountries;
  916. static CSteamID s_mysteamid = steamapicontext->SteamUser()->GetSteamID();
  917. for ( int iMachine = 0, numMachines = pFullGameSettings->GetInt( "members/numMachines" ); iMachine < numMachines; ++iMachine )
  918. {
  919. KeyValues *pMachine = pFullGameSettings->FindKey( CFmtStr( "members/machine%d", iMachine ) );
  920. for ( int iPlayer = 0, numPlayers = pMachine->GetInt( "numPlayers" ); iPlayer < numPlayers; ++iPlayer )
  921. {
  922. KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", iPlayer ) );
  923. if ( !pPlayer )
  924. continue;
  925. if ( !pPlayer->GetInt( "game/prime" ) )
  926. bAllPrime = false;
  927. int nRanking = pPlayer->GetInt( "game/ranking" );
  928. if ( nRanking )
  929. {
  930. nAvgRank += nRanking;
  931. ++ nHaveRank;
  932. }
  933. ++ nTotalPlayers;
  934. char const *szLocation = pPlayer->GetString( "game/loc" );
  935. if ( !*szLocation )
  936. continue;
  937. UtlSymId_t symid = mapPlayerCountries.Find( szLocation );
  938. if ( symid == UTL_INVAL_SYMBOL )
  939. symid = mapPlayerCountries.Insert( szLocation, 0.0f );
  940. float flNewWeightOfThisCountry = (
  941. mapPlayerCountries[symid] += ( 1.0f + ( ( CSteamID( pPlayer->GetUint64( "xuid" ) ).GetAccountID() == s_mysteamid.GetAccountID() ) ? 0.5f : 0.0f ) )
  942. );
  943. if ( flNewWeightOfThisCountry > flBestCountryWeight )
  944. {
  945. szBestCountry = szLocation;
  946. flBestCountryWeight = flNewWeightOfThisCountry;
  947. }
  948. }
  949. }
  950. if ( !nAvgRank )
  951. {
  952. nAvgRank = 7 * 10; // Nova II
  953. }
  954. else if ( nHaveRank == nTotalPlayers )
  955. {
  956. nAvgRank = ( nAvgRank * 10 ) / nHaveRank;
  957. }
  958. else
  959. {
  960. int nAvgRankedPeople = ( nAvgRank * 10 );
  961. for ( ; nHaveRank < nTotalPlayers; ++ nHaveRank )
  962. {
  963. nAvgRankedPeople += ( nAvgRankedPeople * 9 / nHaveRank / 10 );
  964. }
  965. nAvgRank = nAvgRankedPeople / nHaveRank;
  966. }
  967. int nAPR = bAllPrime ? 1 : 0;
  968. if ( *pFullGameSettings->GetString( "game/mmqueue" ) )
  969. nAPR |= 0x2;
  970. int numSlots = 5;
  971. if ( !V_stricmp( pFullGameSettings->GetString( "game/mode" ), "cooperative" ) )
  972. numSlots = 2;
  973. if ( nTotalPlayers >= numSlots )
  974. nAPR |= 0x4;
  975. pUpdate->SetInt( "game/ark", nAvgRank );
  976. pUpdate->SetInt( "game/apr", nAPR );
  977. pUpdate->SetString( "game/loc", szBestCountry );
  978. #ifdef _DEBUG
  979. DevMsg( "UpdateAggregateMembersSettings: ark %d->%d, apr %d->%d, loc %s->%s\n",
  980. pFullGameSettings->GetInt( "game/ark" ), nAvgRank,
  981. pFullGameSettings->GetInt( "game/apr" ), nAPR,
  982. pFullGameSettings->GetString( "game/loc" ), szBestCountry );
  983. #endif
  984. }
  985. // Executes the command on the session settings, this function on host
  986. // is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues
  987. // When running on a remote client "ppPlayersUpdated" is NULL and players cannot
  988. // be modified
  989. void CMatchTitleGameSettingsMgr::ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated )
  990. {
  991. char const *szCommand = pCommand->GetName();
  992. if ( !Q_stricmp( "Game::SetPlayerRanking", szCommand ) )
  993. {
  994. if ( !Q_stricmp( "host", pSessionSystemData->GetString( "type", "host" ) ) &&
  995. // !Q_stricmp( "lobby", pSessionSystemData->GetString( "state", "lobby" ) ) && - avatars also update when players change team ingame
  996. ppPlayersUpdated )
  997. {
  998. XUID xuidPlayer = pCommand->GetUint64( "xuid" );
  999. // We know that the current session is the host. Validate that it is either updating itself, or a remote user
  1000. // is updating their own data
  1001. if ( !pSessionSystemData // offline session OK
  1002. || ( ( xuidPlayer == pSessionSystemData->GetUint64( "xuidHost" ) ) && !pCommand->GetUint64( "_remote_xuidsrc" ) ) // host player update issued locally
  1003. || ( xuidPlayer == pCommand->GetUint64( "_remote_xuidsrc" ) ) // remote command on behalf of matching XUID
  1004. )
  1005. ; // ok to process this update
  1006. else
  1007. return;
  1008. // Find the layer that is going to be updated
  1009. KeyValues *pPlayer = SessionMembersFindPlayer( pSettings, xuidPlayer );
  1010. if ( !pPlayer )
  1011. return;
  1012. KeyValues *kvGameKey = pCommand->FindKey( "game" );
  1013. if ( pPlayer && kvGameKey )
  1014. {
  1015. KeyValues *kvPlayerGame = pPlayer->FindKey( "game", true );
  1016. if ( !kvPlayerGame )
  1017. return;
  1018. kvPlayerGame->MergeFrom( kvGameKey, KeyValues::MERGE_KV_UPDATE );
  1019. // Notify the sessions of a player update
  1020. * ( ppPlayersUpdated ++ ) = pPlayer;
  1021. // Also recompute aggregate session fields
  1022. if ( IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession() )
  1023. {
  1024. KeyValues *kvPackage = new KeyValues( "Update" );
  1025. if ( KeyValues *kvUpdate = kvPackage->FindKey( "update", true ) )
  1026. {
  1027. UpdateAggregateMembersSettings( pMatchSession->GetSessionSettings(), kvUpdate );
  1028. }
  1029. pMatchSession->UpdateSessionSettings( KeyValues::AutoDeleteInline( kvPackage ) );
  1030. }
  1031. }
  1032. return;
  1033. }
  1034. }
  1035. }
  1036. void CMatchTitleGameSettingsMgr::LoadMatchSettings( void )
  1037. {
  1038. // Only load the match settings once.
  1039. if ( m_pMatchSystemData )
  1040. {
  1041. return;
  1042. }
  1043. m_pMatchSystemData = new KeyValues( "" );
  1044. // Load the match system keyvalues file if we haven't already.
  1045. if ( !m_pMatchSystemData->LoadFromFile( g_pFullFileSystem, "resource\\MatchSystem.res", "GAME" ) )
  1046. {
  1047. m_pMatchSystemData->deleteThis();
  1048. m_pMatchSystemData = NULL;
  1049. }
  1050. if ( !m_pMatchSystemData )
  1051. return;
  1052. // Get the version of the match file and compare to our latest parsed version.
  1053. // Will be used for PROPERTY_CSS_MATCH_VERSION.
  1054. int version = m_pMatchSystemData->GetInt( "version", -1 );
  1055. if ( ( mm_title_debug_version.GetInt() < 100 ) && ( mm_title_debug_version.GetInt() >= version ) )
  1056. {
  1057. // Version file is not newer, so use what we already have.
  1058. AssertMsg( mm_title_debug_version.GetInt() >= 0, "Failed to load any match settings. Matchmaking will likely fail." );
  1059. return;
  1060. }
  1061. if ( mm_title_debug_version.GetInt() < 100 )
  1062. mm_title_debug_version.SetValue( version );
  1063. // Remove all previous formulas.
  1064. m_FormulaExperience.Purge();
  1065. // Load the experience formula.
  1066. KeyValues *pExperienceFormula = m_pMatchSystemData->FindKey( "ExperienceFormula" );
  1067. if ( pExperienceFormula )
  1068. {
  1069. // Search for keys that are a number that corresponds to the skill number they should be used for.
  1070. for ( int nSkillIndex=0; ; ++nSkillIndex )
  1071. {
  1072. char const *pszFormula = pExperienceFormula->GetString( CFmtStr( "%d", nSkillIndex ), NULL );
  1073. if ( !pszFormula || !*pszFormula )
  1074. {
  1075. // No more formulas specified.
  1076. break;
  1077. }
  1078. m_FormulaExperience.AddToTail( pszFormula );
  1079. }
  1080. // Get the min/max range for valid values for these formulas.
  1081. m_nFormulaExperienceRangeMin = pExperienceFormula->GetInt( "minvalue", 0 );
  1082. m_nFormulaExperienceRangeMax = pExperienceFormula->GetInt( "maxvalue", INT_MAX );
  1083. }
  1084. // Remove all previous formulas.
  1085. for ( int i=0; i<m_FormulaSkill.Count(); ++i )
  1086. {
  1087. delete m_FormulaSkill[i];
  1088. }
  1089. m_FormulaSkill.Purge();
  1090. // Load the skill formulas.
  1091. for ( int nSkillFormulaIndex=0; ; ++nSkillFormulaIndex )
  1092. {
  1093. KeyValues *pSkillFormulaInfo = m_pMatchSystemData->FindKey( CFmtStr( "Skill%dFormula", nSkillFormulaIndex ) );
  1094. if ( !pSkillFormulaInfo )
  1095. {
  1096. // No more formulas specified.
  1097. break;
  1098. }
  1099. SkillFormulas *pSkillFormulas = new SkillFormulas();
  1100. // Search for keys that are a number that corresponds to the skill number they should be used for.
  1101. for ( int nSkillIndex=0; ; ++nSkillIndex )
  1102. {
  1103. char const *pszFormula = pSkillFormulaInfo->GetString( CFmtStr( "%d", nSkillIndex ), NULL );
  1104. if ( !pszFormula || !*pszFormula )
  1105. {
  1106. // No more formulas specified.
  1107. break;
  1108. }
  1109. pSkillFormulas->formulas.AddToTail( pszFormula );
  1110. }
  1111. // Get the min/max range for valid values for these formulas.
  1112. pSkillFormulas->rangeMin = pSkillFormulaInfo->GetInt( "minvalue", 0 );
  1113. pSkillFormulas->rangeMax = pSkillFormulaInfo->GetInt( "maxvalue", INT_MAX );
  1114. m_FormulaSkill.AddToTail( pSkillFormulas );
  1115. }
  1116. // Load the average formula.
  1117. KeyValues *pAverageFormula = m_pMatchSystemData->FindKey( "AvgFormula" );
  1118. if ( pAverageFormula )
  1119. {
  1120. // Search for keys that are a number that corresponds to the skill number they should be used for.
  1121. for ( int nSkillIndex=0; ; ++nSkillIndex )
  1122. {
  1123. char const *pszFormula = pAverageFormula->GetString( CFmtStr( "%d", nSkillIndex ), NULL );
  1124. if ( !pszFormula || !*pszFormula )
  1125. {
  1126. // No more formulas specified.
  1127. break;
  1128. }
  1129. m_FormulaAverage.AddToTail( pszFormula );
  1130. }
  1131. }
  1132. // Remove all previous search passes.
  1133. for ( int i=0; i<m_SearchPass.Count(); ++i )
  1134. {
  1135. delete m_SearchPass[i];
  1136. }
  1137. m_SearchPass.Purge();
  1138. // Load the search passes.
  1139. for ( int nSearchPassIndex=0; ; ++nSearchPassIndex )
  1140. {
  1141. KeyValues *pSearchPassInfo = m_pMatchSystemData->FindKey( CFmtStr( "SearchPass%d", nSearchPassIndex ) );
  1142. if ( !pSearchPassInfo )
  1143. {
  1144. // No more passes specified.
  1145. break;
  1146. }
  1147. SearchPass *pSearchPass = new SearchPass();
  1148. // Get the experience checks for this search pass.
  1149. pSearchPass->checkExperience = ( pSearchPassInfo->GetInt( "ExpCheck", 0 ) != 0 );
  1150. pSearchPass->experienceRange = pSearchPassInfo->GetInt( "ExperienceRange", 0 );
  1151. // Loop over all of the possible Skill#Check and Skill#Range entries
  1152. // so we can store their values for quick reference.
  1153. for ( int nSkillIndex=0; nSkillIndex<MATCH_MAX_SKILL_FIELDS; ++nSkillIndex )
  1154. {
  1155. pSearchPass->checkSkill.AddToTail( ( pSearchPassInfo->GetInt( CFmtStr( "Skill%dCheck", nSkillIndex ), 0 ) != 0 ) );
  1156. pSearchPass->skillRange.AddToTail( pSearchPassInfo->GetInt( CFmtStr( "Skill%dRange", nSkillIndex ), 0 ) );
  1157. }
  1158. m_SearchPass.AddToTail( pSearchPass );
  1159. }
  1160. }
  1161. // Retrieves the indexed formula from the match system settings file. (MatchSystem.360.res)
  1162. char const * CMatchTitleGameSettingsMgr::GetFormulaAverage( int index )
  1163. {
  1164. // Ensure the matchmaking settings are loaded.
  1165. LoadMatchSettings();
  1166. if ( !m_FormulaAverage.Count() )
  1167. return "newValue";
  1168. int indexClamped = clamp( index, 0, m_FormulaAverage.Count() - 1 );
  1169. return m_FormulaAverage[ indexClamped ].String();
  1170. }
  1171. // Add Steam version of X360 matchmaking rules. Writes to pResult
  1172. void CMatchTitleGameSettingsMgr::AddSteamMatchmakingRule( KeyValues *pResult, bool bAllSessions,
  1173. KeyValues *pSettings, bool bCssMatchVersion, bool bCssLevel, bool bCssGameType,
  1174. bool bCssGameMode, bool bTeamMatch )
  1175. {
  1176. // Check for official server
  1177. char const *serverType = pSettings->GetString( "options/server", "");
  1178. if ( !Q_stricmp( serverType, "official" ))
  1179. {
  1180. pResult->SetString( "Filter=/options:server", serverType);
  1181. }
  1182. // Determine whether we are looking for community games or not
  1183. int iHosted = pSettings->GetInt( "game/hosted", 0 );
  1184. pResult->SetInt( "Filter=/game:hosted", iHosted );
  1185. int minPlayers = mm_csgo_community_search_players_min.GetInt();
  1186. // even if the key is not set on the lobby the MMS code for numerical compares
  1187. // must pass the check due to comparing it against default zero value
  1188. // Check for sv_search_key
  1189. char const *searchKey = pSettings->GetString( "game/search_key", NULL );
  1190. if ( searchKey )
  1191. {
  1192. pResult->SetString( "Filter=/game:search_key", searchKey );
  1193. }
  1194. // Set up key values that are common across SESSION_MATCH_QUERY_PLAYER_MATCH
  1195. // and SESSION_MATCH_QUERY_PLAYER_MATCH_ANY_LEVEL
  1196. if ( !bAllSessions )
  1197. {
  1198. const char *pMapGroupName = pSettings->GetString( "game/mapgroupname", NULL );
  1199. if ( pMapGroupName && strstr( pMapGroupName, "@workshop" ) && pSettings->GetBool( "options/anytypemode" ) )
  1200. {
  1201. bCssGameMode = false;
  1202. bCssGameType = false;
  1203. }
  1204. // Game mode
  1205. if (bCssGameMode)
  1206. {
  1207. char const *gameMode = pSettings->GetString( "game/mode", NULL );
  1208. AssertMsg(gameMode, "Matchmaking: Rule SESSION_MATCH_QUERY_PLAYER_MATCH - no game mode; ignoring this filter");
  1209. if ( gameMode )
  1210. {
  1211. pResult->SetString("Filter=/game:mode", gameMode);
  1212. }
  1213. }
  1214. #ifdef _X360
  1215. // Matchmaking rules version
  1216. if (bCssMatchVersion)
  1217. {
  1218. pResult->SetInt("Filter=/game:matchversion", mm_title_debug_version.GetInt() );
  1219. }
  1220. #endif
  1221. // Privacy
  1222. char const *privacy = pSettings->GetString( "system/access", NULL );
  1223. AssertMsg(privacy, "Matchmaking: Rule SESSION_MATCH_QUERY_PLAYER_MATCH - no access mode; ignoring this filter");
  1224. if ( privacy )
  1225. {
  1226. pResult->SetString("Filter=/system:access", privacy);
  1227. }
  1228. // Game type
  1229. if (bCssGameType)
  1230. {
  1231. char const *gameType = pSettings->GetString( "game/type", NULL );
  1232. //AssertMsg(gameType, "Matchmaking: Rule SESSION_MATCH_QUERY_PLAYER_MATCH - no game type; ignoring this filter");
  1233. if (gameType)
  1234. {
  1235. pResult->SetString("Filter=/game:type", gameType);
  1236. }
  1237. }
  1238. }
  1239. // Map name
  1240. if (bCssLevel)
  1241. {
  1242. const char *pMapGroupName = pSettings->GetString( "game/mapgroupname", NULL );
  1243. //AssertMsg(pMapGroupName, "Matchmaking: Rule SESSION_MATCH_QUERY_PLAYER_MATCH - no mapgroup name; ignoring this filter");
  1244. if ( pMapGroupName )
  1245. {
  1246. // Check for workshop map matchmaking case
  1247. if ( char const *pszWorkshopMapGroup = strstr( pMapGroupName, "@workshop" ) )
  1248. {
  1249. // search based on workshop map group regardless of the collection
  1250. pResult->SetString( "Filter=/game:map", pszWorkshopMapGroup + 1 );
  1251. minPlayers = 0; // when searching for a workshop map don't require min players
  1252. }
  1253. else
  1254. {
  1255. pResult->SetString("Filter=/game:mapgroupname", pMapGroupName);
  1256. }
  1257. }
  1258. }
  1259. if ( iHosted && ( minPlayers > 0 ) )
  1260. {
  1261. pResult->SetInt( "Filter>=/members:numPlayers", minPlayers );
  1262. }
  1263. else
  1264. {
  1265. pResult->SetInt( "Filter</members:numPlayers", 5 ); // always look for lobbies that are not full
  1266. }
  1267. if ( const char *pGameState = pSettings->GetString( "game/state", NULL ) )
  1268. {
  1269. pResult->SetString( "Filter=/game:state", pGameState );
  1270. }
  1271. if ( KeyValues *kvAllPrime = pSettings->FindKey( "game/apr" ) )
  1272. {
  1273. pResult->SetInt( "Filter=/game:apr", kvAllPrime->GetInt() );
  1274. }
  1275. if ( KeyValues *kvNearRank = pSettings->FindKey( "game/ark" ) )
  1276. {
  1277. if ( !kvNearRank->GetInt() )
  1278. {
  1279. pResult->SetInt( "Filter>=/game:ark", 7*10 ); // Nova 1
  1280. pResult->SetInt( "Filter<=/game:ark", 12*10 ); // MG II
  1281. pResult->SetInt( "Near/game:ark", 9*10 ); // Nova III
  1282. }
  1283. else
  1284. {
  1285. pResult->SetInt( "Filter>=/game:ark", ( kvNearRank->GetInt() - 2 ) * 10 );
  1286. pResult->SetInt( "Filter<=/game:ark", ( kvNearRank->GetInt() + 2 ) * 10 );
  1287. pResult->SetInt( "Near/game:ark", kvNearRank->GetInt()*10 );
  1288. }
  1289. pResult->SetInt( "Near/members:numPlayers", 5 ); // always look for lobbies that are close to full
  1290. }
  1291. if ( const char *pMMQueue = pSettings->GetString( "game/mmqueue", NULL ) )
  1292. {
  1293. pResult->SetString( "Filter=/game:mmqueue", pMMQueue );
  1294. }
  1295. if ( const char *szClanID = pSettings->GetString( "game/clanid", NULL ) )
  1296. {
  1297. pResult->SetString( "Filter=/game:clanid", szClanID );
  1298. }
  1299. }
  1300. // Called by the client to notify matchmaking that it should update matchmaking properties based
  1301. // on player distribution among the teams.
  1302. void CMatchTitleGameSettingsMgr::UpdateTeamProperties( KeyValues *pCurrentSettings, KeyValues *pTeamProperties )
  1303. {
  1304. MM_Title_RichPresence_UpdateTeamPropertiesCSGO( pCurrentSettings, pTeamProperties );
  1305. }
  1306. #ifdef _X360
  1307. void MM_dumpcontextsandproperties( void )
  1308. {
  1309. IXboxSystem *pXboxSystem = g_pMatchExtensions->GetIXboxSystem();
  1310. if ( pXboxSystem )
  1311. {
  1312. DWORD userIndex = XBX_GetPrimaryUserId();
  1313. uint value = 0;
  1314. KeyValues *pkv = new KeyValues( "ContextsAndProperties" );
  1315. KeyValues::AutoDelete autoDeleteEvent( pkv );
  1316. pXboxSystem->UserGetContext( userIndex, X_CONTEXT_GAME_TYPE, value );
  1317. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(X_CONTEXT_GAME_TYPE)", X_CONTEXT_GAME_TYPE ), value );
  1318. pXboxSystem->UserGetContext( userIndex, X_CONTEXT_GAME_MODE, value );
  1319. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(X_CONTEXT_GAME_MODE)", X_CONTEXT_GAME_MODE ), value );
  1320. pXboxSystem->UserGetContext( userIndex, CONTEXT_CSS_LEVEL, value );
  1321. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(CONTEXT_CSS_LEVEL)", CONTEXT_CSS_LEVEL ), value );
  1322. pXboxSystem->UserGetContext( userIndex, CONTEXT_CSS_MAP_GROUP, value );
  1323. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(CONTEXT_CSS_MAP_GROUP)", CONTEXT_CSS_MAP_GROUP ), value );
  1324. pXboxSystem->UserGetContext( userIndex, CONTEXT_GAME_STATE, value );
  1325. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(CONTEXT_GAME_STATE)", CONTEXT_GAME_STATE ), value );
  1326. pXboxSystem->UserGetContext( userIndex, CONTEXT_CSS_GAME_MODE, value );
  1327. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(CONTEXT_CSS_GAME_MODE)", CONTEXT_CSS_GAME_MODE ), value );
  1328. pXboxSystem->UserGetContext( userIndex, CONTEXT_CSS_TEAM, value );
  1329. pkv->SetInt( CFmtStr( "Contexts/%d\t\t(CONTEXT_CSS_TEAM)", CONTEXT_CSS_TEAM ), value );
  1330. pXboxSystem->UserGetContext( userIndex, CONTEXT_CSS_PRIVACY, value );
  1331. pkv->SetInt( CFmtStr( "Context/%d\t\t(CONTEXT_CSS_PRIVACY)", CONTEXT_CSS_PRIVACY ), value );
  1332. pXboxSystem->UserGetContext( userIndex, CONTEXT_CSS_GAME_TYPE, value );
  1333. pkv->SetInt( CFmtStr( "Context/%d\t\t(CONTEXT_CSS_GAME_TYPE)", CONTEXT_CSS_GAME_TYPE ), value );
  1334. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_AGGREGATE_EXPERIENCE, value );
  1335. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_AGGREGATE_EXPERIENCE)", PROPERTY_CSS_AGGREGATE_EXPERIENCE ), value );
  1336. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_AGGREGATE_SKILL0, value );
  1337. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_AGGREGATE_SKILL0)", PROPERTY_CSS_AGGREGATE_SKILL0 ), value );
  1338. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_AGGREGATE_SKILL1, value );
  1339. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_AGGREGATE_SKILL1)", PROPERTY_CSS_AGGREGATE_SKILL1 ), value );
  1340. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_AGGREGATE_SKILL2, value );
  1341. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_AGGREGATE_SKILL2)", PROPERTY_CSS_AGGREGATE_SKILL2 ), value );
  1342. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_AGGREGATE_SKILL3, value );
  1343. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_AGGREGATE_SKILL3)", PROPERTY_CSS_AGGREGATE_SKILL3 ), value );
  1344. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_AGGREGATE_SKILL4, value );
  1345. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_AGGREGATE_SKILL4)", PROPERTY_CSS_AGGREGATE_SKILL4 ), value );
  1346. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_MATCH_VERSION, value );
  1347. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_MATCH_VERSION)", PROPERTY_CSS_MATCH_VERSION ), value );
  1348. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_OPEN_SLOTS, value );
  1349. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_OPEN_SLOTS)", PROPERTY_CSS_OPEN_SLOTS ), value );
  1350. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_EXP_MAX, value );
  1351. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_EXP_MAX)", PROPERTY_CSS_SEARCH_EXP_MAX ), value );
  1352. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_EXP_MIN, value );
  1353. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_EXP_MIN)", PROPERTY_CSS_SEARCH_EXP_MIN ), value );
  1354. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL0_MAX, value );
  1355. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL0_MAX)", PROPERTY_CSS_SEARCH_SKILL0_MAX ), value );
  1356. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL0_MIN, value );
  1357. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL0_MIN)", PROPERTY_CSS_SEARCH_SKILL0_MIN ), value );
  1358. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL1_MAX, value );
  1359. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL1_MAX)", PROPERTY_CSS_SEARCH_SKILL1_MAX ), value );
  1360. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL1_MIN, value );
  1361. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL1_MIN)", PROPERTY_CSS_SEARCH_SKILL1_MIN ), value );
  1362. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL2_MAX, value );
  1363. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL2_MAX)", PROPERTY_CSS_SEARCH_SKILL2_MAX ), value );
  1364. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL2_MIN, value );
  1365. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL2_MIN)", PROPERTY_CSS_SEARCH_SKILL2_MIN ), value );
  1366. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL3_MAX, value );
  1367. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL3_MAX)", PROPERTY_CSS_SEARCH_SKILL3_MAX ), value );
  1368. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL3_MIN, value );
  1369. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL3_MIN)", PROPERTY_CSS_SEARCH_SKILL3_MIN ), value );
  1370. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL4_MAX, value );
  1371. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL4_MAX)", PROPERTY_CSS_SEARCH_SKILL4_MAX ), value );
  1372. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_SEARCH_SKILL4_MIN, value );
  1373. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_SEARCH_SKILL4_MIN)", PROPERTY_CSS_SEARCH_SKILL4_MIN ), value );
  1374. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_GAME_MODE_AS_NUMBER, value );
  1375. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_GAME_MODE_AS_NUMBER)", PROPERTY_CSS_GAME_MODE_AS_NUMBER ), value );
  1376. pXboxSystem->UserGetPropertyInt( userIndex, PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, value );
  1377. pkv->SetInt( CFmtStr( "Properties/%d\t\t(PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS)", PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS ), value );
  1378. KeyValuesDumpAsDevMsg( pkv );
  1379. }
  1380. }
  1381. static ConCommand mm_dumpcontextsandproperties("mm_dumpcontextsandproperties", MM_dumpcontextsandproperties, "Dump the current values for all of the title's contexts and properties.", FCVAR_DEVELOPMENTONLY );
  1382. #endif // _X360