summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_matchmaking_scoring.h
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_matchmaking_scoring.h
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_matchmaking_scoring.h')
-rw-r--r--game/shared/tf/tf_matchmaking_scoring.h205
1 files changed, 205 insertions, 0 deletions
diff --git a/game/shared/tf/tf_matchmaking_scoring.h b/game/shared/tf/tf_matchmaking_scoring.h
new file mode 100644
index 0000000..f637148
--- /dev/null
+++ b/game/shared/tf/tf_matchmaking_scoring.h
@@ -0,0 +1,205 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "tf_quickplay_shared.h"
+
+//
+// NOTE: This actually declares global variables and is intended to
+// only be included ONCE on the client, and ONCE on the GC.
+//
+
+#ifdef GC
+ #define TF2SCORECONVAR(name, defaultval, desc) GCConVar name(#name, defaultval, FCVAR_REPLICATED, desc)
+#else
+ #define TF2SCORECONVAR(name, defaultval, desc) ConVar name(#name, defaultval, FCVAR_NONE, desc)
+#endif
+
+TF2SCORECONVAR(tf_matchmaking_numbers_serverfull_headroom, "1", "Scoring will consider the server 'full' when this many slots are available" );
+TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_hrs_a, "8.00", "Valve server scoring bonus: hours played A" );
+TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_pts_a, "0.30", "Valve server scoring bonus: bonus points A" );
+TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_hrs_b, "16.00", "Valve server scoring bonus: hours played B" );
+TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_pts_b, "0.00", "Valve server scoring bonus: bonus points B" );
+TF2SCORECONVAR(tf_matchmaking_numbers_increase_maxplayers_penalty, "0.50", "Max scoring penalty to servers that have increased the max number of players" );
+TF2SCORECONVAR(tf_matchmaking_retry_cooldown_seconds, "300", "Time to remember quickplay join attempt, and apply scoring penalty to rejoin the same server" );
+TF2SCORECONVAR(tf_matchmaking_retry_max_penalty, "1.0", "Max scoring penalty to rejoin a server previously matched. (Decays linearly over the cooldown period)" );
+TF2SCORECONVAR(tf_matchmaking_noob_map_score_boost, "0.75", "Boost added for quick-plaay scoring purposes if you are a noob and the map is considered noob-friendly" );
+TF2SCORECONVAR(tf_matchmaking_noob_hours_played, "8.0", "Number of hours played to determine 'noob' status for quickplay scoring purposes" );
+
+TF2SCORECONVAR(tf_matchmaking_ping_a, "50.0f", "Quickplay scoring ping time data point A" );
+TF2SCORECONVAR(tf_matchmaking_ping_a_score, "0.9", "Quickplay scoring ping score data point A" );
+TF2SCORECONVAR(tf_matchmaking_ping_b, "150.0f", "Quickplay scoring ping time data point B" );
+TF2SCORECONVAR(tf_matchmaking_ping_b_score, "0.0", "Quickplay scoring ping score data point B" );
+TF2SCORECONVAR(tf_matchmaking_ping_c, "300.0f", "Quickplay scoring ping time data point C" );
+TF2SCORECONVAR(tf_matchmaking_ping_c_score, "-1.0", "Quickplay scoring ping score data point C" );
+
+TF2SCORECONVAR(tf_matchmaking_goodenough_score_start, "8.5", "Good enough score at start of search" );
+TF2SCORECONVAR(tf_matchmaking_goodenough_count_start, "20", "Good enough count at start of search" );
+TF2SCORECONVAR(tf_matchmaking_goodenough_score_end, "7.0", "Good enough score at end of search" );
+TF2SCORECONVAR(tf_matchmaking_goodenough_count_end, "5", "Good enough count at end of search" );
+
+TF2SCORECONVAR( tf_mm_options_bonus, "0.5", "Scoring bonus when approaching tobor rating." );
+TF2SCORECONVAR( tf_mm_options_penalty, "-0.25", "Scoring penalty when options score is too far outside an acceptable range." );
+
+TF2SCORECONVAR( tf_matchmaking_server_player_count_score, "1.5", "Maximum score when server is at/near optimal player count." );
+
+//ConVar tf_matchmaking_goodenough_hi_score_start( "tf_matchmaking_goodenough_hi_score_start", "6.0", FCVAR_NONE );
+//ConVar tf_matchmaking_goodenough_hi_count_start( "tf_matchmaking_goodenough_hi_count_start", "5", FCVAR_NONE );
+//ConVar tf_matchmaking_goodenough_hi_score_end( "tf_matchmaking_goodenough_hi_score_end", "3.0", FCVAR_NONE );
+//ConVar tf_matchmaking_goodenough_hi_count_end( "tf_matchmaking_goodenough_hi_count_end", "2", FCVAR_NONE );
+
+ConVar tf_matchmaking_max_search_time( "tf_matchmaking_max_search_time", "45", FCVAR_NONE );
+
+#undef TF2SCORECONVAR
+
+static inline float lerp( float inA, float outA, float inB, float outB, float x )
+{
+ Assert( inA != inB );
+ return outA + ( outB - outA ) * ( x - inA ) / ( inB - inA );
+}
+
+struct TF2ScoringNumbers_t
+{
+
+ //
+ // If we do further experiments, we should make distinct enum values, so we can easily
+ // compare the stats on the backend
+ //
+ enum ExperimentGroup_t
+ {
+ k_ExperimentGroup_None, // no experiment active
+
+ //
+ // Experiment 1
+ //
+ k_ExperimentGroup_Experiment1_Control = 1,
+ k_ExperimentGroup_Experiment1_ValveBias = 2,
+ k_ExperimentGroup_Experiment1_ValveBiasInactive = 3,
+ k_ExperimentGroup_Experiment1_CommunityBias = 4,
+ k_ExperimentGroup_Experiment1_CommunityBiasInactive = 5,
+ };
+
+ ExperimentGroup_t m_eExperimentGroup;
+
+ TF2ScoringNumbers_t( CSteamID whosAsking )
+ {
+ m_eExperimentGroup = k_ExperimentGroup_None;
+
+ //
+ // Assign experiment group for experiment 1
+ //
+ switch ( whosAsking.GetAccountID() & 3 )
+ {
+ case 0:
+ // 80% chance to adjust behaviour
+ if ( RandomFloat( 0.0f, 1.0f) < 0.8f )
+ {
+ m_eExperimentGroup = k_ExperimentGroup_Experiment1_ValveBias;
+ }
+ else
+ {
+ m_eExperimentGroup = k_ExperimentGroup_Experiment1_ValveBiasInactive;
+ }
+ break;
+
+ case 2:
+ // 80% chance to adjust behaviour
+ if ( RandomFloat( 0.0f, 1.0f) < 0.8f )
+ {
+ m_eExperimentGroup = k_ExperimentGroup_Experiment1_CommunityBias;
+ }
+ else
+ {
+ m_eExperimentGroup = k_ExperimentGroup_Experiment1_CommunityBiasInactive;
+ }
+ break;
+
+ default:
+ m_eExperimentGroup = k_ExperimentGroup_Experiment1_Control;
+ break;
+ }
+ }
+};
+
+float QuickplayCalculateServerScore( int numHumans, int numBots, int maxPlayers, int nNumInSearchParty )
+{
+ Assert( nNumInSearchParty > 0 );
+
+ // Safety check against a degenerate case with invalid max number of players.
+ // Protects against some bad math below
+ if ( maxPlayers < kTFQuickPlayMinMaxNumberOfPlayers )
+ {
+ return -100.0f;
+ }
+ if ( maxPlayers > kTFQuickPlayMaxPlayers )
+ {
+ // Server should have been filtered, but in case we get here...
+ maxPlayers = kTFQuickPlayMaxPlayers;
+ }
+
+ float score = 0.0;
+
+ // Check for completely full server
+ int newNumHumans = numHumans + nNumInSearchParty;
+ int newNumTotalPlayers = newNumHumans + numBots;
+ if ( newNumTotalPlayers + tf_matchmaking_numbers_serverfull_headroom.GetInt() > maxPlayers )
+ {
+ // Server full! Huge penalty!
+ score += -100.0f;
+ }
+ else
+ {
+ // Data points for piecewise linear interpolation.
+ // First point is implied: empty server is a score of zero.
+ //
+ // Then we increase up to point A
+ int playerCountA = maxPlayers / 3;
+ float scoreA = 0.20f;
+
+ // Next data point is when the score peaks at 100%
+ int idealPlayerCount = maxPlayers * 5 / 6;
+
+ // Finally, the last data point when the server is full.
+ // This choice reflects a pretty steep dropoff. This server
+ // is already in a good state, we should begin to send players
+ // to other servers so that they can start to fill up, and reduce
+ // the race condition with players trying to join nearly full
+ // servers and being too late and getting rejected.
+ float scoreFull = scoreA;
+ float flMaxScore = Max( tf_matchmaking_server_player_count_score.GetFloat(), 0.1f );
+
+ // Do the piecewise linear interpolation
+ if ( newNumHumans <= playerCountA )
+ {
+ score += lerp( 0, 0.0f, playerCountA, scoreA, float( newNumHumans ) );
+ }
+ else if ( newNumHumans <= idealPlayerCount )
+ {
+ // Interpolate from point A up to 100%
+ score += lerp( float( playerCountA ), scoreA, idealPlayerCount, flMaxScore, float( newNumHumans ) );
+ }
+ else
+ {
+ // Greater than ideal. Interpolate back down to the score when the server is full
+ score += lerp( idealPlayerCount, flMaxScore, maxPlayers, scoreFull, float( newNumHumans ) );
+ }
+ }
+
+// Don't apply a penalty anymore. Instead, we just let players express their preference
+// // Give a penalty for servers that increase the max player
+// // number above the ideal.
+// if ( maxPlayers > kTFQuickPlayIdealMaxNumberOfPlayers )
+// {
+// // Max penalty, if they increased it up all the way to
+// // kTFQuickPlayMaxPlayers. (Above this, we reject them completely)
+// int nExcessPlayers = maxPlayers - kTFQuickPlayIdealMaxNumberOfPlayers;
+// const int kMaxExcessPlayers = kTFQuickPlayMaxPlayers - kTFQuickPlayIdealMaxNumberOfPlayers;
+// float penalty = tf_matchmaking_numbers_increase_maxplayers_penalty.GetFloat() * (float)nExcessPlayers / (float)kMaxExcessPlayers;
+// score -= penalty;
+// }
+
+ //// being tagged as quickplay is roughly the same weight as best ping and best ratio of player numbers
+ //if ( bHasQuickplayTag )
+ //{
+ // item.score += 2.0f;
+ //}
+
+ return score;
+}