diff --git a/dCommon/Metrics.cpp b/dCommon/Metrics.cpp index b97b5435..522bcf61 100644 --- a/dCommon/Metrics.cpp +++ b/dCommon/Metrics.cpp @@ -14,6 +14,7 @@ std::vector Metrics::m_Variables = { MetricVariable::CPUTime, MetricVariable::Sleep, MetricVariable::Frame, + MetricVariable::Leaderboard, }; void Metrics::AddMeasurement(MetricVariable variable, int64_t value) { diff --git a/dCommon/Metrics.hpp b/dCommon/Metrics.hpp index c03c914f..7009701b 100644 --- a/dCommon/Metrics.hpp +++ b/dCommon/Metrics.hpp @@ -20,6 +20,7 @@ enum class MetricVariable : int32_t CPUTime, Sleep, Frame, + Leaderboard, }; struct Metric diff --git a/dGame/LeaderboardManager.cpp b/dGame/LeaderboardManager.cpp index 72101e64..9388b183 100644 --- a/dGame/LeaderboardManager.cpp +++ b/dGame/LeaderboardManager.cpp @@ -10,9 +10,10 @@ #include "CDClientManager.h" #include "GeneralUtils.h" #include "Entity.h" +#include #include "CDActivitiesTable.h" - +#include "Metrics.hpp" Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const Leaderboard::Type leaderboardType) { this->relatedPlayer = relatedPlayer; this->gameID = gameID; @@ -22,39 +23,35 @@ Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoTy this->leaderboardType = leaderboardType; } -bool Leaderboard::IsScoreBetter(const uint32_t score) const { - -} - void Leaderboard::Serialize(RakNet::BitStream* bitStream) const { - std::string leaderboard; + std::ostringstream leaderboard; - leaderboard += "ADO.Result=7:1\n"; - leaderboard += "Result.Count=1:1\n"; - leaderboard += "Result[0].Index=0:RowNumber\n"; - leaderboard += "Result[0].RowCount=1:" + std::to_string(entries.size()) + "\n"; + leaderboard << "ADO.Result=7:1\n"; // Unused in 1.10.64, but is in captures + leaderboard << "Result.Count=1:1\n"; // number of results, always 1? + leaderboard << "Result[0].Index=0:RowNumber\n"; // "Primary key" + leaderboard << "Result[0].RowCount=1:" << entries.size() << '\n'; // number of rows auto index = 0; for (const auto& entry : entries) { - leaderboard += "Result[0].Row[" + std::to_string(index) + "].LastPlayed=8:" + std::to_string(entry.lastPlayed) + "\n"; - leaderboard += "Result[0].Row[" + std::to_string(index) + "].CharacterID=8:" + std::to_string(entry.playerID) + "\n"; - leaderboard += "Result[0].Row[" + std::to_string(index) + "].NumPlayed=1:1\n"; - leaderboard += "Result[0].Row[" + std::to_string(index) + "].RowNumber=8:" + std::to_string(entry.placement) + "\n"; - leaderboard += "Result[0].Row[" + std::to_string(index) + "].Time=1:" + std::to_string(entry.time) + "\n"; + leaderboard << "Result[0].Row[" << index << "].LastPlayed=8:" << entry.lastPlayed << '\n'; + leaderboard << "Result[0].Row[" << index << "].CharacterID=8:" << entry.playerID << '\n'; + leaderboard << "Result[0].Row[" << index << "].NumPlayed=1:1\n"; // number of times the activity was played + leaderboard << "Result[0].Row[" << index << "].RowNumber=8:" << entry.placement << '\n'; + leaderboard << "Result[0].Row[" << index << "].Time=1:" << entry.time << '\n'; // Only these minigames have a points system if (leaderboardType == Survival || leaderboardType == ShootingGallery) { - leaderboard += "Result[0].Row[" + std::to_string(index) + "].Points=1:" + std::to_string(entry.score) + "\n"; + leaderboard << "Result[0].Row[" << index << "].Points=1:"<< entry.score << '\n'; } else if (leaderboardType == SurvivalNS) { - leaderboard += "Result[0].Row[" + std::to_string(index) + "].Wave=1:" + std::to_string(entry.score) + "\n"; + leaderboard << "Result[0].Row[" << index << "].Wave=1:"<< entry.score << '\n'; } - leaderboard += "Result[0].Row[" + std::to_string(index) + "].name=0:" + entry.playerName + "\n"; + leaderboard << "Result[0].Row[" << index << "].name=0:" << entry.playerName << '\n'; index++; } // Serialize the thing to a BitStream - bitStream->WriteAlignedBytes((const unsigned char*)leaderboard.c_str(), leaderboard.size()); + bitStream->Write(leaderboard.str().c_str(), leaderboard.tellp()); } void Leaderboard::SetupLeaderboard() { diff --git a/dGame/LeaderboardManager.h b/dGame/LeaderboardManager.h index 5f3fa0b2..bbdf76e5 100644 --- a/dGame/LeaderboardManager.h +++ b/dGame/LeaderboardManager.h @@ -1,6 +1,7 @@ #pragma once -#include #include +#include +#include #include "Singleton.h" #include "dCommonVars.h" @@ -14,7 +15,7 @@ typedef uint32_t GameID; class Leaderboard { public: struct Entry { - uint64_t playerID; + LWOOBJID playerID; uint32_t time; uint32_t score; uint32_t placement; @@ -56,7 +57,7 @@ public: * @return true * @return false */ - bool IsScoreBetter(const uint32_t score) const; + bool IsScoreBetter(const uint32_t score) const { return false; }; /** * Builds the leaderboard from the database based on the associated gameID @@ -67,6 +68,12 @@ public: * Sends the leaderboard to the client specified by targetID. */ void Send(LWOOBJID targetID) const; + + /** + * Adds a new entry to the leaderboard + * Used for debug only! + */ + void AddEntry(Entry entry) { entries.push_back(entry); } private: LeaderboardEntries entries; LWOOBJID relatedPlayer; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 0fc32e2e..df58e8f1 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1630,17 +1630,17 @@ void GameMessages::SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, bitStream.Write(objectID); bitStream.Write(GAME_MSG::GAME_MSG_SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA); - - bitStream.Write(leaderboard->GetGameID()); - bitStream.Write(leaderboard->GetInfoType()); + throw ""; + //bitStream.Write(leaderboard->GetGameID()); + //bitStream.Write(leaderboard->GetInfoType()); // Leaderboard is written back as LDF string - const auto leaderboardString = leaderboard->ToString(); - bitStream.Write(leaderboardString.size()); - for (const auto c : leaderboardString) { - bitStream.Write(c); - } - if (!leaderboardString.empty()) bitStream.Write(uint16_t(0)); + //const auto leaderboardString = leaderboard->ToString(); + //bitStream.Write(leaderboardString.size()); + //for (const auto c : leaderboardString) { + // bitStream.Write(c); + //} + //if (!leaderboardString.empty()) bitStream.Write(uint16_t(0)); bitStream.Write0(); bitStream.Write0(); @@ -1666,9 +1666,9 @@ void GameMessages::HandleRequestActivitySummaryLeaderboardData(RakNet::BitStream bool weekly = inStream->ReadBit(); - const auto* leaderboard = LeaderboardManager::GetLeaderboard(gameID, (InfoType)queryType, weekly, entity->GetObjectID()); - SendActivitySummaryLeaderboardData(entity->GetObjectID(), leaderboard, sysAddr); - delete leaderboard; + //const auto* leaderboard = LeaderboardManager::GetLeaderboard(gameID, (InfoType)queryType, weekly, entity->GetObjectID()); + //SendActivitySummaryLeaderboardData(entity->GetObjectID(), leaderboard, sysAddr); + //delete leaderboard; } void GameMessages::HandleActivityStateChangeRequest(RakNet::BitStream* inStream, Entity* entity) { diff --git a/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp b/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp index d2cc647e..7da4ccef 100644 --- a/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp +++ b/dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp @@ -96,8 +96,8 @@ void NpcAgCourseStarter::OnFireEventServerSide(Entity* self, Entity* sender, std } EntityManager::Instance()->SerializeEntity(self); - LeaderboardManager::SaveScore(sender->GetObjectID(), scriptedActivityComponent->GetActivityID(), - 0, (uint32_t)finish); + //LeaderboardManager::SaveScore(sender->GetObjectID(), scriptedActivityComponent->GetActivityID(), + // 0, (uint32_t)finish); GameMessages::SendNotifyClientObject(self->GetObjectID(), u"ToggleLeaderBoard", scriptedActivityComponent->GetActivityID(), 0, sender->GetObjectID(), diff --git a/dScripts/ActivityManager.cpp b/dScripts/ActivityManager.cpp index 078a7a02..acfa5a68 100644 --- a/dScripts/ActivityManager.cpp +++ b/dScripts/ActivityManager.cpp @@ -74,11 +74,11 @@ void ActivityManager::StopActivity(Entity* self, const LWOOBJID playerID, const LootGenerator::Instance().GiveActivityLoot(player, self, gameID, CalculateActivityRating(self, playerID)); // Save the new score to the leaderboard and show the leaderboard to the player - LeaderboardManager::SaveScore(playerID, gameID, score, value1); - const auto* leaderboard = LeaderboardManager::GetLeaderboard(gameID, InfoType::Standings, - false, player->GetObjectID()); - GameMessages::SendActivitySummaryLeaderboardData(self->GetObjectID(), leaderboard, player->GetSystemAddress()); - delete leaderboard; + //LeaderboardManager::SaveScore(playerID, gameID, score, value1); + //const auto* leaderboard = LeaderboardManager::GetLeaderboard(gameID, InfoType::Standings, + // false, player->GetObjectID()); + //GameMessages::SendActivitySummaryLeaderboardData(self->GetObjectID(), leaderboard, player->GetSystemAddress()); + //delete leaderboard; // Makes the leaderboard show up for the player GameMessages::SendNotifyClientObject(self->GetObjectID(), u"ToggleLeaderBoard", @@ -117,7 +117,7 @@ uint32_t ActivityManager::GetActivityID(const Entity* self) { } void ActivityManager::GetLeaderboardData(Entity* self, const LWOOBJID playerID, const uint32_t activityID, uint32_t numResults) { - LeaderboardManager::SendLeaderboard(activityID, Standings, false, self->GetObjectID(), playerID); + //LeaderboardManager::SendLeaderboard(activityID, Standings, false, self->GetObjectID(), playerID); } void ActivityManager::ActivityTimerStart(Entity* self, const std::string& timerName, const float_t updateInterval, diff --git a/tests/dGameTests/CMakeLists.txt b/tests/dGameTests/CMakeLists.txt index b1fdaa07..30b5e20b 100644 --- a/tests/dGameTests/CMakeLists.txt +++ b/tests/dGameTests/CMakeLists.txt @@ -1,5 +1,6 @@ set(DGAMETEST_SOURCES "GameDependencies.cpp" + "LeaderboardTests.cpp" ) add_subdirectory(dComponentsTests) diff --git a/tests/dGameTests/LeaderboardTests.cpp b/tests/dGameTests/LeaderboardTests.cpp new file mode 100644 index 00000000..6a45298b --- /dev/null +++ b/tests/dGameTests/LeaderboardTests.cpp @@ -0,0 +1,76 @@ +#include "LeaderboardManager.h" + +#include "BitStream.h" +#include "GameDependencies.h" +#include "Metrics.hpp" +#include + +class LeaderboardTests : public GameDependenciesTest { +protected: + void SetUp() override { + SetUpDependencies(); + } + void TearDown() override { + TearDownDependencies(); + } + + void TestLeaderboard(Leaderboard& leaderboard, int32_t entries) { + Leaderboard::Entry entry; + entry.playerID = UINT64_MAX; + entry.time = 100; + entry.score = 100; + entry.placement = 1; + entry.lastPlayed = 0; + entry.playerName = "TestThreeWords"; + for (int32_t i = 0; i < entries; i++) leaderboard.AddEntry(entry); + Metrics::StartMeasurement(MetricVariable::Leaderboard); + for (int32_t i = 0; i < MAX_MEASURMENT_POINTS; i++) leaderboard.Serialize(&bitStream); + Metrics::EndMeasurement(MetricVariable::Leaderboard); + + auto timePassed = Metrics::GetMetric(MetricVariable::Leaderboard)->average; + Game::logger->Log("LeaderboardManager", "average time passed for %i leaderboard entries is %lluns", entries, timePassed); + bitStream.Reset(); + } + + CBITSTREAM; +}; + +/** + * Initial metrics + * 19: [12-04-23 23:56:31] [LeaderboardManager] average time passed for 1 leaderboard entries is 1671700ns + * 19: [12-04-23 23:56:31] [LeaderboardManager] average time passed for 10 leaderboard entries is 8388900ns + * 19: [12-04-23 23:56:31] [LeaderboardManager] average time passed for 100 leaderboard entries is 54680133ns + * 19: [12-04-23 23:56:33] [LeaderboardManager] average time passed for 1000 leaderboard entries is 506289325ns + + * Only do each std::to_string once + * 19: [12-04-23 23:57:31] [LeaderboardManager] average time passed for 1 leaderboard entries is 1472700ns + * 19: [12-04-23 23:57:31] [LeaderboardManager] average time passed for 10 leaderboard entries is 7035650ns + * 19: [12-04-23 23:57:31] [LeaderboardManager] average time passed for 100 leaderboard entries is 45147466ns + * 19: [12-04-23 23:57:33] [LeaderboardManager] average time passed for 1000 leaderboard entries is 435724550ns + * + * Only do Result[0].Row[index] once + * 19: [12-04-23 23:59:43] [LeaderboardManager] average time passed for 1 leaderboard entries is 1357700ns + * 19: [12-04-23 23:59:43] [LeaderboardManager] average time passed for 10 leaderboard entries is 6635350ns + * 19: [12-04-23 23:59:43] [LeaderboardManager] average time passed for 100 leaderboard entries is 40247800ns + * 19: [12-04-23 23:59:45] [LeaderboardManager] average time passed for 1000 leaderboard entries is 400965900ns + * + * Switch to ostringstream + * 19: [13-04-23 00:24:44] [LeaderboardManager] average time passed for 1 leaderboard entries is 1334300ns + * 19: [13-04-23 00:24:44] [LeaderboardManager] average time passed for 10 leaderboard entries is 5566250ns + * 19: [13-04-23 00:24:44] [LeaderboardManager] average time passed for 100 leaderboard entries is 34640066ns + * 19: [13-04-23 00:24:46] [LeaderboardManager] average time passed for 1000 leaderboard entries is 357226950ns + * + * No more std::to_string and revert "Only do Result[0].Row[index] once" + * 19: [13-04-23 00:39:18] [LeaderboardManager] average time passed for 1 leaderboard entries is 979200ns + * 19: [13-04-23 00:39:18] [LeaderboardManager] average time passed for 10 leaderboard entries is 4053350ns + * 19: [13-04-23 00:39:18] [LeaderboardManager] average time passed for 100 leaderboard entries is 24785233ns + * 19: [13-04-23 00:39:19] [LeaderboardManager] average time passed for 1000 leaderboard entries is 279457375ns + */ + +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); +}