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.

330 lines
12 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_title_richpresence.h"
  7. #include "mm_title_contextvalues.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. static void SetAllUsersContext( DWORD dwContextId, DWORD dwValue, bool bAsync = true )
  11. {
  12. #ifdef _X360
  13. for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
  14. {
  15. if ( XBX_GetUserIsGuest( k ) )
  16. continue;
  17. int iCtrlr = XBX_GetUserId( k );
  18. //if ( dwContextId == X_CONTEXT_PRESENCE ) DevMsg( "Set presence to %d for user %d\n", dwValue, iCtrlr );
  19. if ( bAsync )
  20. XUserSetContextEx( iCtrlr, dwContextId, dwValue, MMX360_NewOverlappedDormant() );
  21. else
  22. XUserSetContext( iCtrlr, dwContextId, dwValue );
  23. }
  24. #endif
  25. }
  26. static void SetAllUsersProperty( DWORD dwPropertyId, DWORD cbValue, void const *pvValue )
  27. {
  28. #ifdef _X360
  29. for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
  30. {
  31. if ( XBX_GetUserIsGuest( k ) )
  32. continue;
  33. int iCtrlr = XBX_GetUserId( k );
  34. XUserSetPropertyEx( iCtrlr, dwPropertyId, cbValue, pvValue, MMX360_NewOverlappedDormant() );
  35. }
  36. #endif
  37. }
  38. KeyValues * MM_Title_RichPresence_PrepareForSessionCreate( KeyValues *pSettings )
  39. {
  40. SetAllUsersContext( X_CONTEXT_GAME_MODE, CONTEXT_GAME_MODE_CSS_GAME_MODE_MULTIPLAYER, false );
  41. // matchmaking version
  42. {
  43. static int val; // must be valid for the async call
  44. extern ConVar mm_title_debug_version;
  45. val = mm_title_debug_version.GetInt();
  46. SetAllUsersProperty( PROPERTY_CSS_MATCH_VERSION, sizeof( val ), &val );
  47. DevMsg( "PrepareForSessionCreate: matchmaking version %d\n", val );
  48. }
  49. return NULL;
  50. }
  51. void MM_Title_RichPresence_Update( KeyValues *pFullSettings, KeyValues *pUpdatedSettings )
  52. {
  53. #if !defined( _X360 ) && !defined( NO_STEAM ) && !defined( SWDS )
  54. ( void ) g_pMatchExtensions->GetIBaseClientDLL()->GetRichPresenceStatusString();
  55. #endif
  56. #ifdef _X360
  57. if ( !pFullSettings )
  58. {
  59. SetAllUsersContext( X_CONTEXT_PRESENCE, CONTEXT_PRESENCE_MAINMENU ); // main menu
  60. return;
  61. }
  62. // Also set players information during initial rich presence update
  63. if ( !pUpdatedSettings && pFullSettings )
  64. {
  65. MM_Title_RichPresence_PlayersChanged( pFullSettings );
  66. // Open slots
  67. int numSlots = pFullSettings->GetInt( "members/numSlots", XBX_GetNumGameUsers() );
  68. {
  69. static int val; // must be valid for the async call
  70. val = numSlots;
  71. SetAllUsersProperty( PROPERTY_CSS_OPEN_SLOTS, sizeof( val ), &val );
  72. }
  73. // Team slots
  74. int numTeamSlots = MAX( pFullSettings->GetInt( "members/numTSlotsFree", 0 ), pFullSettings->GetInt( "members/numCTSlotsFree", 0 ) );
  75. {
  76. static int val; // must be valid for the async call
  77. val = numTeamSlots;
  78. SetAllUsersProperty( PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, sizeof( val ), &val );
  79. }
  80. // Skill fields
  81. {
  82. static int val = 0; // must be valid for the async call
  83. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_EXPERIENCE, sizeof( val ), &val );
  84. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL0, sizeof( val ), &val );
  85. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL1, sizeof( val ), &val );
  86. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL2, sizeof( val ), &val );
  87. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL3, sizeof( val ), &val );
  88. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL4, sizeof( val ), &val );
  89. }
  90. // Listen/dedicated server resolver
  91. {
  92. static int val; // must be valid for the async call
  93. val = 0;
  94. extern ConVar mm_title_debug_dccheck;
  95. if ( mm_title_debug_dccheck.GetInt() )
  96. val = ( ( mm_title_debug_dccheck.GetInt() > 0 ) ? 0 : 1 );
  97. SetAllUsersProperty( PROPERTY_CSS_SEARCH_LISTEN_SERVER, sizeof( val ), &val );
  98. }
  99. }
  100. // pUpdatedSettings = NULL when the session is created and all contexts need to be set
  101. KeyValues *pNewSettings = pUpdatedSettings ? pUpdatedSettings : pFullSettings;
  102. // if ( KeyValues *kvVal = pNewSettings->FindKey( "game/dlcrequired" ) )
  103. // {
  104. // static int val[10]; // must be valid for the async call
  105. // uint64 uiDlcRequired = kvVal->GetUint64();
  106. // extern ConVar mm_matchmaking_dlcsquery;
  107. // for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
  108. // {
  109. // val[k] = !!( uiDlcRequired & ( 1ull << k ) );
  110. // DevMsg( "DLC%d required: %d\n", k, val[k] );
  111. // SetAllUsersProperty( PROPERTY_REQUIRED_DLC1 - 1 + k, sizeof( val ), &val );
  112. // }
  113. // }
  114. // Actual game type (classic, gungame)
  115. if ( char const *szGameType = pNewSettings->GetString( "game/type", NULL ) )
  116. {
  117. SetAllUsersContext( CONTEXT_CSS_GAME_TYPE, g_GameTypeContexts->ScanValues( szGameType ) );
  118. }
  119. // Game state
  120. if ( char const *szGameState = pNewSettings->GetString( "game/state", NULL ) )
  121. {
  122. if ( !V_stricmp( pFullSettings->GetString( "system/network" ), "offline" ) )
  123. SetAllUsersContext( CONTEXT_GAME_STATE, CONTEXT_GAME_STATE_SINGLE_PLAYER );
  124. else
  125. SetAllUsersContext( CONTEXT_GAME_STATE, ( !V_stricmp( "game", szGameState ) ) ? CONTEXT_GAME_STATE_MULTIPLAYER : CONTEXT_GAME_STATE_IN_MENUS );
  126. }
  127. // Actual game mode (casual, competitive, pro, etc)
  128. if ( char const *szValue = pNewSettings->GetString( "game/mode", NULL ) )
  129. {
  130. SetAllUsersContext( CONTEXT_CSS_GAME_MODE, g_GameModeContexts->ScanValues( szValue ) );
  131. static int val; // must be valid for the async call
  132. val = g_GameModeAsNumberContexts->ScanValues( szValue );
  133. SetAllUsersProperty( PROPERTY_CSS_GAME_MODE_AS_NUMBER, sizeof( val ), &val );
  134. }
  135. // MapGroup being used
  136. if ( char const *szMapGroupName = pNewSettings->GetString( "game/mapgroupname", NULL ) )
  137. {
  138. SetAllUsersContext( CONTEXT_CSS_MAP_GROUP, g_MapGroupContexts->ScanValues( szMapGroupName ) );
  139. }
  140. // Privacy
  141. if ( char const *szPrivacy = pNewSettings->GetString( "system/access", NULL ) )
  142. {
  143. SetAllUsersContext( CONTEXT_CSS_PRIVACY, g_PrivacyContexts->ScanValues( szPrivacy ) );
  144. }
  145. // Listen server
  146. if ( char const *szListenServer = pNewSettings->GetString( "server/server", NULL ) )
  147. {
  148. static int val; // must be valid for the async call
  149. val = ( !V_stricmp( "listen", szListenServer ) ? 1 : 0 );
  150. extern ConVar mm_title_debug_dccheck;
  151. if ( mm_title_debug_dccheck.GetInt() )
  152. val = ( ( mm_title_debug_dccheck.GetInt() > 0 ) ? 0 : 1 );
  153. SetAllUsersProperty( PROPERTY_CSS_SEARCH_LISTEN_SERVER, sizeof( val ), &val );
  154. }
  155. //
  156. // Determine Rich Presence Display
  157. //
  158. if ( char const *szGameModeForRichPresence = pFullSettings->GetString( "game/mode", NULL ) )
  159. {
  160. // Online/Offline
  161. if ( char const *szNetwork = pFullSettings->GetString( "system/network", NULL ) )
  162. {
  163. DWORD dwLevelPresence = CONTEXT_PRESENCE_MAINMENU;
  164. if ( V_stricmp( "offline", szNetwork ) == 0 )
  165. {
  166. dwLevelPresence = CONTEXT_PRESENCE_SINGLEPLAYER;
  167. }
  168. else if ( !V_stricmp( "lobby", pFullSettings->GetString( "game/state" ) ) )
  169. {
  170. dwLevelPresence = CONTEXT_PRESENCE_LOBBY;
  171. }
  172. else
  173. {
  174. // Privacy
  175. if ( char const *szPrivacy = pFullSettings->GetString( "system/access", NULL ) )
  176. {
  177. DWORD dwPrivacy = g_PrivacyContexts->ScanValues( szPrivacy );
  178. if ( dwPrivacy == CONTEXT_CSS_PRIVACY_PUBLIC )
  179. {
  180. // Public match
  181. // See if there are any free slots
  182. int numSlots = pFullSettings->GetInt( "members/numSlots", 0 );
  183. int numPlayers = pFullSettings->GetInt( "members/numPlayers", 0 );
  184. dwLevelPresence = (numSlots > numPlayers) ? CONTEXT_PRESENCE_MULTIPLAYER : CONTEXT_PRESENCE_MULTIPLAYER_NO_SLOTS;
  185. }
  186. else
  187. {
  188. // Private/invite only match
  189. dwLevelPresence = CONTEXT_PRESENCE_MULTIPLAYER_PRIVATE;
  190. }
  191. }
  192. }
  193. SetAllUsersContext( X_CONTEXT_PRESENCE, dwLevelPresence );
  194. }
  195. // Update the map being used
  196. DWORD dwMapRichPresence = pFullSettings->GetInt( "game/mapRichPresence", 0xFFFF );
  197. if ( dwMapRichPresence == 0xFFFF )
  198. {
  199. // We didn't have a richpresence context set in GameModes.txt so look it up based on the name
  200. if ( char const *szMapName = pFullSettings->GetString( "game/map", NULL ) )
  201. {
  202. dwMapRichPresence = g_LevelContexts->ScanValues( szMapName );
  203. }
  204. }
  205. if ( dwMapRichPresence != 0xFFFF )
  206. {
  207. SetAllUsersContext( CONTEXT_CSS_LEVEL, dwMapRichPresence );
  208. }
  209. }
  210. #endif // _X360
  211. }
  212. void MM_Title_RichPresence_PlayersChanged( KeyValues *pFullSettings )
  213. {
  214. //#ifdef _X360
  215. // // Set the installed DLCs masks
  216. // static int val[10]; // must be valid for the async call
  217. // uint64 uiDlcInstalled = g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" );
  218. // extern ConVar mm_matchmaking_dlcsquery;
  219. // for ( int k = 1; k <= mm_matchmaking_dlcsquery.GetInt(); ++ k )
  220. // {
  221. // val[k] = !!( uiDlcInstalled & ( 1ull << k ) );
  222. // DevMsg( "DLC%d installed: %d\n", k, val[k] );
  223. // SetAllUsersProperty( PROPERTY_INSTALLED_DLC1 - 1 + k, sizeof( val[k] ), &val[k] );
  224. // }
  225. //#endif
  226. }
  227. // Called by the client to notify matchmaking that it should update matchmaking properties based
  228. // on player distribution among the teams.
  229. void MM_Title_RichPresence_UpdateTeamPropertiesCSGO( KeyValues *pCurrentSettings, KeyValues *pTeamProperties )
  230. {
  231. #ifdef _X360
  232. int numSlots = pCurrentSettings->GetInt( "members/numSlots", 0 );
  233. int numPlayers = pTeamProperties->GetInt( "members/numPlayers", 0 );
  234. int numSpectators = pTeamProperties->GetInt( "members/numSpectators", 0 );
  235. int numExtraSpectatorSlots = pCurrentSettings->GetInt( "members/numExtraSpectatorSlots", 0 );
  236. int numFreeTSlots = pCurrentSettings->GetInt( "members/numTSlotsFree", 0 );
  237. int numFreeCTSlots = pCurrentSettings->GetInt( "members/numCTSlotsFree", 0 );
  238. // Spectator overflow is computed in case we end up in a situation in which there are more
  239. // spectators than spectator slots. In that case, the extra spectators are counted against
  240. // the active player slots.
  241. int spectatorOverflow = numSpectators - numExtraSpectatorSlots;
  242. static int nPROPERTY_CSS_OPEN_SLOTS;
  243. nPROPERTY_CSS_OPEN_SLOTS = numSlots - numPlayers;
  244. if ( spectatorOverflow > 0 )
  245. {
  246. nPROPERTY_CSS_OPEN_SLOTS = numSlots - ( numPlayers - numExtraSpectatorSlots + spectatorOverflow );
  247. }
  248. SetAllUsersProperty( PROPERTY_CSS_OPEN_SLOTS, sizeof( nPROPERTY_CSS_OPEN_SLOTS ), &nPROPERTY_CSS_OPEN_SLOTS );
  249. // post the average skill rank so matchmaking will work
  250. int avgRank = pTeamProperties->GetInt( "members/timeout", DEFAULT_NEW_PLAYER_ELO_RANK );
  251. static int nPROPERTY_CSS_AGGREGATE_SKILL0;
  252. nPROPERTY_CSS_AGGREGATE_SKILL0 = avgRank;
  253. SetAllUsersProperty( PROPERTY_CSS_AGGREGATE_SKILL0, sizeof( nPROPERTY_CSS_AGGREGATE_SKILL0 ), &nPROPERTY_CSS_AGGREGATE_SKILL0 );
  254. // Post the maximum number of free slots on either team, for team matchmaking only
  255. static int nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS;
  256. nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS = max( numFreeTSlots, numFreeCTSlots );
  257. SetAllUsersProperty( PROPERTY_CSS_MAX_OPEN_TEAM_SLOTS, sizeof( nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS ), &nPROPERTY_CSS_MAX_OPEN_TEAM_SLOTS );
  258. #elif defined( _PS3 )
  259. // This is hacky: we stuff the rank into system data so that if lobby migrates
  260. // to a new owner and the metadata wasn't correctly set by the previous owner
  261. // then the new owner will set it
  262. if ( IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession() )
  263. {
  264. KeyValues *kvSystemData = pMatchSession->GetSessionSystemData();
  265. if ( !kvSystemData )
  266. return;
  267. int avgRank = pTeamProperties->GetInt( "members/timeout", DEFAULT_NEW_PLAYER_ELO_RANK );
  268. kvSystemData->SetInt( "timeout", avgRank );
  269. int numOpenSlots = 10 - pTeamProperties->GetInt( "members/numPlayers" );
  270. if ( numOpenSlots < 0 )
  271. numOpenSlots = 0;
  272. kvSystemData->SetInt( "numOpenSlots", numOpenSlots );
  273. char const *szSessionType = kvSystemData->GetString( "type", NULL );
  274. DevMsg( "Session timeout value=%d (%s)\n", avgRank, szSessionType ? szSessionType : "offline" );
  275. DevMsg( "Session numOpenSlots=%d (%s)\n", numOpenSlots, szSessionType ? szSessionType : "offline" );
  276. if ( szSessionType && !Q_stricmp( szSessionType, "client" ) )
  277. return; // don't run on clients for now, maybe later when we become owner
  278. steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:timeout", CFmtStr( "%u", avgRank ) );
  279. steamapicontext->SteamMatchmaking()->SetLobbyData( kvSystemData->GetUint64( "xuidReserve", 0ull ), "game:numOpenSlots", CFmtStr( "%u", numOpenSlots ) );
  280. }
  281. #endif
  282. }