From 92fa46d3b102abc8b405c0dec530da210b80bf32 Mon Sep 17 00:00:00 2001 From: "born a rick, raised a morty, died a jerry" Date: Wed, 1 Jul 2020 14:34:14 -0400 Subject: [PATCH] add function to manually trigger a forced recalc of all loaded scores --- .../ScreenSelectMusic decorations/profile.lua | 22 +++- Themes/Til Death/Languages/en.ini | 1 + Themes/Til Death/Languages/zh.ini | 3 +- src/Etterna/Models/Misc/Profile.cpp | 7 ++ src/Etterna/Singletons/ScoreManager.cpp | 115 +++++++++++++++++- src/Etterna/Singletons/ScoreManager.h | 1 + src/RageUtil/Misc/RageThreads.h | 28 +++++ 7 files changed, 173 insertions(+), 4 deletions(-) diff --git a/Themes/Til Death/BGAnimations/ScreenSelectMusic decorations/profile.lua b/Themes/Til Death/BGAnimations/ScreenSelectMusic decorations/profile.lua index 51d022dd8c..145794121c 100644 --- a/Themes/Til Death/BGAnimations/ScreenSelectMusic decorations/profile.lua +++ b/Themes/Til Death/BGAnimations/ScreenSelectMusic decorations/profile.lua @@ -17,7 +17,8 @@ local translated_info = { AssetSettings = THEME:GetString("TabProfile", "AssetSettingEntry"), Success = THEME:GetString("TabProfile", "SaveSuccess"), Failure = THEME:GetString("TabProfile", "SaveFail"), - ValidateAll = THEME:GetString("TabProfile", "ValidateAllScores") + ValidateAll = THEME:GetString("TabProfile", "ValidateAllScores"), + ForceRecalc = THEME:GetString("TabProfile", "ForceRecalcScores"), } local t = @@ -743,7 +744,24 @@ local profilebuttons = STATSMAN:UpdatePlayerRating() end end - } + }, + LoadFont("Common Large") .. + { + InitCommand = function(self) + self:x(300):diffuse(getMainColor("positive")):settext(translated_info["ForceRecalc"]):zoom(0.3) + end + }, + Def.Quad { + InitCommand = function(self) + self:x(300):zoomto(100, 20):diffusealpha(buttondiffuse) + end, + MouseLeftClickMessageCommand = function(self) + if ButtonActive(self) and rankingSkillset == 1 then + ms.ok("Recalculating Scores... this might be slow and may or may not crash") + profile:ForceRecalcScores() + end + end +} } t[#t + 1] = profilebuttons diff --git a/Themes/Til Death/Languages/en.ini b/Themes/Til Death/Languages/en.ini index ce42617ef7..1b1b303d44 100644 --- a/Themes/Til Death/Languages/en.ini +++ b/Themes/Til Death/Languages/en.ini @@ -577,6 +577,7 @@ SaveSuccess=Save Successful SaveFail=Save Failed AssetSettingEntry=Asset Settings ValidateAllScores=Validate All +ForceRecalcScores=Recalc Scores [TabFilter] Title=Filters (WIP) diff --git a/Themes/Til Death/Languages/zh.ini b/Themes/Til Death/Languages/zh.ini index ee43b261a5..36b03c0baf 100644 --- a/Themes/Til Death/Languages/zh.ini +++ b/Themes/Til Death/Languages/zh.ini @@ -1,4 +1,4 @@ -;Etterna Til Death主題繁體中文化檔案 Ver 0.66.1 +;Etterna Til Death主題繁體中文化檔案 Ver 0.66.1 ;譯者雨幕幕, 有問題或建議請加Q695312637或discord找我 [Common] @@ -562,6 +562,7 @@ SaveSuccess=保存成功 SaveFail=保存失敗 AssetSettingEntry=個人偏好設置 ValidateAllScores=全部認證 +ForceRecalcScores=重新計算分數 [TabFilter] Title=歌曲篩選 (待完成) diff --git a/src/Etterna/Models/Misc/Profile.cpp b/src/Etterna/Models/Misc/Profile.cpp index 6a45782113..9fb1770100 100644 --- a/src/Etterna/Models/Misc/Profile.cpp +++ b/src/Etterna/Models/Misc/Profile.cpp @@ -1762,6 +1762,12 @@ class LunaProfile : public Luna return 1; } + static int ForceRecalcScores(T* p, lua_State* L) + { + SCOREMAN->RecalculateSSRs(p->m_sProfileID); + return 0; + } + LunaProfile() { ADD_METHOD(AddScreenshot); @@ -1815,6 +1821,7 @@ class LunaProfile : public Luna ADD_METHOD(SortByDiff); ADD_METHOD(ToggleFilter); ADD_METHOD(GetFilterMode); + ADD_METHOD(ForceRecalcScores); } }; diff --git a/src/Etterna/Singletons/ScoreManager.cpp b/src/Etterna/Singletons/ScoreManager.cpp index cf79d65221..ff5bdf1ca5 100644 --- a/src/Etterna/Singletons/ScoreManager.cpp +++ b/src/Etterna/Singletons/ScoreManager.cpp @@ -565,7 +565,120 @@ ScoreManager::RecalculateSSRs(LoadingWindow* ld, const string& profileID) return; } -// should deal with this misnomer - mina +void +ScoreManager::RecalculateSSRs(const string& profileID) +{ + auto& scores = SCOREMAN->GetAllScores(); + + mutex songVectorPtrMutex; + vector currentlyLockedSongs; + // This is meant to ensure mutual exclusion for a song + class SongLock + { + public: + mutex& songVectorPtrMutex; // This mutex guards the vector + vector& + currentlyLockedSongs; // Vector of currently locked songs + std::uintptr_t song; // The song for this lock + SongLock(vector& vec, mutex& mut, std::uintptr_t k) + : currentlyLockedSongs(vec) + , songVectorPtrMutex(mut) + , song(k) + { + bool active = true; + { + lock_guard lk(songVectorPtrMutex); + active = find(currentlyLockedSongs.begin(), + currentlyLockedSongs.end(), + song) != currentlyLockedSongs.end(); + if (!active) + currentlyLockedSongs.emplace_back(song); + } + while (active) { + // TODO: Try to make this wake up from the destructor (CondVar's + // maybe) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + { + lock_guard lk(songVectorPtrMutex); + active = find(currentlyLockedSongs.begin(), + currentlyLockedSongs.end(), + song) != currentlyLockedSongs.end(); + if (!active) + currentlyLockedSongs.emplace_back(song); + } + } + } + ~SongLock() + { + lock_guard lk(songVectorPtrMutex); + currentlyLockedSongs.erase(find( + currentlyLockedSongs.begin(), currentlyLockedSongs.end(), song)); + } + }; + function, vectorIt>, + ThreadData*)> + callback = + [&songVectorPtrMutex, ¤tlyLockedSongs]( + std::pair, vectorIt> workload, + ThreadData* data) { + std::unique_ptr per_thread_calc = std::make_unique(); + + int scoreIndex = 0; + for (auto it = workload.first; it != workload.second; it++) { + auto hs = *it; + ++scoreIndex; + + const string& ck = hs->GetChartKey(); + Steps* steps = SONGMAN->GetStepsByChartkey(ck); + + // check for unloaded steps, only allow 4k + if (steps == nullptr || + steps->m_StepsType != StepsType_dance_single) + continue; + + float ssrpercent = hs->GetSSRNormPercent(); + + // don't waste time on <= 0%s + if (ssrpercent <= 0.f || !steps->IsRecalcValid()) { + hs->ResetSkillsets(); + continue; + } + SongLock lk(currentlyLockedSongs, + songVectorPtrMutex, + reinterpret_cast(steps->m_pSong)); + + float musicrate = hs->GetMusicRate(); + + TimingData* td = steps->GetTimingData(); + NoteData nd; + steps->GetNoteData(nd); + + const auto& serializednd = nd.SerializeNoteData2(td); + vector dakine; + + if (steps->m_StepsType == StepsType_dance_single) { + dakine = MinaSDCalc(serializednd, + musicrate, + ssrpercent, + per_thread_calc.get()); + } + + auto ssrVals = dakine; + FOREACH_ENUM(Skillset, ss) + hs->SetSkillsetSSR(ss, ssrVals[ss]); + hs->SetSSRCalcVersion(GetCalcVersion()); + + td->UnsetEtaner(); + nd.UnsetNerv(); + nd.UnsetSerializedNoteData(); + steps->Compress(); + } + }; + + parallelExecution(scores, callback); + return; +} + void ScoreManager::UnInvalidateAllScores() { diff --git a/src/Etterna/Singletons/ScoreManager.h b/src/Etterna/Singletons/ScoreManager.h index 2887e27fdd..d8c8c71b45 100644 --- a/src/Etterna/Singletons/ScoreManager.h +++ b/src/Etterna/Singletons/ScoreManager.h @@ -143,6 +143,7 @@ class ScoreManager Skillset ss, const string& profileID = PROFILEMAN->GetProfile(PLAYER_1)->m_sProfileID); void RecalculateSSRs(LoadingWindow* ld, const string& profileID); + void RecalculateSSRs(const string& profileID); void UnInvalidateAllScores(); void CalcPlayerRating(float& prating, float* pskillsets, diff --git a/src/RageUtil/Misc/RageThreads.h b/src/RageUtil/Misc/RageThreads.h index e3bf63183d..eba9b77b13 100644 --- a/src/RageUtil/Misc/RageThreads.h +++ b/src/RageUtil/Misc/RageThreads.h @@ -109,6 +109,34 @@ parallelExecution(vector vec, parallelExecution(vec, update, exec, nullptr); } +template +void +parallelExecution(vector vec, + function, ThreadData*)> exec) +{ + const int THREADS = PREFSMAN->ThreadsToUse <= 0 + ? std::thread::hardware_concurrency() + : min((int)PREFSMAN->ThreadsToUse, + (int)std::thread::hardware_concurrency()); + std::vector> workloads = + splitWorkLoad(vec, static_cast(vec.size() / THREADS)); + ThreadData data; + auto threadCallback = [&data, &exec](vectorRange workload) { + exec(workload, &data); + data._threadsFinished++; + data.setUpdated(true); + }; + vector threadpool; + for (auto& workload : workloads) + threadpool.emplace_back(thread(threadCallback, workload)); + while (data._threadsFinished < (int)workloads.size()) { + data.waitForUpdate(); + data.setUpdated(false); + } + for (auto& thread : threadpool) + thread.join(); +} + struct ThreadSlot; class RageTimer;