diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 2fd57d272..513e58cf0 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -775,34 +775,43 @@ void ServerLobby::checkRaceFinished() //----------------------------------------------------------------------------- /** Compute the new player's rankings in ranked servers + * //FIXME : this function assumes that m_rankings, + * m_num_ranked_races and m_max_ranking + * are correctly filled before + * It also assumes that the data stored by them + * is written back to the main list after the GP */ void ServerLobby::computeNewRankings() { - //TODO : this is overall a rather naive implementation - // several possibilities to speed up the computation exist, - // though this is minor in importance as it's not done - // during racing. + auto players = m_game_setup->getConnectedPlayers(true/*same_index_for_disconnected*/); - //TODO : make some numerical parts of the formulas - // depend of a single constant value + assert (m_rankings.size() == players.size() && + m_num_ranked_races.size() == players.size() && + m_max_ranking.size() == players.size() ); // No ranking yet for battle mode // TODO : separate rankings for time-trial and normal and FTL ?? if (!race_manager->modeHasLaps()) return; - auto& players = m_game_setup->getPlayers(); - + // Using a vector of vector, it would be possible to fill + // all j < i v[i][j] with -v[j][i] + // Would this be worth it ? std::vector ranking_change; + for (unsigned i = 0; i < players.size(); i++) + { + m_rankings[i] += distributeBasePoints(i); + } + for (unsigned i = 0; i < players.size(); i++) { ranking_change.push_back(0); - //FIXME : if the players have been kicked out, there is no way to retrieve the data ! + int player1_ranking = m_rankings[i]; - //FIXME : can't use a weak_ptr like that - int player1_ranking = players[i]->getRankingPoints(); + // If the player has quitted before the race end, + // the value will be incorrect, but it will not be used float player1_time = race_manager->getKartRaceTime(i); float player1_factor = computeRankingFactor(i); @@ -817,13 +826,13 @@ void ServerLobby::computeNewRankings() double ranking_importance = 0.0f; // No change between two quitting players - if (!players[i].lock() && !players[j].lock()) + if (!players[i] && !players[j]) continue; - int player2_ranking = players[i]->getRankingPoints(); + int player2_ranking = m_rankings[j]; float player2_time = race_manager->getKartRaceTime(j); - // Compute the expected result + // Compute the expected result using an ELO-like function double diff = (double) player2_ranking - player1_ranking; expected_result = 1.0f/(1.0f+std::pow(10.0f, diff/(BASE_RANKING_POINTS/2.0f))); @@ -831,15 +840,17 @@ void ServerLobby::computeNewRankings() float player_factors = std::max(player1_factor, computeRankingFactor(j) ); - if (!players[i].lock()) + if (!players[i]) { result = 0.0f; - ranking_importance = (MAX_SCALING_TIME/20.0f)*player_factors; + ranking_importance = + MAX_SCALING_TIME*MAX_POINTS_PER_SECOND*player_factors; } - else if (!players[j].lock()) + else if (!players[j]) { result = 1.0f; - ranking_importance = (MAX_SCALING_TIME/20.0f)*player_factors; + ranking_importance = + MAX_SCALING_TIME*MAX_POINTS_PER_SECOND*player_factors; } else { @@ -856,21 +867,22 @@ void ServerLobby::computeNewRankings() result = std::max( (double) 0.0f, 0.5f - result); } ranking_importance = std::min ( std::max (player1_time, player2_time), - MAX_SCALING_TIME ) * player_factors/20.0f; + MAX_SCALING_TIME ) * MAX_POINTS_PER_SECOND * player_factors; } // Compute the ranking change ranking_change[i] += ranking_importance * (result - expected_result); } } - // Don't merge it in the main loop as long as getRankingPoints - // is called in it + // Don't merge it in the main loop as m_rankings value are used there for (unsigned i = 0; i < players.size(); i++) { - // Add or substract 0.5f so that the cast to int acts like rounding - ranking_change[i] += (ranking_change[i] > 0 ) ? 0.5f : -0.5f; - int old_ranking_points = players[i]->getRankingPoints(); - players[i]->setRankingPoints(old_ranking_points + ((int) ranking_change[i])); + m_rankings[i] += ranking_change[i]; + // This isn't entirely correct when rankings are negatives, but + // the max will always be positive + if ((int) (m_rankings[i]+0.5f) > m_max_ranking[i]) + m_max_ranking[i] = (int) (m_rankings[i]+0.5f); + m_num_ranked_races[i]++; } } //computeNewRankings @@ -880,30 +892,42 @@ void ServerLobby::computeNewRankings() */ float ServerLobby::computeRankingFactor(unsigned int player_id) { - auto& players = m_game_setup->getPlayers(); + int max_points = m_max_ranking[player_id]; + int num_races = m_num_ranked_races[player_id]; - assert(player_id < players.size()); - - int max_points = players[player_id].getMaxRankingPoints(); - int num_races = players[player_id].getNumRankedRaces(); - - if (max_points >= (int) (BASE_RANKING_POINTS * 2.0f)) - return 1.0f; + if ( max_points >= (int) (BASE_RANKING_POINTS * 2.0f)) + return 0.4f; else if (max_points >= (int) (BASE_RANKING_POINTS * 1.75f) || num_races > 500) - return 1.25f; + return 0.5f; else if (max_points >= (int) (BASE_RANKING_POINTS * 1.5f) || num_races > 250) - return 1.5f; + return 0.6f; else if (max_points >= (int) (BASE_RANKING_POINTS * 1.25f) || num_races > 100) - return 1.75f; + return 0.7f; // The base ranking points are not distributed all at once // So it's not guaranteed a player reach them - else if (max_points >= (int) (BASE_RANKING_POINTS ) || num_races > 50) - return 2.0f; + else if (max_points >= (int) (BASE_RANKING_POINTS ) || num_races > 50) + return 0.8f; else - return 2.5f; + return 1.0f; } //computeRankingFactor +//----------------------------------------------------------------------------- +/** Manages the distribution of the base points. + * Gives half of the points immediately and the other half progressively + * by smaller and smaller chuncks from race 1 to 46. + */ +float ServerLobby::distributeBasePoints(unsigned int player_id) +{ + int num_races = m_num_ranked_races[player_id]; + if (num_races == 0) + return BASE_RANKING_POINTS/2.0f; + else if (num_races <= 45) + return (BASE_RANKING_POINTS/2000.0f * std::max((45-num_races),4)*2.0f); + else + return 0.0f; +} + //----------------------------------------------------------------------------- /** Stop any race currently in server, should only be called in main thread. */ diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 253875b7c..eb826c7f9 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -80,8 +80,13 @@ private: /* Ranking related variables */ - const float BASE_RANKING_POINTS = 4000.0f; - const float MAX_SCALING_TIME = 600.0f; + const float BASE_RANKING_POINTS = 4000.0f; + const float MAX_SCALING_TIME = 600.0f; + const float MAX_POINTS_PER_SECOND = 0.125f; + + std::vector m_rankings; // TODO : convert from and to int when communicating with the server. Think to round it correctly + std::vector m_num_ranked_races; + std::vector m_max_ranking; // connection management void clientDisconnected(Event* event); @@ -133,6 +138,7 @@ private: void stopCurrentRace(); void computeNewRankings(); float computeRankingFactor(unsigned int player_id); + float distributeBasePoints(unsigned int player_id); public: ServerLobby();