Merge pull request #563 Fix leaderboard not updating when it is supposed to

Fixed leaderboards not updating when they are supposed to.
This commit is contained in:
David Markowitz 2022-05-24 20:36:35 -07:00 committed by GitHub
commit 731c25741c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 187 additions and 19 deletions

View File

@ -3,8 +3,10 @@
#include "Database.h" #include "Database.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Character.h" #include "Character.h"
#include "Game.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "dLogger.h" #include "dLogger.h"
#include "dConfig.h"
Leaderboard::Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries, Leaderboard::Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries,
LWOOBJID relatedPlayer, LeaderboardType leaderboardType) { LWOOBJID relatedPlayer, LeaderboardType leaderboardType) {
@ -90,6 +92,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
const auto storedTime = result->getInt(1); const auto storedTime = result->getInt(1);
const auto storedScore = result->getInt(2); const auto storedScore = result->getInt(2);
auto highscore = true; auto highscore = true;
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
switch (leaderboardType) { switch (leaderboardType) {
case ShootingGallery: case ShootingGallery:
@ -97,8 +100,11 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
highscore = false; highscore = false;
break; break;
case Racing: case Racing:
if (time >= storedTime)
highscore = false;
break;
case MonumentRace: case MonumentRace:
if (time > storedTime) if (time >= storedTime)
highscore = false; highscore = false;
break; break;
case FootRace: case FootRace:
@ -106,8 +112,18 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
highscore = false; highscore = false;
break; break;
case Survival: case Survival:
if (classicSurvivalScoring) {
if (time <= storedTime) { // Based on time (LU live)
highscore = false;
}
}
else {
if (score <= storedScore) // Based on score (DLU)
highscore = false;
}
break;
case SurvivalNS: case SurvivalNS:
if (score < storedScore || time >= storedTime) if (!(score > storedScore || (time < storedTime && score >= storedScore)))
highscore = false; highscore = false;
break; break;
default: default:
@ -125,7 +141,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
delete result; delete result;
if (any) { if (any) {
auto* statement = Database::CreatePreppedStmt("UPDATE leaderboard SET time = ?, score = ? WHERE character_id = ? AND game_id = ?"); auto* statement = Database::CreatePreppedStmt("UPDATE leaderboard SET time = ?, score = ?, last_played=SYSDATE() WHERE character_id = ? AND game_id = ?;");
statement->setInt(1, time); statement->setInt(1, time);
statement->setInt(2, score); statement->setInt(2, score);
statement->setUInt64(3, character->GetID()); statement->setUInt64(3, character->GetID());
@ -134,6 +150,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
delete statement; delete statement;
} else { } else {
// Note: last_played will be set to SYSDATE() by default when inserting into leaderboard
auto* statement = Database::CreatePreppedStmt("INSERT INTO leaderboard (character_id, game_id, time, score) VALUES (?, ?, ?, ?);"); auto* statement = Database::CreatePreppedStmt("INSERT INTO leaderboard (character_id, game_id, time, score) VALUES (?, ?, ?, ?);");
statement->setUInt64(1, character->GetID()); statement->setUInt64(1, character->GetID());
statement->setInt(2, gameID); statement->setInt(2, gameID);
@ -149,15 +166,62 @@ Leaderboard *LeaderboardManager::GetLeaderboard(uint32_t gameID, InfoType infoTy
auto leaderboardType = GetLeaderboardType(gameID); auto leaderboardType = GetLeaderboardType(gameID);
std::string query; std::string query;
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
switch (infoType) { switch (infoType) {
case InfoType::Standings: case InfoType::Standings:
query = leaderboardType == MonumentRace ? standingsQueryAsc : standingsQuery; switch (leaderboardType) {
case ShootingGallery:
query = standingsScoreQuery; // Shooting gallery is based on the highest score.
break; break;
case InfoType::Friends: case FootRace:
query = leaderboardType == MonumentRace ? friendsQueryAsc : friendsQuery; query = standingsTimeQuery; // The higher your time, the better for FootRace.
break;
case Survival:
query = classicSurvivalScoring ? standingsTimeQuery : standingsScoreQuery;
break;
case SurvivalNS:
query = standingsScoreQueryAsc; // BoNS is scored by highest wave (score) first, then time.
break; break;
default: default:
query = leaderboardType == MonumentRace ? topPlayersQueryAsc : topPlayersQuery; query = standingsTimeQueryAsc; // MonumentRace and Racing are based on the shortest time.
}
break;
case InfoType::Friends:
switch (leaderboardType) {
case ShootingGallery:
query = friendsScoreQuery; // Shooting gallery is based on the highest score.
break;
case FootRace:
query = friendsTimeQuery; // The higher your time, the better for FootRace.
break;
case Survival:
query = classicSurvivalScoring ? friendsTimeQuery : friendsScoreQuery;
break;
case SurvivalNS:
query = friendsScoreQueryAsc; // BoNS is scored by highest wave (score) first, then time.
break;
default:
query = friendsTimeQueryAsc; // MonumentRace and Racing are based on the shortest time.
}
break;
default:
switch (leaderboardType) {
case ShootingGallery:
query = topPlayersScoreQuery; // Shooting gallery is based on the highest score.
break;
case FootRace:
query = topPlayersTimeQuery; // The higher your time, the better for FootRace.
break;
case Survival:
query = classicSurvivalScoring ? topPlayersTimeQuery : topPlayersScoreQuery;
break;
case SurvivalNS:
query = topPlayersScoreQueryAsc; // BoNS is scored by highest wave (score) first, then time.
break;
default:
query = topPlayersTimeQueryAsc; // MonumentRace and Racing are based on the shortest time.
}
} }
auto* statement = Database::CreatePreppedStmt(query); auto* statement = Database::CreatePreppedStmt(query);
@ -220,7 +284,7 @@ LeaderboardType LeaderboardManager::GetLeaderboardType(uint32_t gameID) {
return LeaderboardType::None; return LeaderboardType::None;
} }
const std::string LeaderboardManager::topPlayersQuery = const std::string LeaderboardManager::topPlayersScoreQuery =
"WITH leaderboard_vales AS ( " "WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, " " SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
"RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank " "RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank "
@ -231,7 +295,7 @@ const std::string LeaderboardManager::topPlayersQuery =
"SELECT time, score, leaderboard_rank, id, name, last_played " "SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales LIMIT 11;"; "FROM leaderboard_vales LIMIT 11;";
const std::string LeaderboardManager::friendsQuery = const std::string LeaderboardManager::friendsScoreQuery =
"WITH leaderboard_vales AS ( " "WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, " " SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
" RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank " " RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank "
@ -249,7 +313,7 @@ const std::string LeaderboardManager::friendsQuery =
"FROM leaderboard_vales, personal_values " "FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);"; "WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
const std::string LeaderboardManager::standingsQuery = const std::string LeaderboardManager::standingsScoreQuery =
"WITH leaderboard_vales AS ( " "WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, " " SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
" RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank " " RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank "
@ -265,7 +329,7 @@ const std::string LeaderboardManager::standingsQuery =
"FROM leaderboard_vales, personal_values " "FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;"; "WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
const std::string LeaderboardManager::topPlayersQueryAsc = const std::string LeaderboardManager::topPlayersScoreQueryAsc =
"WITH leaderboard_vales AS ( " "WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, " " SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
"RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank " "RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank "
@ -276,7 +340,7 @@ const std::string LeaderboardManager::topPlayersQueryAsc =
"SELECT time, score, leaderboard_rank, id, name, last_played " "SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales LIMIT 11;"; "FROM leaderboard_vales LIMIT 11;";
const std::string LeaderboardManager::friendsQueryAsc = const std::string LeaderboardManager::friendsScoreQueryAsc =
"WITH leaderboard_vales AS ( " "WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, " " SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
" RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank " " RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank "
@ -294,7 +358,7 @@ const std::string LeaderboardManager::friendsQueryAsc =
"FROM leaderboard_vales, personal_values " "FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);"; "WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
const std::string LeaderboardManager::standingsQueryAsc = const std::string LeaderboardManager::standingsScoreQueryAsc =
"WITH leaderboard_vales AS ( " "WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, " " SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
" RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank " " RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank "
@ -309,3 +373,93 @@ const std::string LeaderboardManager::standingsQueryAsc =
"SELECT time, score, leaderboard_rank, id, name, last_played " "SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales, personal_values " "FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;"; "WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
const std::string LeaderboardManager::topPlayersTimeQuery =
"WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
"RANK() OVER ( ORDER BY l.time DESC, l.score DESC, last_played ) leaderboard_rank "
" FROM leaderboard l "
"INNER JOIN charinfo c ON l.character_id = c.id "
"WHERE l.game_id = ? "
"ORDER BY leaderboard_rank) "
"SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales LIMIT 11;";
const std::string LeaderboardManager::friendsTimeQuery =
"WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
" RANK() OVER ( ORDER BY l.time DESC, l.score DESC, last_played ) leaderboard_rank "
" FROM leaderboard l "
" INNER JOIN charinfo c ON l.character_id = c.id "
" INNER JOIN friends f ON f.player_id = c.id "
" WHERE l.game_id = ? "
" ORDER BY leaderboard_rank), "
" personal_values AS ( "
" SELECT id as related_player_id, "
" GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
" FROM leaderboard_vales WHERE leaderboard_vales.id = ? LIMIT 1) "
"SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
const std::string LeaderboardManager::standingsTimeQuery =
"WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
" RANK() OVER ( ORDER BY l.time DESC, l.score DESC, last_played ) leaderboard_rank "
" FROM leaderboard l "
" INNER JOIN charinfo c ON l.character_id = c.id "
" WHERE l.game_id = ? "
" ORDER BY leaderboard_rank), "
"personal_values AS ( "
" SELECT GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
" FROM leaderboard_vales WHERE id = ? LIMIT 1) "
"SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
const std::string LeaderboardManager::topPlayersTimeQueryAsc =
"WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
"RANK() OVER ( ORDER BY l.time ASC, l.score DESC, last_played ) leaderboard_rank "
" FROM leaderboard l "
"INNER JOIN charinfo c ON l.character_id = c.id "
"WHERE l.game_id = ? "
"ORDER BY leaderboard_rank) "
"SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales LIMIT 11;";
const std::string LeaderboardManager::friendsTimeQueryAsc =
"WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
" RANK() OVER ( ORDER BY l.time ASC, l.score DESC, last_played ) leaderboard_rank "
" FROM leaderboard l "
" INNER JOIN charinfo c ON l.character_id = c.id "
" INNER JOIN friends f ON f.player_id = c.id "
" WHERE l.game_id = ? "
" ORDER BY leaderboard_rank), "
" personal_values AS ( "
" SELECT id as related_player_id, "
" GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
" FROM leaderboard_vales WHERE leaderboard_vales.id = ? LIMIT 1) "
"SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
const std::string LeaderboardManager::standingsTimeQueryAsc =
"WITH leaderboard_vales AS ( "
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
" RANK() OVER ( ORDER BY l.time ASC, l.score DESC, last_played ) leaderboard_rank "
" FROM leaderboard l "
" INNER JOIN charinfo c ON l.character_id = c.id "
" WHERE l.game_id = ? "
" ORDER BY leaderboard_rank), "
"personal_values AS ( "
" SELECT GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
" FROM leaderboard_vales WHERE id = ? LIMIT 1) "
"SELECT time, score, leaderboard_rank, id, name, last_played "
"FROM leaderboard_vales, personal_values "
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";

View File

@ -60,11 +60,21 @@ public:
static LeaderboardType GetLeaderboardType(uint32_t gameID); static LeaderboardType GetLeaderboardType(uint32_t gameID);
private: private:
static LeaderboardManager* address; static LeaderboardManager* address;
static const std::string topPlayersQuery;
static const std::string friendsQuery; // Modified 12/12/2021: Existing queries were renamed to be more descriptive.
static const std::string standingsQuery; static const std::string topPlayersScoreQuery;
static const std::string topPlayersQueryAsc; static const std::string friendsScoreQuery;
static const std::string friendsQueryAsc; static const std::string standingsScoreQuery;
static const std::string standingsQueryAsc; static const std::string topPlayersScoreQueryAsc;
static const std::string friendsScoreQueryAsc;
static const std::string standingsScoreQueryAsc;
// Added 12/12/2021: Queries dictated by time are needed for certain minigames.
static const std::string topPlayersTimeQuery;
static const std::string friendsTimeQuery;
static const std::string standingsTimeQuery;
static const std::string topPlayersTimeQueryAsc;
static const std::string friendsTimeQueryAsc;
static const std::string standingsTimeQueryAsc;
}; };

View File

@ -53,3 +53,7 @@ disable_anti_speedhack=0
# 0 or 1, check server fdb (res/CDServer.fdb) against clients # 0 or 1, check server fdb (res/CDServer.fdb) against clients
check_fdb=0 check_fdb=0
# 0 or 1, DLU leaderboards will rate Avant Gardens Survival based on score by default.
# This option should be set to 1 if you would like it to reflect the game when it was live (scoring based on time).
classic_survival_scoring=0