Further work on leaderboards

- Modularize tests
- Add migrations
- Fix switch case so it actually breaks
- Add in missing writes
- Beginning work on custom migration to move the leaderboard to the final state
This commit is contained in:
David Markowitz 2023-04-17 01:16:23 -07:00
parent ed2639ce4e
commit fcb088d263
8 changed files with 93 additions and 51 deletions

View File

@ -7,6 +7,8 @@
#include "GeneralUtils.h"
#include "dLogger.h"
#include "BinaryPathFinder.h"
#include "CDActivitiesTable.h"
#include "CDClientManager.h"
#include <istream>
@ -56,6 +58,8 @@ void MigrationRunner::RunMigrations() {
Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str());
if (migration.name == "dlu/5_brick_model_sd0.sql") {
runSd0Migrations = true;
} else if (migration.name == "dlu/10_Update_Leaderboard_Columns.sql") {
continue;
} else {
finalSQL.append(migration.data.c_str());
}
@ -109,7 +113,7 @@ void MigrationRunner::RunSQLiteMigrations() {
// Check if there is an entry in the migration history table on the cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
cdstmt.bind((int32_t) 1, migration.name.c_str());
cdstmt.bind((int32_t)1, migration.name.c_str());
auto cdres = cdstmt.execQuery();
bool doExit = !cdres.eof();
cdres.finalize();
@ -127,7 +131,7 @@ void MigrationRunner::RunSQLiteMigrations() {
if (doExit) {
// Insert into cdclient database if there is an entry in the main database but not the cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
cdstmt.bind((int32_t) 1, migration.name.c_str());
cdstmt.bind((int32_t)1, migration.name.c_str());
cdstmt.execQuery().finalize();
cdstmt.finalize();
continue;
@ -148,7 +152,7 @@ void MigrationRunner::RunSQLiteMigrations() {
// Insert into cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
cdstmt.bind((int32_t) 1, migration.name.c_str());
cdstmt.bind((int32_t)1, migration.name.c_str());
cdstmt.execQuery().finalize();
cdstmt.finalize();
CDClientDatabase::ExecuteQuery("COMMIT;");
@ -156,3 +160,23 @@ void MigrationRunner::RunSQLiteMigrations() {
Game::logger->Log("MigrationRunner", "CDServer database is up to date.");
}
void MigrationRunner::MigrateLeaderboard() {
Game::logger->Log("MigrationRunner", "Checking if leaderboard migration needs to be run...");
{
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT * FROM migration_history WHERE name = ?;"));
stmt->setString(1, "dlu/10_Update_Leaderboard_Columns.sql");
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
if (res->next()) {
Game::logger->Log("MigrationRunner", "Leaderboard migration has already been run.");
return;
}
}
auto activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
auto leaderboards = activitiesTable->Query([=](const CDActivities& entry) {
return entry.leaderboardType != -1;
});
for (auto entry : leaderboards) {
Game::logger->Log("MigrationRunner", "Got leaderboard type %i for activity %i", entry.leaderboardType, entry.ActivityID);
}
}

View File

@ -10,4 +10,5 @@ struct Migration {
namespace MigrationRunner {
void RunMigrations();
void RunSQLiteMigrations();
void MigrateLeaderboard();
};

View File

@ -41,53 +41,59 @@ void Leaderboard::Serialize(RakNet::BitStream* bitStream) const {
WriteLeaderboardRow(leaderboard, index, "LastPlayed", eLDFType::LDF_TYPE_U64, entry.lastPlayed);
WriteLeaderboardRow(leaderboard, index, "NumPlayed", eLDFType::LDF_TYPE_S32, 1);
WriteLeaderboardRow(leaderboard, index, "name", eLDFType::LDF_TYPE_UTF_16, entry.playerName);
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_S32, entry.placement);
// Each minigame has its own "points" system
switch (leaderboardType) {
case Type::ShootingGallery:
WriteLeaderboardRow(leaderboard, index, "HitPercentage", eLDFType::LDF_TYPE_FLOAT, 0.0f);
// HitPercentage:3 between 0 and 1
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_S32, entry.placement);
// RowNumber:1
WriteLeaderboardRow(leaderboard, index, "Score", eLDFType::LDF_TYPE_S32, entry.score);
// Score:1
WriteLeaderboardRow(leaderboard, index, "HitPercentage", eLDFType::LDF_TYPE_FLOAT, 0.0f);
// HitPercentage:3 between 0 and 1
WriteLeaderboardRow(leaderboard, index, "Score", eLDFType::LDF_TYPE_S32, entry.score);
// Score:1
WriteLeaderboardRow(leaderboard, index, "Streak", eLDFType::LDF_TYPE_S32, 0);
// Streak:1
break;
case Type::Racing:
WriteLeaderboardRow(leaderboard, index, "BestLapTime", eLDFType::LDF_TYPE_FLOAT, 0.0f);
// BestLapTime:3
WriteLeaderboardRow(leaderboard, index, "BestTime", eLDFType::LDF_TYPE_FLOAT, 0.0f);
// BestTime:3
WriteLeaderboardRow(leaderboard, index, "License", eLDFType::LDF_TYPE_S32, 0);
// License:1
WriteLeaderboardRow(leaderboard, index, "NumWins", eLDFType::LDF_TYPE_S32, 0);
// NumWins:1
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_U64, entry.placement);
// RowNumber:8
WriteLeaderboardRow(leaderboard, index, "BestLapTime", eLDFType::LDF_TYPE_FLOAT, 0.0f);
// BestLapTime:3
WriteLeaderboardRow(leaderboard, index, "BestTime", eLDFType::LDF_TYPE_FLOAT, 0.0f);
// BestTime:3
WriteLeaderboardRow(leaderboard, index, "License", eLDFType::LDF_TYPE_S32, 0);
// License:1
WriteLeaderboardRow(leaderboard, index, "NumWins", eLDFType::LDF_TYPE_S32, 0);
// NumWins:1
break;
case Type::UnusedLeaderboard4:
WriteLeaderboardRow(leaderboard, index, "Points", eLDFType::LDF_TYPE_S32, entry.score);
// Points:1
break;
case Type::MonumentRace:
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_S32, entry.placement);
// RowNumber:1
// Time:1(?)
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, entry.time);
// Time:1(?)
break;
case Type::FootRace:
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_S32, entry.placement);
// RowNumber:1
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, 0);
// Time:1
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, entry.time);
// Time:1
break;
case Type::Survival:
WriteLeaderboardRow(leaderboard, index, "Points", eLDFType::LDF_TYPE_S32, entry.score);
// Points:1
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_S32, entry.placement);
// RowNumber:1
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, 0);
// Time:1
WriteLeaderboardRow(leaderboard, index, "Points", eLDFType::LDF_TYPE_S32, entry.score);
// Points:1
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, entry.time);
// Time:1
break;
case Type::SurvivalNS:
WriteLeaderboardRow(leaderboard, index, "RowNumber", eLDFType::LDF_TYPE_U64, entry.placement);
// RowNumber:8
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, entry.time);
// Time:1
WriteLeaderboardRow(leaderboard, index, "Wave", eLDFType::LDF_TYPE_S32, entry.score);
// Wave:1
WriteLeaderboardRow(leaderboard, index, "Time", eLDFType::LDF_TYPE_S32, entry.time);
// Time:1
WriteLeaderboardRow(leaderboard, index, "Wave", eLDFType::LDF_TYPE_S32, entry.score);
// Wave:1
break;
case Type::Donations:
// Something? idk yet.
WriteLeaderboardRow(leaderboard, index, "Score", eLDFType::LDF_TYPE_S32, entry.score);
// Score:1
// Something? idk yet.
break;
case Type::None:
// This type is included here simply to resolve a compiler warning on mac about unused enum types
break;
default:
break;

