Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

206 lines
8.3 KiB

5 years ago
//========= 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;
}