Team Fortress 2 Source Code as on 22/4/2020
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
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Matchmaking stuff shared between GC and gameserver / client
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_matchmaking_shared.h"
  8. #include "tf_match_description.h"
  9. #include "tf_ladder_data.h"
  10. #ifdef GC_DLL
  11. #include "tf_lobbymanager.h"
  12. #include "tf_partymanager.h"
  13. #include "tf_party.h"
  14. #endif
  15. #ifdef CLIENT_DLL
  16. #include "tf_gc_client.h"
  17. #include "tf_gamerules.h"
  18. #endif
  19. #ifdef GAME_DLL
  20. #include "tf_gc_server.h"
  21. #include "tf_gamerules.h"
  22. #include "tf_party.h"
  23. #endif
  24. const char *s_pszMatchGroups[] =
  25. {
  26. "MatchGroup_MvM_Practice",
  27. "MatchGroup_MvM_MannUp",
  28. "MatchGroup_Ladder_6v6",
  29. "MatchGroup_Ladder_9v9",
  30. "MatchGroup_Ladder_12v12",
  31. "MatchGroup_Casual_6v6",
  32. "MatchGroup_Casual_9v9",
  33. "MatchGroup_Casual_12v12",
  34. };
  35. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszMatchGroups ) == k_nMatchGroup_Count );
  36. #ifdef GC_DLL
  37. void On6v6MatchSizeChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
  38. {
  39. if ( !GGCBase()->BIsInLogonSurge() && !GGCTF()->GetIsShuttingDown() && TFLobbyManager()->GetMatchmaker() )
  40. {
  41. TFLobbyManager()->GetMatchmaker()->timeRemove6v6LadderGroupsExpire = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), 10.f, k_ETimeUnitSecond );
  42. }
  43. }
  44. #endif
  45. #if !defined GC_DLL
  46. #define GCConVar ConVar
  47. #define FCVAR_MATCHSIZE_THING ( FCVAR_REPLICATED )
  48. #else
  49. #define FCVAR_MATCHSIZE_THING 0
  50. #endif
  51. GCConVar tf_mm_match_size_mvm( "tf_mm_match_size_mvm", "6", FCVAR_MATCHSIZE_THING,
  52. "How many players in an MvM matchmade group?" );
  53. GCConVar tf_mm_match_size_ladder_6v6( "tf_mm_match_size_ladder_6v6", "12", FCVAR_MATCHSIZE_THING,
  54. "Number of players required to play a 6v6 ladder game.", true, 1, true, 12
  55. #ifdef GC_DLL
  56. , On6v6MatchSizeChanged
  57. #endif
  58. );
  59. GCConVar tf_mm_match_size_ladder_9v9( "tf_mm_match_size_ladder_9v9", "18", FCVAR_MATCHSIZE_THING,
  60. "Number of players required to play a 9v9 ladder game." );
  61. GCConVar tf_mm_match_size_ladder_12v12( "tf_mm_match_size_ladder_12v12", "24", FCVAR_MATCHSIZE_THING,
  62. "Number of players required to play a 12v12 ladder game." );
  63. GCConVar tf_mm_match_size_ladder_12v12_minimum( "tf_mm_match_size_ladder_12v12_minimum", "12", FCVAR_MATCHSIZE_THING,
  64. "Specifies the minimum number of players needed to launch a 12v12 match. Set to -1 to disable." );
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Init internal bitvec with ints from the protobuf message
  67. //-----------------------------------------------------------------------------
  68. CCasualCriteriaHelper::CCasualCriteriaHelper( const CMsgCasualMatchmakingSearchCriteria& criteria )
  69. {
  70. m_mapsBits.Resize( GetItemSchema()->GetMasterMapsList().Count(), true );
  71. Assert( m_mapsBits.GetNumDWords() >= criteria.selected_maps_bits_size() );
  72. for( int i=0; i < criteria.selected_maps_bits_size() && i < m_mapsBits.GetNumDWords(); ++i )
  73. {
  74. m_mapsBits.SetDWord( i , criteria.selected_maps_bits( i ) );
  75. }
  76. // validate all of the bits to make sure the maps are in valid categories
  77. int nNumBits = m_mapsBits.GetNumBits();
  78. for( int i=0; i < nNumBits; ++i )
  79. {
  80. if ( m_mapsBits.IsBitSet( i ) == false )
  81. continue;
  82. if ( !IsMapInValidCategory( i ) )
  83. {
  84. const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( i );
  85. if ( pMap )
  86. {
  87. DevMsg( "CCasualCriteriaHelper: Map %s is selected, but not in any valid game modes!\n", pMap->pszMapName );
  88. }
  89. SetMapSelected( i, false );
  90. }
  91. }
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. //-----------------------------------------------------------------------------
  96. bool CCasualCriteriaHelper::IsMapSelected( const MapDef_t* pMapDef ) const
  97. {
  98. if ( !pMapDef )
  99. return false;
  100. return IsMapSelected( pMapDef->m_nDefIndex );
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Check if bit is selected
  104. //-----------------------------------------------------------------------------
  105. bool CCasualCriteriaHelper::IsMapSelected( const uint32 nMapDefIndex ) const
  106. {
  107. return m_mapsBits.IsBitSet( nMapDefIndex );
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose:
  111. //-----------------------------------------------------------------------------
  112. bool CCasualCriteriaHelper::IsMapInValidCategory( uint32 nMapDefIndex ) const
  113. {
  114. Assert( (int)nMapDefIndex < m_mapsBits.GetNumBits() );
  115. const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( nMapDefIndex );
  116. if ( !pMap || pMap->m_vecAssociatedGameCategories.Count() == 0 )
  117. {
  118. return false;
  119. }
  120. // Make sure this map is in at least one category that's in a casual matchmaking group
  121. FOR_EACH_VEC( pMap->m_vecAssociatedGameCategories, j )
  122. {
  123. const SchemaGameCategory_t* pCategory = GetItemSchema()->GetGameCategory( pMap->m_vecAssociatedGameCategories[j] );
  124. const SchemaMMGroup_t* pMMGroup = pCategory->m_pMMGroup;
  125. // Need to have active maps in a match making group
  126. if ( !pMMGroup || ( pCategory->m_vecEnabledMaps.Count() == 0 ) || ( !pCategory->PassesRestrictions() ) )
  127. {
  128. continue;
  129. }
  130. // and the matchmaking group needs to be Special Events, Core or Alternative (not Comp)
  131. if ( pMMGroup->m_eMMGroup == kMatchmakingType_SpecialEvents || pMMGroup->m_eMMGroup == kMatchmakingType_Core || pMMGroup->m_eMMGroup == kMatchmakingType_Alternative )
  132. {
  133. return true;
  134. }
  135. }
  136. return false;
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose: Check if this criteria is well formed
  140. //-----------------------------------------------------------------------------
  141. bool CCasualCriteriaHelper::IsValid() const
  142. {
  143. bool bValidMapSeen = false;
  144. int nNumBits = m_mapsBits.GetNumBits();
  145. for( int i=0; i < nNumBits; ++i )
  146. {
  147. if ( m_mapsBits.IsBitSet( i ) == false )
  148. continue;
  149. if ( IsMapInValidCategory( i ) )
  150. {
  151. bValidMapSeen = true;
  152. }
  153. }
  154. return bValidMapSeen;
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Turn helper back into protobuf message
  158. //-----------------------------------------------------------------------------
  159. CMsgCasualMatchmakingSearchCriteria CCasualCriteriaHelper::GetCasualCriteria() const
  160. {
  161. CMsgCasualMatchmakingSearchCriteria outCriteria;
  162. for( int i=0; i < m_mapsBits.GetNumDWords(); ++i )
  163. {
  164. outCriteria.add_selected_maps_bits( m_mapsBits.GetDWord( i ) );
  165. }
  166. return outCriteria;
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose: Intersection of this criteria and another
  170. //-----------------------------------------------------------------------------
  171. void CCasualCriteriaHelper::Intersect( const CMsgCasualMatchmakingSearchCriteria& otherCriteria )
  172. {
  173. CCasualCriteriaHelper otherHelper( otherCriteria );
  174. m_mapsBits.And( otherHelper.m_mapsBits, &m_mapsBits );
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: Flip a specific map bit
  178. //-----------------------------------------------------------------------------
  179. bool CCasualCriteriaHelper::SetMapSelected( uint32 nMapDefIndex, bool bSelected )
  180. {
  181. Assert( (int)nMapDefIndex < m_mapsBits.GetNumBits() );
  182. if ( bSelected && !IsMapInValidCategory( nMapDefIndex ) )
  183. {
  184. const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( nMapDefIndex );
  185. if ( pMap )
  186. {
  187. DevMsg( "CCasualCriteriaHelper: Attempting to set map %s as selected, but not in any valid game modes!\n", pMap->pszMapName );
  188. }
  189. return false;
  190. }
  191. m_mapsBits.Set( (int)nMapDefIndex, bSelected );
  192. return true;
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose: Sets all bits to zero
  196. //-----------------------------------------------------------------------------
  197. void CCasualCriteriaHelper::Clear( void )
  198. {
  199. m_mapsBits.ClearAll();
  200. }
  201. //
  202. // MvM Missions
  203. //
  204. CMvMMissionSet::CMvMMissionSet() { Clear(); }
  205. CMvMMissionSet::CMvMMissionSet( const CMvMMissionSet &x ) { m_bits = x.m_bits; }
  206. CMvMMissionSet::~CMvMMissionSet() {}
  207. void CMvMMissionSet::operator=( const CMvMMissionSet &x ) { m_bits = x.m_bits; }
  208. void CMvMMissionSet::Clear() { m_bits = 0; }
  209. bool CMvMMissionSet::operator==( const CMvMMissionSet &x ) const { return m_bits == x.m_bits; }
  210. void CMvMMissionSet::SetMissionBySchemaIndex( int idxMission, bool flag )
  211. {
  212. Assert( idxMission >= 0 && idxMission < GetItemSchema()->GetMvmMissions().Count() );
  213. uint64 mask = ( (uint64)1 << (unsigned)idxMission );
  214. if ( flag )
  215. m_bits |= mask;
  216. else
  217. m_bits &= ~mask;
  218. }
  219. bool CMvMMissionSet::GetMissionBySchemaIndex( int idxMission ) const
  220. {
  221. // Bogus index?
  222. if ( idxMission == k_iMvmMissionIndex_NotInSchema )
  223. return false;
  224. if ( idxMission < 0 || idxMission >= GetItemSchema()->GetMvmMissions().Count() )
  225. {
  226. Assert( idxMission >= 0 );
  227. Assert( idxMission < GetItemSchema()->GetMvmMissions().Count() );
  228. return false;
  229. }
  230. // Check the bit
  231. uint64 mask = ( (uint64)1 << (unsigned)idxMission );
  232. return ( m_bits & mask ) != 0;
  233. }
  234. void CMvMMissionSet::Intersect( const CMvMMissionSet &x )
  235. {
  236. m_bits &= x.m_bits;
  237. }
  238. bool CMvMMissionSet::HasIntersection( const CMvMMissionSet &x ) const
  239. {
  240. return ( m_bits & x.m_bits ) != 0;
  241. }
  242. bool CMvMMissionSet::IsEmpty() const
  243. {
  244. return ( m_bits == 0 );
  245. }
  246. const char *GetMatchGroupName( EMatchGroup eMatchGroup )
  247. {
  248. switch ( eMatchGroup )
  249. {
  250. case k_nMatchGroup_Invalid: return "(Invalid)";
  251. case k_nMatchGroup_MvM_Practice: return "MvM Practice";
  252. case k_nMatchGroup_MvM_MannUp: return "MvM MannUp";
  253. case k_nMatchGroup_Ladder_6v6: return "6v6 Ladder Match";
  254. case k_nMatchGroup_Ladder_9v9: return "9v9 Ladder Match";
  255. case k_nMatchGroup_Ladder_12v12: return "12v12 Ladder Match";
  256. case k_nMatchGroup_Casual_6v6: return "6v6 Casual Match";
  257. case k_nMatchGroup_Casual_9v9: return "9v9 Casual Match";
  258. case k_nMatchGroup_Casual_12v12: return "12v12 Casual Match";
  259. }
  260. AssertMsg1( false, "Invalid match group %d", eMatchGroup );
  261. return "(Invalid match group)";
  262. }
  263. const char *GetServerPoolName( int iServerPool )
  264. {
  265. switch ( iServerPool )
  266. {
  267. case k_nGameServerPool_MvM_Practice_Incomplete_Match: return "MvM Boot Camp Active";
  268. case k_nGameServerPool_MvM_MannUp_Incomplete_Match: return "MvM MannUp Active";
  269. case k_nGameServerPool_Casual_6v6_Incomplete_Match: return "Casual 6v6 Active";
  270. case k_nGameServerPool_Casual_9v9_Incomplete_Match: return "Casual 9v9 Active";
  271. case k_nGameServerPool_Casual_12v12_Incomplete_Match: return "Casual 12v12 Active";
  272. case k_nGameServerPool_MvM_Practice_Full: return "MvM Boot Camp Full";
  273. case k_nGameServerPool_MvM_MannUp_Full: return "MvM MannUp Full";
  274. case k_nGameServerPool_Casual_6v6_Full: return "Casual 6v6 Full";
  275. case k_nGameServerPool_Casual_9v9_Full: return "Casual 9v9 Full";
  276. case k_nGameServerPool_Casual_12v12_Full: return "Casual 12v12 Full";
  277. }
  278. AssertMsg1( false, "Invalid server pool %d", iServerPool );
  279. return "(Invalid pool index)";
  280. }