diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_matchmaking_scoring.h | |
| download | archived-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.h | 205 |
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; +} |