mirror of
https://github.com/DarkflameUniverse/DarkflameServer
synced 2024-08-30 18:43:58 +00:00
first draft
This commit is contained in:
parent
4734996c7c
commit
c02963013b
@ -13,17 +13,20 @@
|
|||||||
|
|
||||||
#include "CDActivitiesTable.h"
|
#include "CDActivitiesTable.h"
|
||||||
|
|
||||||
Leaderboard::Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries,
|
Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const Leaderboard::Type leaderboardType) {
|
||||||
LWOOBJID relatedPlayer, LeaderboardType leaderboardType) {
|
|
||||||
this->relatedPlayer = relatedPlayer;
|
this->relatedPlayer = relatedPlayer;
|
||||||
this->gameID = gameID;
|
this->gameID = gameID;
|
||||||
this->weekly = weekly;
|
this->weekly = weekly;
|
||||||
this->infoType = infoType;
|
this->infoType = infoType;
|
||||||
this->entries = std::move(entries);
|
this->entries = entries;
|
||||||
this->leaderboardType = leaderboardType;
|
this->leaderboardType = leaderboardType;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string Leaderboard::ToString() const {
|
bool Leaderboard::IsScoreBetter(const uint32_t score) const {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Leaderboard::Serialize(RakNet::BitStream* bitStream) const {
|
||||||
std::string leaderboard;
|
std::string leaderboard;
|
||||||
|
|
||||||
leaderboard += "ADO.Result=7:1\n";
|
leaderboard += "ADO.Result=7:1\n";
|
||||||
@ -50,19 +53,14 @@ std::u16string Leaderboard::ToString() const {
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GeneralUtils::UTF8ToUTF16(leaderboard);
|
// Serialize the thing to a BitStream
|
||||||
|
bitStream->WriteAlignedBytes((const unsigned char*)leaderboard.c_str(), leaderboard.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LeaderboardEntry> Leaderboard::GetEntries() {
|
void Leaderboard::SetupLeaderboard() {
|
||||||
return entries;
|
// Setup query based on activity.
|
||||||
}
|
// Where clause will vary based on what query we are doing
|
||||||
|
|
||||||
uint32_t Leaderboard::GetGameID() const {
|
|
||||||
return gameID;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Leaderboard::GetInfoType() const {
|
|
||||||
return infoType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Leaderboard::Send(LWOOBJID targetID) const {
|
void Leaderboard::Send(LWOOBJID targetID) const {
|
||||||
@ -78,10 +76,10 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto* character = player->GetCharacter();
|
auto* character = player->GetCharacter();
|
||||||
if (character == nullptr)
|
if (!character)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* select = Database::CreatePreppedStmt("SELECT time, score FROM leaderboard WHERE character_id = ? AND game_id = ?;");
|
std::unique_ptr<sql::PreparedStatement> select(Database::CreatePreppedStmt("SELECT time, score FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
|
||||||
|
|
||||||
select->setUInt64(1, character->GetID());
|
select->setUInt64(1, character->GetID());
|
||||||
select->setInt(2, gameID);
|
select->setInt(2, gameID);
|
||||||
@ -100,23 +98,23 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
|||||||
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
||||||
|
|
||||||
switch (leaderboardType) {
|
switch (leaderboardType) {
|
||||||
case ShootingGallery:
|
case Leaderboard::Type::ShootingGallery:
|
||||||
if (score <= storedScore)
|
if (score <= storedScore)
|
||||||
highscore = false;
|
highscore = false;
|
||||||
break;
|
break;
|
||||||
case Racing:
|
case Leaderboard::Type::Racing:
|
||||||
if (time >= storedTime)
|
if (time >= storedTime)
|
||||||
highscore = false;
|
highscore = false;
|
||||||
break;
|
break;
|
||||||
case MonumentRace:
|
case Leaderboard::Type::MonumentRace:
|
||||||
if (time >= storedTime)
|
if (time >= storedTime)
|
||||||
highscore = false;
|
highscore = false;
|
||||||
break;
|
break;
|
||||||
case FootRace:
|
case Leaderboard::Type::FootRace:
|
||||||
if (time <= storedTime)
|
if (time <= storedTime)
|
||||||
highscore = false;
|
highscore = false;
|
||||||
break;
|
break;
|
||||||
case Survival:
|
case Leaderboard::Type::Survival:
|
||||||
if (classicSurvivalScoring) {
|
if (classicSurvivalScoring) {
|
||||||
if (time <= storedTime) { // Based on time (LU live)
|
if (time <= storedTime) { // Based on time (LU live)
|
||||||
highscore = false;
|
highscore = false;
|
||||||
@ -126,7 +124,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
|||||||
highscore = false;
|
highscore = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SurvivalNS:
|
case Leaderboard::Type::SurvivalNS:
|
||||||
if (!(score > storedScore || (time < storedTime && score >= storedScore)))
|
if (!(score > storedScore || (time < storedTime && score >= storedScore)))
|
||||||
highscore = false;
|
highscore = false;
|
||||||
break;
|
break;
|
||||||
@ -135,13 +133,11 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!highscore) {
|
if (!highscore) {
|
||||||
delete select;
|
|
||||||
delete result;
|
delete result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete select;
|
|
||||||
delete result;
|
delete result;
|
||||||
|
|
||||||
if (any) {
|
if (any) {
|
||||||
@ -166,305 +162,24 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Leaderboard* LeaderboardManager::GetLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID playerID) {
|
void LeaderboardManager::SendLeaderboard(uint32_t gameID, Leaderboard::InfoType infoType, bool weekly, LWOOBJID targetID,
|
||||||
auto leaderboardType = GetLeaderboardType(gameID);
|
|
||||||
|
|
||||||
std::string query;
|
|
||||||
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
|
||||||
switch (infoType) {
|
|
||||||
case InfoType::Standings:
|
|
||||||
switch (leaderboardType) {
|
|
||||||
case ShootingGallery:
|
|
||||||
query = standingsScoreQuery; // Shooting gallery is based on the highest score.
|
|
||||||
break;
|
|
||||||
case FootRace:
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
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);
|
|
||||||
statement->setUInt(1, gameID);
|
|
||||||
|
|
||||||
// Only the standings and friends leaderboards require the character ID to be set
|
|
||||||
if (infoType == Standings || infoType == Friends) {
|
|
||||||
auto characterID = 0;
|
|
||||||
|
|
||||||
const auto* player = EntityManager::Instance()->GetEntity(playerID);
|
|
||||||
if (player != nullptr) {
|
|
||||||
auto* character = player->GetCharacter();
|
|
||||||
if (character != nullptr)
|
|
||||||
characterID = character->GetID();
|
|
||||||
}
|
|
||||||
|
|
||||||
statement->setUInt64(2, characterID);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* res = statement->executeQuery();
|
|
||||||
|
|
||||||
std::vector<LeaderboardEntry> entries{};
|
|
||||||
|
|
||||||
uint32_t index = 0;
|
|
||||||
while (res->next()) {
|
|
||||||
LeaderboardEntry entry;
|
|
||||||
entry.playerID = res->getUInt64(4);
|
|
||||||
entry.playerName = res->getString(5);
|
|
||||||
entry.time = res->getUInt(1);
|
|
||||||
entry.score = res->getUInt(2);
|
|
||||||
entry.placement = res->getUInt(3);
|
|
||||||
entry.lastPlayed = res->getUInt(6);
|
|
||||||
|
|
||||||
entries.push_back(entry);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete res;
|
|
||||||
delete statement;
|
|
||||||
|
|
||||||
return new Leaderboard(gameID, infoType, weekly, entries, playerID, leaderboardType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaderboardManager::SendLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID targetID,
|
|
||||||
LWOOBJID playerID) {
|
LWOOBJID playerID) {
|
||||||
const auto* leaderboard = LeaderboardManager::GetLeaderboard(gameID, infoType, weekly, playerID);
|
// Create the leaderboard here and then send it right after. On the stack.
|
||||||
leaderboard->Send(targetID);
|
Leaderboard leaderboard(gameID, infoType, weekly, GetLeaderboardType(gameID));
|
||||||
delete leaderboard;
|
leaderboard.SetupLeaderboard();
|
||||||
|
leaderboard.Send(targetID);
|
||||||
}
|
}
|
||||||
|
|
||||||
LeaderboardType LeaderboardManager::GetLeaderboardType(uint32_t gameID) {
|
// Done
|
||||||
|
Leaderboard::Type LeaderboardManager::GetLeaderboardType(const GameID gameID) {
|
||||||
|
auto lookup = leaderboardCache.find(gameID);
|
||||||
|
if (lookup != leaderboardCache.end()) return lookup->second;
|
||||||
|
|
||||||
auto* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
auto* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||||
std::vector<CDActivities> activities = activitiesTable->Query([=](const CDActivities& entry) {
|
std::vector<CDActivities> activities = activitiesTable->Query([=](const CDActivities& entry) {
|
||||||
return (entry.ActivityID == gameID);
|
return (entry.ActivityID == gameID);
|
||||||
});
|
});
|
||||||
|
auto type = activities.empty() ? static_cast<Leaderboard::Type>(activities.at(0).leaderboardType) : Leaderboard::Type::None;
|
||||||
for (const auto& activity : activities) {
|
leaderboardCache.insert_or_assign(gameID, type);
|
||||||
return static_cast<LeaderboardType>(activity.leaderboardType);
|
return type;
|
||||||
}
|
|
||||||
|
|
||||||
return LeaderboardType::None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string LeaderboardManager::topPlayersScoreQuery =
|
|
||||||
"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.score DESC, l.time 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::friendsScoreQuery =
|
|
||||||
"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.score DESC, l.time 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::standingsScoreQuery =
|
|
||||||
"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.score DESC, l.time 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::topPlayersScoreQueryAsc =
|
|
||||||
"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.score DESC, l.time ASC, 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::friendsScoreQueryAsc =
|
|
||||||
"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.score DESC, l.time ASC, 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::standingsScoreQueryAsc =
|
|
||||||
"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.score DESC, l.time ASC, 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::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;";
|
|
||||||
|
@ -1,80 +1,90 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
|
#include "Singleton.h"
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
struct LeaderboardEntry {
|
namespace RakNet{
|
||||||
uint64_t playerID;
|
class BitStream;
|
||||||
std::string playerName;
|
|
||||||
uint32_t time;
|
|
||||||
uint32_t score;
|
|
||||||
uint32_t placement;
|
|
||||||
time_t lastPlayed;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InfoType : uint32_t {
|
typedef uint32_t GameID;
|
||||||
Top, // Top 11 all time players
|
|
||||||
Standings, // Ranking of the current player
|
|
||||||
Friends // Ranking between friends
|
|
||||||
};
|
|
||||||
|
|
||||||
enum LeaderboardType : uint32_t {
|
|
||||||
ShootingGallery,
|
|
||||||
Racing,
|
|
||||||
MonumentRace,
|
|
||||||
FootRace,
|
|
||||||
Survival = 5,
|
|
||||||
SurvivalNS = 6,
|
|
||||||
None = UINT_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
class Leaderboard {
|
class Leaderboard {
|
||||||
public:
|
public:
|
||||||
Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries,
|
struct Entry {
|
||||||
LWOOBJID relatedPlayer = LWOOBJID_EMPTY, LeaderboardType = None);
|
uint64_t playerID;
|
||||||
std::vector<LeaderboardEntry> GetEntries();
|
uint32_t time;
|
||||||
[[nodiscard]] std::u16string ToString() const;
|
uint32_t score;
|
||||||
[[nodiscard]] uint32_t GetGameID() const;
|
uint32_t placement;
|
||||||
[[nodiscard]] uint32_t GetInfoType() const;
|
time_t lastPlayed;
|
||||||
|
std::string playerName;
|
||||||
|
};
|
||||||
|
typedef std::vector<Entry> LeaderboardEntries;
|
||||||
|
|
||||||
|
// Enums for leaderboards
|
||||||
|
enum InfoType : uint32_t {
|
||||||
|
Top, // Top 11 all time players
|
||||||
|
MyStanding, // Ranking of the current player
|
||||||
|
Friends // Ranking between friends
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Type : uint32_t {
|
||||||
|
ShootingGallery,
|
||||||
|
Racing,
|
||||||
|
MonumentRace,
|
||||||
|
FootRace,
|
||||||
|
Survival = 5,
|
||||||
|
SurvivalNS = 6,
|
||||||
|
None = UINT_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const Leaderboard::Type = None);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the Leaderboard to a BitStream
|
||||||
|
*
|
||||||
|
* Expensive! Leaderboards are very string intensive so be wary of performatnce calling this method.
|
||||||
|
*/
|
||||||
|
void Serialize(RakNet::BitStream* bitStream) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the associated gameID, return true if the score provided
|
||||||
|
* is better than the current entries' score
|
||||||
|
* @param score
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
bool IsScoreBetter(const uint32_t score) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the leaderboard from the database based on the associated gameID
|
||||||
|
*/
|
||||||
|
void SetupLeaderboard();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the leaderboard to the client specified by targetID.
|
||||||
|
*/
|
||||||
void Send(LWOOBJID targetID) const;
|
void Send(LWOOBJID targetID) const;
|
||||||
private:
|
private:
|
||||||
std::vector<LeaderboardEntry> entries{};
|
LeaderboardEntries entries;
|
||||||
LWOOBJID relatedPlayer;
|
LWOOBJID relatedPlayer;
|
||||||
uint32_t gameID;
|
GameID gameID;
|
||||||
uint32_t infoType;
|
InfoType infoType;
|
||||||
LeaderboardType leaderboardType;
|
Leaderboard::Type leaderboardType;
|
||||||
bool weekly;
|
bool weekly;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LeaderboardManager {
|
class LeaderboardManager: public Singleton<LeaderboardManager> {
|
||||||
|
typedef std::map<GameID, Leaderboard::Type> LeaderboardCache;
|
||||||
public:
|
public:
|
||||||
static LeaderboardManager* Instance() {
|
void SendLeaderboard(GameID gameID, Leaderboard::InfoType infoType, bool weekly, LWOOBJID targetID,
|
||||||
if (address == nullptr)
|
|
||||||
address = new LeaderboardManager;
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
static void SendLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID targetID,
|
|
||||||
LWOOBJID playerID = LWOOBJID_EMPTY);
|
LWOOBJID playerID = LWOOBJID_EMPTY);
|
||||||
static Leaderboard* GetLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID playerID = LWOOBJID_EMPTY);
|
void SaveScore(LWOOBJID playerID, GameID gameID, uint32_t score, uint32_t time);
|
||||||
static void SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t score, uint32_t time);
|
|
||||||
static LeaderboardType GetLeaderboardType(uint32_t gameID);
|
|
||||||
private:
|
private:
|
||||||
static LeaderboardManager* address;
|
Leaderboard::Type GetLeaderboardType(const GameID gameID);
|
||||||
|
void GetLeaderboard(uint32_t gameID, Leaderboard::InfoType infoType, bool weekly, LWOOBJID playerID = LWOOBJID_EMPTY);
|
||||||
// Modified 12/12/2021: Existing queries were renamed to be more descriptive.
|
LeaderboardCache leaderboardCache;
|
||||||
static const std::string topPlayersScoreQuery;
|
|
||||||
static const std::string friendsScoreQuery;
|
|
||||||
static const std::string standingsScoreQuery;
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,6 +150,12 @@ int main(int argc, char** argv) {
|
|||||||
const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite");
|
const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite");
|
||||||
const bool fdbExists = std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb");
|
const bool fdbExists = std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb");
|
||||||
|
|
||||||
|
auto query = Database::CreatePreppedStmt("select name, score, time, UNIX_TIMESTAMP(last_played) as lastPlayed from leaderboard as l join charinfo as ci on ci.id = l.character_id where game_id = 1864 order by score desc, time desc limit 11;");
|
||||||
|
auto myResult = query->executeQuery();
|
||||||
|
while (myResult->next()) {
|
||||||
|
Game::logger->Log("MasterServer", "%s %i %i %i", myResult->getString("name").c_str(), myResult->getInt("score"), myResult->getInt("time"), myResult->getInt("lastPlayed"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!cdServerExists) {
|
if (!cdServerExists) {
|
||||||
if (oldCDServerExists) {
|
if (oldCDServerExists) {
|
||||||
// If the file doesn't exist in the new CDServer location, copy it there. We copy because we may not have write permissions from the previous directory.
|
// If the file doesn't exist in the new CDServer location, copy it there. We copy because we may not have write permissions from the previous directory.
|
||||||
|
Loading…
Reference in New Issue
Block a user