summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_matchmaking_shared.h
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_matchmaking_shared.h')
-rw-r--r--game/shared/tf/tf_matchmaking_shared.h351
1 files changed, 351 insertions, 0 deletions
diff --git a/game/shared/tf/tf_matchmaking_shared.h b/game/shared/tf/tf_matchmaking_shared.h
new file mode 100644
index 0000000..75394a6
--- /dev/null
+++ b/game/shared/tf/tf_matchmaking_shared.h
@@ -0,0 +1,351 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Matchmaking stuff shared between GC and gameserver / client
+//
+//=============================================================================
+
+#ifndef TF_MATCHMAKING_SHARED_H
+#define TF_MATCHMAKING_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gcmessages.pb.h"
+
+class IMatchGroupDescription;
+
+#define NEXT_MAP_VOTE_OPTIONS 3
+
+// Replace this hard-coded value concept in order to support all bracket types (i.e. anything higher than 6v6, etc)
+// This increases a number of hard-coded data structures and so on, so beware
+#define MATCH_SIZE_MAX 24
+
+// Similarly, some hot path MM structures use this for fixed arrays. It should be the maximum number of players that
+// will ever be in a party and no larger.
+#define MAX_PARTY_SIZE 6
+
+// Range clients are allowed to pass up for custom ping tolerance
+// Currently matches CS:GO
+#define CUSTOM_PING_TOLERANCE_MIN 25
+#define CUSTOM_PING_TOLERANCE_MAX 350
+
+// XXX(JohnS): Before we can actually use other rating backends for matchmaking or display purposes, there are remaining
+// hard coded assumptions about where the primary rating is, and issues with e.g. Match_Result assuming the
+// backend in use *now* is what the match was created for, etc. I've sprinkled this around at all the
+// landmines I found while implementing the new rating backend, so... comment this out and fix everything
+// that breaks.
+static inline void FixmeMMRatingBackendSwapping() {}
+
+// Backend-agnostic storage type for rating data, so we're not manually passing pairs around to every rating-specific
+// interface when we inevitably decide to add a third.
+//
+// Keep in mind that it is much easier to query well structured data for reports & otherwise, so we should prefer
+// e.g. adding another value to packing two 16-bit ints in RatingSecondary for a new backend that needs more 16-bit
+// values. (The calculus may change if we end up with a backend that has eight 8-bit values, however)
+//
+// Schema objects that embed rating data: RatingHistory, RatingData
+// Proto objects that embed rating data: CSOTFRatingData
+struct MMRatingData_t {
+ uint32_t unRatingPrimary;
+ uint32_t unRatingSecondary;
+ uint32_t unRatingTertiary;
+
+ inline bool operator==(const MMRatingData_t &b) const
+ { return this->unRatingPrimary == b.unRatingPrimary &&
+ this->unRatingSecondary == b.unRatingSecondary &&
+ this->unRatingTertiary == b.unRatingTertiary; }
+};
+
+// Stored value, don't re-order
+enum EMatchGroup
+{
+ k_nMatchGroup_Invalid = -1,
+ k_nMatchGroup_First = 0,
+
+ k_nMatchGroup_MvM_Practice = 0,
+ k_nMatchGroup_MvM_MannUp,
+
+ k_nMatchGroup_Ladder_6v6,
+ k_nMatchGroup_Ladder_9v9,
+ k_nMatchGroup_Ladder_12v12,
+
+ k_nMatchGroup_Casual_6v6,
+ k_nMatchGroup_Casual_9v9,
+ k_nMatchGroup_Casual_12v12,
+
+ k_nMatchGroup_Count,
+ // When adding a new matchgroup, add case handling to GetMatchSizeForMatchGroup(), GetMatchGroupName(), GetServerPoolName(), GetMaxLobbySizeForMatchGroup(), YldWebAPIServersByDataCenter()
+};
+
+// Stored value, don't re-order
+//
+// If you add a new backend, see ITFMMRatingBackend::GetRatingBackend -- you need to at least provide a GetDefault()
+enum EMMRating
+{
+ k_nMMRating_LowestValue = -1,
+ k_nMMRating_Invalid = -1,
+
+ k_nMMRating_First = 0,
+ k_nMMRating_6v6_DRILLO = 0,
+ k_nMMRating_6v6_DRILLO_PlayerAcknowledged = 1,
+ k_nMMRating_6v6_GLICKO = 2,
+ k_nMMRating_12v12_DRILLO = 3,
+ k_nMMRating_12v12_GLICKO = 4,
+ k_nMMRating_Last = 4,
+};
+
+// This must be in the range of an int16 for database serialization
+COMPILE_TIME_ASSERT( k_nMMRating_LowestValue >= INT16_MIN );
+COMPILE_TIME_ASSERT( k_nMMRating_Last <= INT16_MAX );
+
+// Stored value, don't re-order
+enum EMMRatingSource
+{
+ k_nMMRatingSource_LowestValue = -1,
+ k_nMMRatingSource_Invalid = -1,
+
+ k_nMMRatingSource_Match = 0, // Match result. Source ID is match ID
+ k_nMMRatingSource_Admin = 1, // Admin command/manual adjustment. Source ID is probably 0 or something.
+ k_nMMRatingSource_PlayerAcknowledge = 2, // For 'acknowledge' type ratings, the player acknowledged the value
+ k_nMMRatingSource_ImportedOldSystem = 3, // For pre-history ratings, this source is used once for 'initial rating from old system'
+ k_nMMRatingSource_Last = 3,
+};
+
+// This must be in the range of an int16 for database serialization
+COMPILE_TIME_ASSERT( k_nMMRatingSource_LowestValue >= INT16_MIN );
+COMPILE_TIME_ASSERT( k_nMMRatingSource_Last <= INT16_MAX );
+
+// Also update this guy if you do the thing
+const char *GetMatchGroupName( EMatchGroup eMatchGroup );
+
+// Probably a better place for this...
+enum ELadderLeaderboardTypes
+{
+ LADDER_LEADERBOARDS_6V6 = 0,
+ LADDER_LEADERBOARDS_PUBLIC,
+ LADDER_LEADERBOARDS_9V9,
+ LADDER_LEADERBOARDS_12V12,
+ LADDER_LEADERBOARDS_MAX
+};
+
+// Late join modes
+enum EMatchMode
+{
+ // Uninitialized/unknown
+ eMatchMode_Invalid,
+ // Not late join / don't use late join
+ eMatchMode_MatchMaker_CompleteFromQueue,
+ // The add-one-player-at-a-time mode that doesn't work with the new scoring system, but still used for MvM and other
+ // old-scoring-system stuff.
+ eMatchMode_MatchMaker_LateJoinDropIn,
+ // The new late join mode that re-evaulates complete matches with the missing spot(s) filled.
+ eMatchMode_MatchMaker_LateJoinMatchBased,
+ // A match that is being manually crafted
+ eMatchMode_Manual,
+};
+
+const EMatchGroup k_nMatchGroup_Ladder_First = k_nMatchGroup_Ladder_6v6;
+const EMatchGroup k_nMatchGroup_Ladder_Last = k_nMatchGroup_Ladder_12v12;
+
+const EMatchGroup k_nMatchGroup_Casual_First = k_nMatchGroup_Casual_6v6;
+const EMatchGroup k_nMatchGroup_Casual_Last = k_nMatchGroup_Casual_12v12;
+
+inline bool IsMvMMatchGroup( EMatchGroup eMatchGroup )
+{
+ return ( eMatchGroup == k_nMatchGroup_MvM_Practice ) || ( eMatchGroup == k_nMatchGroup_MvM_MannUp );
+}
+
+inline bool IsLadderGroup( EMatchGroup eMatchGroup )
+{
+ return ( eMatchGroup >= k_nMatchGroup_Ladder_First && eMatchGroup <= k_nMatchGroup_Ladder_Last )
+ || ( eMatchGroup >= k_nMatchGroup_Casual_First && eMatchGroup <= k_nMatchGroup_Casual_Last );
+}
+
+inline bool IsCasualGroup( EMatchGroup eMatchGroup )
+{
+ return ( eMatchGroup >= k_nMatchGroup_Casual_First ) && ( eMatchGroup <= k_nMatchGroup_Casual_Last );
+}
+
+inline bool IsMannUpGroup( EMatchGroup eMatchGroup )
+{
+ switch ( eMatchGroup )
+ {
+ case k_nMatchGroup_MvM_Practice:
+ return false;
+ case k_nMatchGroup_MvM_MannUp:
+ return true;
+ case k_nMatchGroup_Ladder_6v6:
+ case k_nMatchGroup_Ladder_9v9:
+ case k_nMatchGroup_Ladder_12v12:
+ return false;
+ case k_nMatchGroup_Invalid:
+ default:
+ Assert( !"IsMannUpGroup called with invalid match group" );
+ return false;
+ }
+
+ return false;
+}
+
+enum EMMServerMode
+{
+ eMMServerMode_Idle,
+ eMMServerMode_Incomplete_Match,
+ eMMServerMode_Full,
+ eMMServerMode_Count
+};
+
+// Separate penalty pools (and rules) for different classes of modes
+enum EMMPenaltyPool
+{
+ eMMPenaltyPool_Invalid,
+ eMMPenaltyPool_Casual, // Pool with lenient penalties for most casual/mainstream gamemodes
+ eMMPenaltyPool_Ranked // Pool with strict and cumulative penalties for ranked gamemodes where abandons tank matches
+};
+
+enum
+{
+ // !! This should match up with GetServerPoolIndex to map these between match groups and server pools
+ // eMMServerMode_NotParticipating
+ k_nGameServerPool_NotParticipating = -1,
+
+ // eMMServerMode_Incomplete_Match
+ k_nGameServerPool_MvM_Practice_Incomplete_Match = 0,
+ k_nGameServerPool_MvM_MannUp_Incomplete_Match,
+ k_nGameServerPool_Ladder_6v6_Incomplete_Match,
+ k_nGameServerPool_Ladder_9v9_Incomplete_Match,
+ k_nGameServerPool_Ladder_12v12_Incomplete_Match,
+ k_nGameServerPool_Casual_6v6_Incomplete_Match,
+ k_nGameServerPool_Casual_9v9_Incomplete_Match,
+ k_nGameServerPool_Casual_12v12_Incomplete_Match,
+
+ // eMMServerMode_Full
+ k_nGameServerPool_MvM_Practice_Full,
+ k_nGameServerPool_MvM_MannUp_Full,
+ k_nGameServerPool_Ladder_6v6_Full,
+ k_nGameServerPool_Ladder_9v9_Full,
+ k_nGameServerPool_Ladder_12v12_Full,
+ k_nGameServerPool_Casual_6v6_Full,
+ k_nGameServerPool_Casual_9v9_Full,
+ k_nGameServerPool_Casual_12v12_Full,
+
+ // eMMServerMode_Idle
+ k_nGameServerPool_Idle,
+ // When adding a new matchgroup, add case handling to GetMatchSizeForMatchGroup(), GetMatchGroupName(), GetServerPoolName(), GetMaxLobbySizeForMatchGroup(), YldWebAPIServersByDataCenter()
+
+ k_nGameServerPoolCountTotal,
+};
+
+// Also update this guy if you touch pools
+const char *GetServerPoolName( int iServerPool );
+
+const int k_nGameServerPool_Incomplete_Match_First = k_nGameServerPool_MvM_Practice_Incomplete_Match;
+const int k_nGameServerPool_Incomplete_Match_Last = k_nGameServerPool_Casual_12v12_Incomplete_Match;
+const int k_nGameServerPool_Full_First = k_nGameServerPool_MvM_Practice_Full;
+const int k_nGameServerPool_Full_Last = k_nGameServerPool_Casual_12v12_Full;
+
+COMPILE_TIME_ASSERT( k_nGameServerPool_Incomplete_Match_First + k_nMatchGroup_Count - 1 == k_nGameServerPool_Incomplete_Match_Last );
+COMPILE_TIME_ASSERT( k_nGameServerPool_Full_First + k_nMatchGroup_Count - 1 == k_nGameServerPool_Full_Last );
+
+inline bool IsIncompleteMatchPool( int nGameServerPool )
+{
+ return nGameServerPool >= k_nGameServerPool_Incomplete_Match_First && nGameServerPool <= k_nGameServerPool_Incomplete_Match_Last;
+}
+
+// Stuff is simpler if we can set a max number of challenges in the schema
+#define MAX_MVM_CHALLENGES 64
+
+// Store a set of MvM challenges (search criteria, etc)
+class CMvMMissionSet
+{
+public:
+ CMvMMissionSet();
+ CMvMMissionSet( const CMvMMissionSet &x );
+ ~CMvMMissionSet();
+ void operator=( const CMvMMissionSet &x );
+ bool operator==( const CMvMMissionSet &x ) const;
+
+ /// Set to the empty set
+ void Clear();
+
+ /// get/set individual bits, based on index of the challenge in the schema
+ void SetMissionBySchemaIndex( int iChallengeSchemaIndex, bool flag );
+ bool GetMissionBySchemaIndex( int iChallengeSchemaIndex ) const;
+
+ /// Intersect this set with the other set. Use IsEmpty()
+ /// to see if this produced the empty set.
+ void Intersect( const CMvMMissionSet &x );
+
+ /// Return true if the two sets have a nonzero intersection. (Neither object is modified)
+ bool HasIntersection( const CMvMMissionSet &x ) const;
+
+ /// Return true if any challenges are selected
+ bool IsEmpty() const;
+private:
+
+ COMPILE_TIME_ASSERT( MAX_MVM_CHALLENGES <= 64 );
+
+ // Just use a plain old uint64 for now. We can make this into a proper bitfield class at some point
+ uint64 m_bits;
+};
+
+// Player Skill Ratings
+const int k_nDrilloRating_MinRatingAdjust = 1;
+const int k_nDrilloRating_MaxRatingAdjust = 100;
+const int k_nDrilloRating_Ladder_MaxRatingAdjust = 500;
+const int k_nDrilloRating_Ladder_MaxLossAdjust_LowRank = 100;
+const uint32 k_unDrilloRating_MaxDifference = 25000;
+const uint32 k_unDrilloRating_Min = 1;
+const uint32 k_unDrilloRating_Ladder_Min = 10000;
+const uint32 k_unDrilloRating_Max = 50000;
+const uint32 k_unDrilloRating_Avg = 20000;
+const uint32 k_unDrilloRating_Ladder_Start = 10000;
+const uint32 k_unDrilloRating_Ladder_LowSkill = 19500; // First 6 ranks ceiling
+const uint32 k_unDrilloRating_Ladder_HighSkill = 33001; // Last 6 ranks floor
+
+struct MapDef_t;
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapper class to make dealing with CMsgCasualMatchmakingSearchCriteria
+// much easier.
+//-----------------------------------------------------------------------------
+class CCasualCriteriaHelper
+{
+public:
+ CCasualCriteriaHelper( const CMsgCasualMatchmakingSearchCriteria& criteria );
+
+ bool IsMapSelected( const MapDef_t* pMapDef ) const;
+ bool IsMapSelected( const uint32 nMapDefIndex ) const;
+ bool IsValid() const;
+ bool AnySelected() const { return !m_mapsBits.IsAllClear(); }
+ CMsgCasualMatchmakingSearchCriteria GetCasualCriteria() const;
+
+ void Intersect( const CMsgCasualMatchmakingSearchCriteria& otherCriteria );
+ bool SetMapSelected( uint32 nMapDefIndex, bool bSelected );
+
+ void Clear( void );
+
+private:
+ bool IsMapInValidCategory( uint32 nMapDefIndex ) const;
+
+private:
+ CLargeVarBitVec m_mapsBits;
+};
+
+// CSOTFLobby flags
+#define LOBBY_FLAG_LOWPRIORITY ( 1 << 0 )
+#define LOBBY_FLAG_REMATCH ( 1 << 1 )
+
+// CMsgGC_Match_Result match flags
+#define MATCH_FLAG_LOWPRIORITY ( 1 << 0 )
+#define MATCH_FLAG_REMATCH ( 1 << 1 )
+
+// CMsgGC_Match_Result player flags
+#define MATCH_FLAG_PLAYER_LEAVER ( 1 << 0 )
+#define MATCH_FLAG_PLAYER_LATEJOIN ( 1 << 1 )
+// Separate from LEAVER - was marked as an abandon and issued a penalty. You can be a leaver without being an
+// abandoner.
+#define MATCH_FLAG_PLAYER_ABANDONER ( 1 << 2 )
+#define MATCH_FLAG_PLAYER_PLAYED ( 1 << 3 ) // Did they stay long enough for the game to start?
+
+#endif // #ifndef TF_MATCHMAKING_SHARED_H