View File

@ -37,7 +37,7 @@ public:
Racing,
MonumentRace,
FootRace,
// There is no 4 defined anywhere in the client or cdclient
UnusedLeaderboard4,// There is no 4 defined anywhere in the client or cdclient
Survival = 5,
SurvivalNS,
Donations,

View File

@ -150,12 +150,6 @@ int main(int argc, char** argv) {
const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite");
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 (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.
@ -208,6 +202,9 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
// This migration relies on cdclient data so run it last
MigrationRunner::MigrateLeaderboard();
//If the first command line argument is -a or --account then make the user
//input a username and password, with the password being hidden.
if (argc > 1 &&

View File

@ -0,0 +1 @@
# This migration is a mock. See the real migration in MigrationRunner::MigrateLeaderboard.

View File

@ -0,0 +1,8 @@
ALTER TABLE leaderboard
ADD COLUMN hitPercentage FLOAT NOT NULL DEFAULT 0,
ADD COLUMN streak INT NOT NULL DEFAULT 0,
ADD COLUMN bestLapTime FLOAT NOT NULL DEFAULT 0,
ADD COLUMN numWins INT NOT NULL DEFAULT 0,
MODIFY time FLOAT NOT NULL DEFAULT 0;
ALTER TABLE leaderboard CHANGE time bestTime float;

View File

@ -32,6 +32,15 @@ protected:
bitStream.Reset();
}
void RunTests(Leaderboard::Type type) {
Game::logger->Log("LeaderboardTests", "Testing leaderboard %i for Serialize speed", type);
Leaderboard leaderboard(0, Leaderboard::InfoType::Top, false, type);
TestLeaderboard(leaderboard, 1);
TestLeaderboard(leaderboard, 10);
TestLeaderboard(leaderboard, 100);
TestLeaderboard(leaderboard, 1000);
}
CBITSTREAM;
};
@ -68,9 +77,5 @@ protected:
*/
TEST_F(LeaderboardTests, LeaderboardSpeedTest) {
Leaderboard leaderboard(10, Leaderboard::InfoType::MyStanding, false, Leaderboard::Type::Survival);
TestLeaderboard(leaderboard, 1);
TestLeaderboard(leaderboard, 10);
TestLeaderboard(leaderboard, 100);
TestLeaderboard(leaderboard, 1000);
RunTests(Leaderboard::Type::Racing);
}