From efaf38b8ed4c1ee3b1396bb045b2a45515477adb Mon Sep 17 00:00:00 2001 From: Crash Cringle <30600688+CrashCringle12@users.noreply.github.com> Date: Mon, 20 Feb 2023 08:47:30 -0500 Subject: [PATCH] Fix for "10th score" leaderboard bug, Reduce load from GAMESTATE::StoreRankingName() - Fixes bug where 10th scores get erased when two players play the same song multiple times and rank on the leaderboard more than once - Only clamping leaderboards and deduping high scores for songs/courses once overall instead of once per player - Instead of looping through every song and course on the machine multiple times -> Only loop through the songs/course played in the past session --- src/GameState.cpp | 85 +++++++++++++++++++++++++++++++++++++---------- src/GameState.h | 1 + 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/GameState.cpp b/src/GameState.cpp index 98ff476107..334eaa724e 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -579,6 +579,7 @@ void GameState::BeginGame() m_timeGameStarted.Touch(); m_vpsNamesThatWereFilled.clear(); + m_sPlayersThatWereFilled.clear(); // Play attract on the ending screen, then on the ranking screen // even if attract sounds are set to off. @@ -2349,29 +2350,77 @@ void GameState::StoreRankingName( PlayerNumber pn, RString sName ) m_vpsNamesThatWereFilled.push_back( aFeats[i].pStringToFill ); } + m_sPlayersThatWereFilled.insert(pn); - Profile *pProfile = PROFILEMAN->GetMachineProfile(); + // Only attempt to remove/clamp scores after the last enabled player has saved their scores. + if (GAMESTATE->GetNumPlayersEnabled() <= static_cast(m_sPlayersThatWereFilled.size())) { + StepsType st = GetCurrentStyle(pn)->m_StepsType; + PlayMode mode = m_PlayMode.Get(); + Profile *pProfile = PROFILEMAN->GetMachineProfile(); + switch (mode) + { + case PLAY_MODE_REGULAR: + case PLAY_MODE_BATTLE: + case PLAY_MODE_RAVE: + { + // Find unique Song and Steps combinations that were played. + // Code is taken from implementation in GetRankingFeats() + std::vector vSongAndSteps; - if( !PREFSMAN->m_bAllowMultipleHighScoreWithSameName ) - { - // erase all but the highest score for each name - for (auto &songIter : pProfile->m_SongHighScores) - for (auto &stepIter : songIter.second.m_StepsHighScores) - stepIter.second.hsl.RemoveAllButOneOfEachName(); + for( unsigned i=0; im_vPlayedStageStats.size(); i++ ) + { + CHECKPOINT_M( ssprintf("%u/%i", i, (int)STATSMAN->m_vPlayedStageStats.size() ) ); + SongAndSteps sas; + ASSERT( !STATSMAN->m_vPlayedStageStats[i].m_vpPlayedSongs.empty() ); + sas.pSong = STATSMAN->m_vPlayedStageStats[i].m_vpPlayedSongs[0]; + ASSERT( sas.pSong != nullptr ); - for (auto &courseIter : pProfile->m_CourseHighScores) - for (auto &trailIter : courseIter.second.m_TrailHighScores) - trailIter.second.hsl.RemoveAllButOneOfEachName(); + if ( STATSMAN->m_vPlayedStageStats[i].m_player[pn].m_vpPossibleSteps.empty() ) + continue; + sas.pSteps = STATSMAN->m_vPlayedStageStats[i].m_player[pn].m_vpPossibleSteps[0]; + ASSERT( sas.pSteps != nullptr ); + vSongAndSteps.push_back( sas ); + } + + std::vector::iterator toDelete = std::unique( vSongAndSteps.begin(), vSongAndSteps.end() ); + vSongAndSteps.erase(toDelete, vSongAndSteps.end()); + + for (unsigned i = 0; i < vSongAndSteps.size(); ++i) + { + HighScoreList &hsl = pProfile->GetStepsHighScoreList(vSongAndSteps[i].pSong,vSongAndSteps[i].pSteps); + if (!PREFSMAN->m_bAllowMultipleHighScoreWithSameName) + { + // erase all but the highest score for each name + hsl.RemoveAllButOneOfEachName(); + } + hsl.ClampSize(true); + } + } + break; + case PLAY_MODE_NONSTOP: + case PLAY_MODE_ONI: + case PLAY_MODE_ENDLESS: + { + // Code is taken from implementation in GetRankingFeats() + Course* pCourse = m_pCurCourse; + ASSERT( pCourse != nullptr ); + Trail *pTrail = m_pCurTrail[pn]; + ASSERT( pTrail != nullptr ); + CourseDifficulty cd = pTrail->m_CourseDifficulty; + HighScoreList &hsl = pProfile->GetCourseHighScoreList( pCourse, pTrail ); + if (!PREFSMAN->m_bAllowMultipleHighScoreWithSameName) + { + // erase all but the highest score for each name + hsl.RemoveAllButOneOfEachName(); + } + hsl.ClampSize(true); + } + break; + default: + FAIL_M(ssprintf("Invalid play mode: %i", int(m_PlayMode))); + } } - // clamp high score sizes - for (auto &songIter : pProfile->m_SongHighScores) - for (auto &stepIter : songIter.second.m_StepsHighScores) - stepIter.second.hsl.ClampSize( true ); - - for (auto &courseIter : pProfile->m_CourseHighScores) - for (auto &trailIter : courseIter.second.m_TrailHighScores) - trailIter.second.hsl.ClampSize( true ); } bool GameState::AllAreInDangerOrWorse() const diff --git a/src/GameState.h b/src/GameState.h index 8e2a3dea08..040b298e63 100644 --- a/src/GameState.h +++ b/src/GameState.h @@ -357,6 +357,7 @@ public: bool AnyPlayerHasRankingFeats() const; void StoreRankingName( PlayerNumber pn, RString name ); // Called by name entry screens std::vector m_vpsNamesThatWereFilled; // filled on StoreRankingName, + std::set m_sPlayersThatWereFilled; // filled on StoreRankingName, // Award stuff // lowest priority in front, highest priority at the back.