From 7aa80079ae7c626fead02ae47daa15bdeea593ac Mon Sep 17 00:00:00 2001 From: Alex Uskov Date: Sat, 4 Jan 2025 13:11:21 +0400 Subject: [PATCH] WIP Adding presets support and refactoring --- include/FilterOptions.hpp | 320 +++++----------------- include/PluginConfig.hpp | 130 ++++++++- include/UI/ViewControllers/FilterView.hpp | 8 +- include/UI/ViewControllers/SongList.hpp | 20 +- include/Util/TextUtil.hpp | 3 + include/internal_macros.hpp | 1 - include/main.hpp | 4 +- src/FilterOptions.cpp | 167 +++++++++++ src/UI/Modals/Settings.cpp | 8 +- src/UI/ViewControllers/FilterView.cpp | 285 +++++++------------ src/UI/ViewControllers/SongList.cpp | 84 +++--- src/UI/ViewControllers/SongListCell.cpp | 4 +- src/Util/SongUtil.cpp | 12 +- src/Util/TextUtil.cpp | 11 + src/main.cpp | 52 +--- 15 files changed, 550 insertions(+), 559 deletions(-) delete mode 100644 include/internal_macros.hpp create mode 100644 src/FilterOptions.cpp diff --git a/include/FilterOptions.hpp b/include/FilterOptions.hpp index 525b23a..3ba9474 100644 --- a/include/FilterOptions.hpp +++ b/include/FilterOptions.hpp @@ -2,260 +2,70 @@ #include #include -#include "main.hpp" -#include "song-details/shared/Data/MapCharacteristic.hpp" -#include "song-details/shared/Data/MapDifficulty.hpp" -#include "song-details/shared/Data/RankedStates.hpp" -#include "song-details/shared/Data/MapMods.hpp" -#include "song-details/shared/Data/SongDifficulty.hpp" - - - -class FilterOptions -{ -public: - // FilterOptions(FilterOptions const&) = delete; // no accidental copying - // FilterOptions() = default; - - enum class DownloadFilterType - { - All, - OnlyDownloaded, - HideDownloaded - }; - enum class LocalScoreFilterType - { - All, - HidePassed, - OnlyPassed - }; - enum class RankedFilterType - { - ShowAll, - ScoreSaberRanked, - BeatLeaderRanked, - ScoreSaberQualified, - BeatLeaderQualified - }; - enum class DifficultyFilterType - { - All, - Easy, - Normal, - Hard, - Expert, - ExpertPlus - }; - enum class CharFilterType - { - All, - Custom, - Standard, - OneSaber, - NoArrows, - NinetyDegrees, - ThreeSixtyDegrees, - LightShow, - Lawless, - }; - - enum class RequirementType { - Any, - NoodleExtensions, - MappingExtensions, - Chroma, - Cinema, - None - }; - - static inline const float SONG_LENGTH_FILTER_MAX = 15.0f; - static inline const float STAR_FILTER_MAX = 18.0f; - static inline const float NJS_FILTER_MAX = 25.0f; - static inline const float NPS_FILTER_MAX = 12.0f; - static inline const int64_t BEATSAVER_EPOCH = 1525136400; - - //General - DownloadFilterType downloadType = DownloadFilterType::All; - LocalScoreFilterType localScoreType = LocalScoreFilterType::All; - float minLength = 0, maxLength = 900; - - //Mapping - float minNJS = 0, maxNJS = NJS_FILTER_MAX; - float minNPS = 0, maxNPS = NPS_FILTER_MAX; - - // Ranked - RankedFilterType rankedType = RankedFilterType::ShowAll; - float minStars = 0, maxStars = STAR_FILTER_MAX; - - //BeatSaver - int minUploadDate = BEATSAVER_EPOCH; - int minUploadDateInMonths = 0; - float minRating = 0; - int minVotes = 0; - std::vector uploaders; - bool uploadersBlackList = false; - - //Difficulty - DifficultyFilterType difficultyFilter = DifficultyFilterType::All; - CharFilterType charFilter = CharFilterType::All; - - //Mods - RequirementType modRequirement = RequirementType::Any; -}; - -enum class SortMode { - Newest, - Oldest, - Latest_Ranked, - Most_Stars, - Least_Stars, - Best_rated, - Worst_rated -}; - -enum class PreferredLeaderBoard { - ScoreSaber = 0, - BeatLeader = 1 -}; - - - -// Map for characteristics -static const std::unordered_map charMap = { - {FilterOptions::CharFilterType::Custom, SongDetailsCache::MapCharacteristic::Custom}, - {FilterOptions::CharFilterType::Standard, SongDetailsCache::MapCharacteristic::Standard}, - {FilterOptions::CharFilterType::OneSaber, SongDetailsCache::MapCharacteristic::OneSaber}, - {FilterOptions::CharFilterType::NoArrows, SongDetailsCache::MapCharacteristic::NoArrows}, - {FilterOptions::CharFilterType::NinetyDegrees, SongDetailsCache::MapCharacteristic::NinetyDegree}, - {FilterOptions::CharFilterType::ThreeSixtyDegrees, SongDetailsCache::MapCharacteristic::ThreeSixtyDegree}, - {FilterOptions::CharFilterType::LightShow, SongDetailsCache::MapCharacteristic::LightShow}, - {FilterOptions::CharFilterType::Lawless, SongDetailsCache::MapCharacteristic::Lawless} -}; -// Map for difficulties -static const std::unordered_map diffMap = { - {FilterOptions::DifficultyFilterType::Easy, SongDetailsCache::MapDifficulty::Easy}, - {FilterOptions::DifficultyFilterType::Normal, SongDetailsCache::MapDifficulty::Normal}, - {FilterOptions::DifficultyFilterType::Hard, SongDetailsCache::MapDifficulty::Hard}, - {FilterOptions::DifficultyFilterType::Expert, SongDetailsCache::MapDifficulty::Expert}, - {FilterOptions::DifficultyFilterType::ExpertPlus, SongDetailsCache::MapDifficulty::ExpertPlus} -}; - - -// Map for ranked states -static const std::unordered_map rankMap = { - {FilterOptions::RankedFilterType::ScoreSaberRanked, SongDetailsCache::RankedStates::ScoresaberRanked}, - {FilterOptions::RankedFilterType::BeatLeaderRanked, SongDetailsCache::RankedStates::BeatleaderRanked}, - {FilterOptions::RankedFilterType::ScoreSaberQualified, SongDetailsCache::RankedStates::ScoresaberQualified}, - {FilterOptions::RankedFilterType::BeatLeaderQualified, SongDetailsCache::RankedStates::BeatleaderQualified} -}; - -// Map for preferred leaderboard -static const std::unordered_map leaderBoardMap = { - {"Scoresaber", PreferredLeaderBoard::ScoreSaber}, - {"Beatleader", PreferredLeaderBoard::BeatLeader} -}; - -class FilterOptionsCache -{ -public: - FilterOptionsCache(FilterOptionsCache const&) = delete; // no accidental copying - FilterOptionsCache() = default; - - void cache(FilterOptions s){ - downloadType=s.downloadType; - localScoreType=s.localScoreType; - minLength=s.minLength; - if (s.maxLength / 60 >= FilterOptions::SONG_LENGTH_FILTER_MAX) { maxLength=std::numeric_limits::infinity(); } else { maxLength=s.maxLength;} - minNJS=s.minNJS; - if (s.maxNJS >= FilterOptions::NJS_FILTER_MAX) { maxNJS=std::numeric_limits::infinity(); } else { maxNJS=s.maxNJS;} - minNPS=s.minNPS; - if (s.maxNPS >= FilterOptions::NPS_FILTER_MAX) { maxNPS=std::numeric_limits::infinity(); } else { maxNPS=s.maxNPS;} - rankedType=s.rankedType; - minStars=s.minStars; - if (s.maxStars >= FilterOptions::STAR_FILTER_MAX) { maxStars=std::numeric_limits::infinity(); } else { maxStars=s.maxStars;} - minUploadDate=s.minUploadDate; - minRating=s.minRating; - minVotes=s.minVotes; - uploaders=s.uploaders; - uploadersBlackList=s.uploadersBlackList; - difficultyFilter=s.difficultyFilter; - charFilter=s.charFilter; - modRequirement=s.modRequirement; - - // Process char filter - if (s.charFilter != FilterOptions::CharFilterType::All) { - charFilterPreprocessed = charMap.at(s.charFilter); - }; - - // Map difficulty filter - if (s.difficultyFilter != FilterOptions::DifficultyFilterType::All) { - difficultyFilterPreprocessed = diffMap.at(s.difficultyFilter); - }; - - // Check if filter is needed at all - skipFilter = false; - skipFilter = ( - downloadType == FilterOptions::DownloadFilterType::All && - localScoreType == FilterOptions::LocalScoreFilterType::All && - (s.maxLength / 60 >= FilterOptions::SONG_LENGTH_FILTER_MAX) && - (s.minLength == 0) && - minNJS == 0 && - s.maxNJS >= FilterOptions::NJS_FILTER_MAX && - s.minNPS == 0 && - s.maxNPS >= FilterOptions::NPS_FILTER_MAX && - rankedType == FilterOptions::RankedFilterType::ShowAll && - minStars == 0 && - s.maxStars >= FilterOptions::STAR_FILTER_MAX && - s.minUploadDateInMonths == 0 && - minRating == 0 && - minVotes == 0 && - uploaders.size() == 0 && - difficultyFilter == FilterOptions::DifficultyFilterType::All && - charFilter == FilterOptions::CharFilterType::All && - modRequirement == FilterOptions::RequirementType::Any - ); - - // Do infinity checks for songs that are out of bounds - if (s.maxStars >= FilterOptions::STAR_FILTER_MAX) { maxStars=std::numeric_limits::infinity(); } - if (s.maxNJS >= FilterOptions::NJS_FILTER_MAX) { maxNJS=std::numeric_limits::infinity(); } - if (s.maxNPS >= FilterOptions::NPS_FILTER_MAX) { maxNPS=std::numeric_limits::infinity(); } - if (s.maxLength / 60 >= FilterOptions::SONG_LENGTH_FILTER_MAX) { maxLength=std::numeric_limits::infinity(); } - } - - bool skipFilter = false; - - - //General - FilterOptions::DownloadFilterType downloadType = FilterOptions::DownloadFilterType::All; - FilterOptions::LocalScoreFilterType localScoreType = FilterOptions::LocalScoreFilterType::All; - float minLength = 0, maxLength = 900; - - //Mapping - float minNJS = 0, maxNJS = FilterOptions::NJS_FILTER_MAX; - float minNPS = 0, maxNPS = FilterOptions::NPS_FILTER_MAX; - - //ScoreSaber - FilterOptions::RankedFilterType rankedType = FilterOptions::RankedFilterType::ShowAll; - float minStars = 0, maxStars = FilterOptions::STAR_FILTER_MAX; - - //BeatSaver - int minUploadDate = FilterOptions::BEATSAVER_EPOCH; - float minRating = 0; - int minVotes = 0; - std::vector uploaders; - bool uploadersBlackList = false; - - //Difficulty - FilterOptions::DifficultyFilterType difficultyFilter = FilterOptions::DifficultyFilterType::All; - SongDetailsCache::MapDifficulty difficultyFilterPreprocessed; - - - /// @brief Char filter for gui - FilterOptions::CharFilterType charFilter = FilterOptions::CharFilterType::All; - /// @brief Used to speedup filtering, only if not All - SongDetailsCache::MapCharacteristic charFilterPreprocessed = SongDetailsCache::MapCharacteristic::Custom; +#include "rapidjson-macros/shared/macros.hpp" +#include "song-details/shared/Data/MapMods.hpp" +#include "PluginConfig.hpp" + +using namespace SongDetailsCache; + +namespace BetterSongSearch { + DECLARE_JSON_CLASS(FilterProfile, + VALUE_DEFAULT(FilterTypes::DownloadFilter, downloadType, FilterTypes::DownloadFilter::All); + VALUE_DEFAULT(FilterTypes::LocalScoreFilter, localScoreType, FilterTypes::LocalScoreFilter::All); + VALUE_DEFAULT(FilterTypes::RankedFilter, rankedType, FilterTypes::RankedFilter::ShowAll); + VALUE_DEFAULT(FilterTypes::DifficultyFilter, difficultyFilter, FilterTypes::DifficultyFilter::All); + VALUE_DEFAULT(FilterTypes::CharFilter, charFilter, FilterTypes::CharFilter::All); + VALUE_DEFAULT(FilterTypes::Requirement, modRequirement, FilterTypes::Requirement::Any); + + VALUE_DEFAULT(float, minLength, 0); + VALUE_DEFAULT(float, maxLength, 900); + VALUE_DEFAULT(float, minNJS, 0); + VALUE_DEFAULT(float, maxNJS, NJS_FILTER_MAX); + VALUE_DEFAULT(float, minNPS, 0); + VALUE_DEFAULT(float, maxNPS, NPS_FILTER_MAX); + + VALUE_DEFAULT(float, minStars, 0); + VALUE_DEFAULT(float, maxStars, STAR_FILTER_MAX); + VALUE_DEFAULT(int, minUploadDate, BEATSAVER_EPOCH); + VALUE_DEFAULT(int, minUploadDateInMonths, 0); + VALUE_DEFAULT(float, minRating, 0); + VALUE_DEFAULT(int, minVotes, 0); + + VECTOR_DEFAULT(std::string, uploaders, {}); + VALUE_DEFAULT(bool, uploadersBlackList, false); + + public: + SongDetailsCache::MapCharacteristic charFilterPreprocessed = SongDetailsCache::MapCharacteristic::Custom; + SongDetailsCache::MapDifficulty difficultyFilterPreprocessed = SongDetailsCache::MapDifficulty::Easy; + + bool isDefaultPreprocessed = true; + + // @brief Checks if the profile is the default profile (no filters) + bool IsDefault(); + + // @brief Recalculates preprocessed values + void RecalculatePreprocessedValues(); + + // @brief Loads the profile from the mod config + void LoadFromConfig(); + + // @brief Saves the profile to the mod config + void SaveToConfig(); + + // @brief Saves the profile to a preset + // @param presetName The name of the preset to save + // @return True if the preset was saved successfully + bool SaveToPreset(std::string presetName) const; + + // @brief Gets a list of all available presets + // @return A list of all available presets + static std::vector GetPresetList(); + + // @brief Loads the profile from a preset + // @param presetName The name of the preset to load + // @return The loaded profile + static std::optional LoadFromPreset(std::string presetName); + ) +} - //Mods - FilterOptions::RequirementType modRequirement = FilterOptions::RequirementType::Any; -}; diff --git a/include/PluginConfig.hpp b/include/PluginConfig.hpp index 36f9ec4..d5c51ba 100644 --- a/include/PluginConfig.hpp +++ b/include/PluginConfig.hpp @@ -1,15 +1,127 @@ #pragma once -#include "config-utils/shared/config-utils.hpp" - #include #include -#include "FilterOptions.hpp" -#include "main.hpp" +#include "song-details/shared/Data/MapCharacteristic.hpp" +#include "song-details/shared/Data/MapDifficulty.hpp" +#include "song-details/shared/Data/RankedStates.hpp" +#include "song-details/shared/Data/SongDifficulty.hpp" -DECLARE_CONFIG(PluginConfig, +#include "config-utils/shared/config-utils.hpp" + +static inline const float SONG_LENGTH_FILTER_MAX = 15.0f; +static inline const float STAR_FILTER_MAX = 18.0f; +static inline const float NJS_FILTER_MAX = 25.0f; +static inline const float NPS_FILTER_MAX = 12.0f; +static inline const int64_t BEATSAVER_EPOCH = 1525136400; +static inline const std::chrono::system_clock::time_point BEATSAVER_EPOCH_TIME_POINT{std::chrono::seconds(BEATSAVER_EPOCH)}; + +namespace FilterTypes { + enum class DownloadFilter + { + All, + OnlyDownloaded, + HideDownloaded + }; + enum class LocalScoreFilter + { + All, + HidePassed, + OnlyPassed + }; + enum class RankedFilter + { + ShowAll, + ScoreSaberRanked, + BeatLeaderRanked, + ScoreSaberQualified, + BeatLeaderQualified + }; + enum class DifficultyFilter + { + All, + Easy, + Normal, + Hard, + Expert, + ExpertPlus + }; + enum class CharFilter + { + All, + Custom, + Standard, + OneSaber, + NoArrows, + NinetyDegrees, + ThreeSixtyDegrees, + LightShow, + Lawless, + }; + + enum class Requirement { + Any, + NoodleExtensions, + MappingExtensions, + Chroma, + Cinema, + None + }; + + enum class SortMode { + Newest, + Oldest, + Latest_Ranked, + Most_Stars, + Least_Stars, + Best_rated, + Worst_rated + }; + enum class PreferredLeaderBoard { + ScoreSaber = 0, + BeatLeader = 1 + }; + +} + +// Map for characteristics +static const std::unordered_map CHARACTERISTIC_MAP = { + {FilterTypes::CharFilter::Custom, SongDetailsCache::MapCharacteristic::Custom}, + {FilterTypes::CharFilter::Standard, SongDetailsCache::MapCharacteristic::Standard}, + {FilterTypes::CharFilter::OneSaber, SongDetailsCache::MapCharacteristic::OneSaber}, + {FilterTypes::CharFilter::NoArrows, SongDetailsCache::MapCharacteristic::NoArrows}, + {FilterTypes::CharFilter::NinetyDegrees, SongDetailsCache::MapCharacteristic::NinetyDegree}, + {FilterTypes::CharFilter::ThreeSixtyDegrees, SongDetailsCache::MapCharacteristic::ThreeSixtyDegree}, + {FilterTypes::CharFilter::LightShow, SongDetailsCache::MapCharacteristic::LightShow}, + {FilterTypes::CharFilter::Lawless, SongDetailsCache::MapCharacteristic::Lawless} +}; + +// Map for difficulties +static const std::unordered_map DIFFICULTY_MAP = { + {FilterTypes::DifficultyFilter::Easy, SongDetailsCache::MapDifficulty::Easy}, + {FilterTypes::DifficultyFilter::Normal, SongDetailsCache::MapDifficulty::Normal}, + {FilterTypes::DifficultyFilter::Hard, SongDetailsCache::MapDifficulty::Hard}, + {FilterTypes::DifficultyFilter::Expert, SongDetailsCache::MapDifficulty::Expert}, + {FilterTypes::DifficultyFilter::ExpertPlus, SongDetailsCache::MapDifficulty::ExpertPlus} +}; + +// Map for ranked states +static const std::unordered_map RANK_MAP = { + {FilterTypes::RankedFilter::ScoreSaberRanked, SongDetailsCache::RankedStates::ScoresaberRanked}, + {FilterTypes::RankedFilter::BeatLeaderRanked, SongDetailsCache::RankedStates::BeatleaderRanked}, + {FilterTypes::RankedFilter::ScoreSaberQualified, SongDetailsCache::RankedStates::ScoresaberQualified}, + {FilterTypes::RankedFilter::BeatLeaderQualified, SongDetailsCache::RankedStates::BeatleaderQualified} +}; + +// Map for preferred leaderboard +static const std::unordered_map LEADERBOARD_MAP = { + {"Scoresaber", FilterTypes::PreferredLeaderBoard::ScoreSaber}, + {"Beatleader", FilterTypes::PreferredLeaderBoard::BeatLeader} +}; + +DECLARE_CONFIG(PluginConfig, CONFIG_VALUE(ReturnToBSS, bool, "Return to BSS from Solo", true); CONFIG_VALUE(LoadSongPreviews, bool, "Load song previews", true); CONFIG_VALUE(SmallerFontSize, bool, "Smaller font size", true); @@ -19,14 +131,14 @@ DECLARE_CONFIG(PluginConfig, CONFIG_VALUE(MinLength, float, "Minimum Song Length", 0); CONFIG_VALUE(MaxLength, float, "Maximum Song Length", 900); CONFIG_VALUE(MinNJS, float, "Minimum Note Jump Speed", 0); - CONFIG_VALUE(MaxNJS, float, "Maximum Note Jump Speed", 25); + CONFIG_VALUE(MaxNJS, float, "Maximum Note Jump Speed", SONG_LENGTH_FILTER_MAX); CONFIG_VALUE(MinNPS, float, "Minimum Notes Per Second", 0); - CONFIG_VALUE(MaxNPS, float, "Maximum Note Per Second", 12); + CONFIG_VALUE(MaxNPS, float, "Maximum Note Per Second", NPS_FILTER_MAX); CONFIG_VALUE(RankedType, int, "Ranked Type", 0); CONFIG_VALUE(MinStars, float, "Minimum Ranked Stars", 0); - CONFIG_VALUE(MaxStars, float, "Maximum Ranked Stars", 18); + CONFIG_VALUE(MaxStars, float, "Maximum Ranked Stars", STAR_FILTER_MAX); CONFIG_VALUE(MinUploadDateInMonths, int, "Minimum Upload Date In Months", 0); //TEMPORARY UNTIL I FIX CONVERTING UNIX -> MONTHS SINCE BEAT SAVER - CONFIG_VALUE(MinUploadDate, int, "Minimum Upload Date", FilterOptions::BEATSAVER_EPOCH); + CONFIG_VALUE(MinUploadDate, int, "Minimum Upload Date", BEATSAVER_EPOCH); CONFIG_VALUE(MinRating, float, "Minimum Rating", 0); CONFIG_VALUE(MinVotes, int, "Minimum Votes", 0); CONFIG_VALUE(DifficultyType, int, "Difficulty Type", 0); diff --git a/include/UI/ViewControllers/FilterView.hpp b/include/UI/ViewControllers/FilterView.hpp index b2ebec5..ebadc75 100644 --- a/include/UI/ViewControllers/FilterView.hpp +++ b/include/UI/ViewControllers/FilterView.hpp @@ -31,7 +31,13 @@ DECLARE_CLASS_CODEGEN(BetterSongSearch::UI::ViewControllers, FilterViewControlle DECLARE_INSTANCE_METHOD(void, CloseSponsorModal); DECLARE_INSTANCE_METHOD(void, OpenSponsorsLink); DECLARE_INSTANCE_METHOD(void, TryToDownloadDataset); - + + // @brief Update the global state from the filter settings withouth updating the UI + DECLARE_INSTANCE_METHOD(void, UpdateLocalState); + + DECLARE_INSTANCE_METHOD(void, ForceRefreshUI); + + DECLARE_INSTANCE_METHOD(void, ForceFormatValues); // Header buttons DECLARE_INSTANCE_METHOD(void, ClearFilters); diff --git a/include/UI/ViewControllers/SongList.hpp b/include/UI/ViewControllers/SongList.hpp index 4cb53ce..3118e1b 100644 --- a/include/UI/ViewControllers/SongList.hpp +++ b/include/UI/ViewControllers/SongList.hpp @@ -45,6 +45,7 @@ DECLARE_OVERRIDE_METHOD(retval, method, il2cpp_utils::il2cpp_type_check::MetadataGetter::get(), __VA_ARGS__) #endif +using namespace BetterSongSearch; #define GET_FIND_METHOD(mPtr) il2cpp_utils::il2cpp_type_check::MetadataGetter::get() @@ -66,30 +67,29 @@ namespace BetterSongSearch::UI { inline static std::vector sortedSongList; // State variables to be globally accessible - inline static SortMode currentSort = SortMode::Newest; + inline static FilterTypes::SortMode currentSort = FilterTypes::SortMode::Newest; inline static std::string currentSearch = ""; inline static std::unordered_set songsWithScores; - inline static FilterOptions filterOptions; - inline static FilterOptionsCache filterOptionsCache; + inline static FilterProfile filterOptions; + inline static FilterProfile filterOptionsCache; /// @brief Player data model to get the scores inline static UnityW playerDataModel = nullptr; // Preferred Leaderboard - inline static PreferredLeaderBoard preferredLeaderboard = PreferredLeaderBoard::ScoreSaber; + inline static FilterTypes::PreferredLeaderBoard preferredLeaderboard = FilterTypes::PreferredLeaderBoard::ScoreSaber; /// @brief Song data is loaded inline static bool loaded = false; /// @brief Song data failed to load inline static bool failed = false; - // Song data is loading + // Song data is loading inline static bool loading = false; /// @brief Flag to say that the song list needs a refresh when the user opens the BSS because the data got updated inline static bool needsRefresh = false; // Song list is invalid (means that we should not touch anything in the song list rn) inline static bool invalid = false; - }; #define PROP_GET(jsonName, varName) \ @@ -127,7 +127,7 @@ namespace BetterSongSearch::UI { } using SortFunction = std::function< float (SongDetailsCache::Song const*)>; -extern std::unordered_map sortFunctionMap; +extern std::unordered_map sortFunctionMap; #ifdef HotReload DECLARE_CLASS_CUSTOM_INTERFACES(BetterSongSearch::UI::ViewControllers, SongListController, BSML::HotReloadViewController, classof(HMUI::TableView::IDataSource*), @@ -228,7 +228,7 @@ DECLARE_CLASS_CODEGEN_INTERFACES(BetterSongSearch::UI::ViewControllers, SongList BetterSongSearch::Util::RatelimitCoroutine* limitedUpdateSearchedSongsList = nullptr; - void SortAndFilterSongs(SortMode sort, std::string_view search, bool resetTable); + void SortAndFilterSongs(FilterTypes::SortMode sort, std::string_view search, bool resetTable); void ResetTable(); const SongDetailsCache::Song* currentSong = nullptr; void UpdateDetails(); @@ -236,10 +236,10 @@ DECLARE_CLASS_CODEGEN_INTERFACES(BetterSongSearch::UI::ViewControllers, SongList // Temp values std::string search = ""; - SortMode sort = (SortMode) 0; + FilterTypes::SortMode sort = (FilterTypes::SortMode) 0; // Prev values std::string prevSearch = ""; - SortMode prevSort = (SortMode) 0; + FilterTypes::SortMode prevSort = (FilterTypes::SortMode) 0; bool filterChanged = true; diff --git a/include/Util/TextUtil.hpp b/include/Util/TextUtil.hpp index 7f26a21..f0d628b 100644 --- a/include/Util/TextUtil.hpp +++ b/include/Util/TextUtil.hpp @@ -8,6 +8,9 @@ namespace BetterSongSearch::Util { // this hurts std::vector split(std::string_view buffer, const std::string_view delimeter = " "); + // @brief Joins a vector of strings into a single string, separated by a delimeter + std::string join(std::vector strings, const std::string_view delimeter = " "); + /** * Removes special characters from a string */ diff --git a/include/internal_macros.hpp b/include/internal_macros.hpp deleted file mode 100644 index ccdf7ba..0000000 --- a/include/internal_macros.hpp +++ /dev/null @@ -1 +0,0 @@ -// FIXME: Dummy file cause Red included it for some reason, to be removed in the next release \ No newline at end of file diff --git a/include/main.hpp b/include/main.hpp index 9143659..9837af6 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -9,4 +9,6 @@ #include "beatsaber-hook/shared/utils/hooking.hpp" #include "beatsaber-hook/shared/config/config-utils.hpp" #include "beatsaber-hook/shared/utils/il2cpp-functions.hpp" -#include "UnityEngine/GameObject.hpp" \ No newline at end of file +#include "UnityEngine/GameObject.hpp" + +static inline modloader::ModInfo modInfo = {MOD_ID, VERSION, GIT_COMMIT}; // Stores the ID and version of our mod, and is sent to the modloader upon startup diff --git a/src/FilterOptions.cpp b/src/FilterOptions.cpp new file mode 100644 index 0000000..b165b2c --- /dev/null +++ b/src/FilterOptions.cpp @@ -0,0 +1,167 @@ +#include "FilterOptions.hpp" + +#include "Util/TextUtil.hpp" +#include "main.hpp" +#include "logging.hpp" + +using namespace rapidjson; +using namespace UnityEngine; +using namespace BetterSongSearch::Util; + + +bool BetterSongSearch::FilterProfile::IsDefault(){ + return ( + downloadType == FilterTypes::DownloadFilter::All && + localScoreType == FilterTypes::LocalScoreFilter::All && + (maxLength / 60 >= SONG_LENGTH_FILTER_MAX) && + (minLength == 0) && + minNJS == 0 && + maxNJS >= NJS_FILTER_MAX && + minNPS == 0 && + maxNPS >= NPS_FILTER_MAX && + rankedType == FilterTypes::RankedFilter::ShowAll && + minStars == 0 && + maxStars >= STAR_FILTER_MAX && + minUploadDateInMonths == 0 && + minRating == 0 && + minVotes == 0 && + uploaders.empty() && + difficultyFilter == FilterTypes::DifficultyFilter::All && + charFilter == FilterTypes::CharFilter::All && + modRequirement == FilterTypes::Requirement::Any + ); +} + +void BetterSongSearch::FilterProfile::LoadFromConfig() { + downloadType = (FilterTypes::DownloadFilter) getPluginConfig().DownloadType.GetValue(); + localScoreType = (FilterTypes::LocalScoreFilter) getPluginConfig().LocalScoreType.GetValue(); + minLength = getPluginConfig().MinLength.GetValue(); + maxLength = getPluginConfig().MaxLength.GetValue(); + minNJS = getPluginConfig().MinNJS.GetValue(); + maxNJS = getPluginConfig().MaxNJS.GetValue(); + minNPS = getPluginConfig().MinNPS.GetValue(); + maxNPS = getPluginConfig().MaxNPS.GetValue(); + rankedType = (FilterTypes::RankedFilter) getPluginConfig().RankedType.GetValue(); + minStars = getPluginConfig().MinStars.GetValue(); + maxStars = getPluginConfig().MaxStars.GetValue(); + minUploadDate = getPluginConfig().MinUploadDate.GetValue(); + minRating = getPluginConfig().MinRating.GetValue(); + minVotes = getPluginConfig().MinVotes.GetValue(); + charFilter = (FilterTypes::CharFilter) getPluginConfig().CharacteristicType.GetValue(); + difficultyFilter = (FilterTypes::DifficultyFilter) getPluginConfig().DifficultyType.GetValue(); + modRequirement = (FilterTypes::Requirement) getPluginConfig().RequirementType.GetValue(); + minUploadDateInMonths = getPluginConfig().MinUploadDateInMonths.GetValue(); + + // Custom string loader + auto uploadersString = getPluginConfig().Uploaders.GetValue(); + if (!uploadersString.empty()) { + if (uploadersString[0] == '!') { + uploadersString.erase(0,1); + uploadersBlackList = true; + } else { + uploadersBlackList = false; + } + uploaders = split(toLower(uploadersString), " "); + } else { + uploaders.clear(); + } +} + +void BetterSongSearch::FilterProfile::SaveToConfig() { + getPluginConfig().DownloadType.SetValue((int) downloadType, false); + getPluginConfig().LocalScoreType.SetValue((int) localScoreType, false); + getPluginConfig().MinLength.SetValue(minLength, false); + getPluginConfig().MaxLength.SetValue(maxLength, false); + getPluginConfig().MinNJS.SetValue(minNJS, false); + getPluginConfig().MaxNJS.SetValue(maxNJS, false); + getPluginConfig().MinNPS.SetValue(minNPS, false); + getPluginConfig().MaxNPS.SetValue(maxNPS, false); + getPluginConfig().RankedType.SetValue((int) rankedType, false); + getPluginConfig().MinStars.SetValue(minStars, false); + getPluginConfig().MaxStars.SetValue(maxStars, false); + getPluginConfig().MinUploadDate.SetValue(minUploadDate, false); + getPluginConfig().MinRating.SetValue(minRating, false); + getPluginConfig().MinVotes.SetValue(minVotes, false); + getPluginConfig().CharacteristicType.SetValue((int) charFilter, false); + getPluginConfig().DifficultyType.SetValue((int) difficultyFilter, false); + getPluginConfig().RequirementType.SetValue((int) modRequirement, false); + getPluginConfig().MinUploadDateInMonths.SetValue(minUploadDateInMonths, false); + + getPluginConfig().Uploaders.SetValue((uploadersBlackList ? "!" : "") + join(uploaders, " "), false); + + getPluginConfig().Save(); +} + +std::optional BetterSongSearch::FilterProfile::LoadFromPreset(std::string presetName){ + std::string presetsDir = getDataDir(modInfo) + "/Presets/"; + + // Ensure the directory exists + if (!direxists(presetsDir)) { + mkpath(presetsDir); + } + + std::string path = presetsDir + presetName + ".json"; + + if (!fileexists(path)) { + return std::nullopt; + } + + try { + return ReadFromFile(path); + } catch (const std::exception &e) { + ERROR("Failed to load preset: {}", e.what()); + return std::nullopt; + } +} + +bool BetterSongSearch::FilterProfile::SaveToPreset(std::string presetName) const { + std::string presetsDir = getDataDir(modInfo) + "/Presets/"; + + // Ensure the directory exists + if (!direxists(presetsDir)) { + mkpath(presetsDir); + } + + std::string path = presetsDir + presetName + ".json"; + + try { + WriteToFile(path, *this); + return true; + } catch (const std::exception &e) { + ERROR("Failed to save preset: {}", e.what()); + return false; + } +} + +std::vector BetterSongSearch::FilterProfile::GetPresetList() { + std::vector presetNames; + + std::string presetsDir = getDataDir(modInfo) + "/Presets/"; + + // Ensure the directory exists + if (!direxists(presetsDir)) { + mkpath(presetsDir); + } + + if(!std::filesystem::is_directory(presetsDir)) return presetNames; + + std::error_code ec; + auto directory_iterator = std::filesystem::directory_iterator(presetsDir, std::filesystem::directory_options::none, ec); + for (auto const& entry : directory_iterator) { + if(!entry.is_regular_file()) continue; + std::string file_extension = entry.path().extension().string(); + std::string raw_file_name = entry.path().filename().replace_extension().string(); + if (file_extension == ".json") presetNames.push_back(raw_file_name); + } + std::sort(presetNames.begin(), presetNames.end(), [](std::string& a, std::string& b) { + return a < b; + }); + + return presetNames; +} + +void BetterSongSearch::FilterProfile::RecalculatePreprocessedValues(){ + charFilterPreprocessed = CHARACTERISTIC_MAP.at(charFilter); + difficultyFilterPreprocessed = DIFFICULTY_MAP.at(difficultyFilter); + isDefaultPreprocessed = IsDefault(); +} diff --git a/src/UI/Modals/Settings.cpp b/src/UI/Modals/Settings.cpp index f17a4de..f8e4a2c 100644 --- a/src/UI/Modals/Settings.cpp +++ b/src/UI/Modals/Settings.cpp @@ -69,18 +69,18 @@ void Modals::Settings::set_smallerFontSize(bool value) { StringW Modals::Settings::get_preferredLeaderboard() { // Preferred Leaderboard std::string preferredLeaderboard = getPluginConfig().PreferredLeaderboard.GetValue(); - if (leaderBoardMap.contains(preferredLeaderboard)) { + if (LEADERBOARD_MAP.contains(preferredLeaderboard)) { return preferredLeaderboard; } else { - DataHolder::preferredLeaderboard = PreferredLeaderBoard::ScoreSaber; + DataHolder::preferredLeaderboard = FilterTypes::PreferredLeaderBoard::ScoreSaber; getPluginConfig().PreferredLeaderboard.SetValue("Scoresaber"); return "Scoresaber"; } } void Modals::Settings::set_preferredLeaderboard(StringW value) { - if (leaderBoardMap.contains(value)) { - DataHolder::preferredLeaderboard = leaderBoardMap.at(value); + if (LEADERBOARD_MAP.contains(value)) { + DataHolder::preferredLeaderboard = LEADERBOARD_MAP.at(value); getPluginConfig().PreferredLeaderboard.SetValue(value); auto controller = fcInstance->SongListController; controller->filterChanged = true; diff --git a/src/UI/ViewControllers/FilterView.cpp b/src/UI/ViewControllers/FilterView.cpp index 2386737..ca6aa88 100644 --- a/src/UI/ViewControllers/FilterView.cpp +++ b/src/UI/ViewControllers/FilterView.cpp @@ -25,12 +25,12 @@ using namespace BetterSongSearch::Util; using namespace BetterSongSearch::UI; using namespace BetterSongSearch::UI::Util::BSMLStuff; -static const std::chrono::system_clock::time_point BEATSAVER_EPOCH_TIME_POINT{std::chrono::seconds(FilterOptions::BEATSAVER_EPOCH)}; + DEFINE_TYPE(BetterSongSearch::UI::ViewControllers, FilterViewController); #define coro(coroutine) BSML::SharedCoroutineStarter::get_instance()->StartCoroutine(custom_types::Helpers::CoroutineHelper::New(coroutine)) -#define SAVE_STRING_CONFIG(value, options, configName, filterProperty ) \ +#define SAVE_STRING_CONFIG(value, options, configName ) \ if (value != nullptr) { \ int index = get_##options()->IndexOf(reinterpret_cast (value.convert())); \ if (index < 0 ) { \ @@ -39,24 +39,21 @@ DEFINE_TYPE(BetterSongSearch::UI::ViewControllers, FilterViewController); if (index != getPluginConfig().configName.GetValue()) { \ filtersChanged = true; \ getPluginConfig().configName.SetValue(index); \ - DataHolder::filterOptions.filterProperty = (typeof(DataHolder::filterOptions.filterProperty)) index; \ } \ }\ } -#define SAVE_NUMBER_CONFIG(value, configName, filterProperty) \ +#define SAVE_NUMBER_CONFIG(value, configName) \ if (value != getPluginConfig().configName.GetValue()) { \ filtersChanged = true; \ getPluginConfig().configName.SetValue(value); \ - DataHolder::filterOptions.filterProperty = (typeof(DataHolder::filterOptions.filterProperty)) value; \ } \ // TODO: Fix saving last saved -#define SAVE_INTEGER_CONFIG(value, configName, filterProperty) \ +#define SAVE_INTEGER_CONFIG(value, configName) \ if (static_cast(value) != getPluginConfig().configName.GetValue()) { \ filtersChanged = true; \ getPluginConfig().configName.SetValue(static_cast(value)); \ - DataHolder::filterOptions.filterProperty = static_cast(value); \ } \ @@ -67,45 +64,29 @@ custom_types::Helpers::Coroutine ViewControllers::FilterViewController::_UpdateF // WARNING: There is a bug with bsml update, it runs before the value is changed for some reason bool filtersChanged = false; -// if (this->existingSongs != nullptr) { -// int index = get_downloadedFilterOptions()->IndexOf(reinterpret_cast (this->existingSongs.convert())); -// if (index < 0) { -// getLogger().fmtLog("WE HAVE A BUG WITH SAVING VALUE {}", -// (std::string) this->existingSongs); -// } -// else { -// if (index != getPluginConfig().DownloadType.GetValue()) { -// filtersChanged = true; -// getPluginConfig().DownloadType.SetValue(index); -// DataHolder::filterOptions.downloadType = (typeof(DataHolder::filterOptions.downloadType)) index; -// } -// } -// } - SAVE_STRING_CONFIG(this->existingSongs, downloadedFilterOptions, DownloadType, downloadType); - SAVE_STRING_CONFIG(this->existingScore, scoreFilterOptions, LocalScoreType , localScoreType); - - SAVE_STRING_CONFIG(this->characteristic, characteristics, CharacteristicType, charFilter); - SAVE_STRING_CONFIG(this->difficulty, difficulties, DifficultyType, difficultyFilter); - SAVE_STRING_CONFIG(this->rankedState, rankedFilterOptions, RankedType, rankedType); - SAVE_STRING_CONFIG(this->mods, modOptions, RequirementType, modRequirement); - SAVE_NUMBER_CONFIG(this->minimumNjs, MinNJS, minNJS); - SAVE_NUMBER_CONFIG(this->maximumNjs,MaxNJS, maxNJS); - SAVE_NUMBER_CONFIG(this->minimumNps, MinNPS, minNPS); - SAVE_NUMBER_CONFIG(this->maximumNps,MaxNPS, maxNPS); - SAVE_NUMBER_CONFIG(this->minimumStars,MinStars, minStars); - SAVE_NUMBER_CONFIG(this->maximumStars,MaxStars, maxStars); - SAVE_NUMBER_CONFIG(this->minimumRating, MinRating, minRating); - SAVE_INTEGER_CONFIG(this->minimumVotes,MinVotes, minVotes); + + SAVE_STRING_CONFIG(this->existingSongs, downloadedFilterOptions, DownloadType); + SAVE_STRING_CONFIG(this->existingScore, scoreFilterOptions, LocalScoreType); + + SAVE_STRING_CONFIG(this->characteristic, characteristics, CharacteristicType); + SAVE_STRING_CONFIG(this->difficulty, difficulties, DifficultyType); + SAVE_STRING_CONFIG(this->rankedState, rankedFilterOptions, RankedType); + SAVE_STRING_CONFIG(this->mods, modOptions, RequirementType); + SAVE_NUMBER_CONFIG(this->minimumNjs, MinNJS); + SAVE_NUMBER_CONFIG(this->maximumNjs, MaxNJS); + SAVE_NUMBER_CONFIG(this->minimumNps, MinNPS); + SAVE_NUMBER_CONFIG(this->maximumNps, MaxNPS); + SAVE_NUMBER_CONFIG(this->minimumStars, MinStars); + SAVE_NUMBER_CONFIG(this->maximumStars, MaxStars); + SAVE_NUMBER_CONFIG(this->minimumRating, MinRating); + SAVE_INTEGER_CONFIG(this->minimumVotes, MinVotes); // Special case for saving date if (this->hideOlderThan != getPluginConfig().MinUploadDateInMonths.GetValue()) { filtersChanged = true; - auto timestamp = GetDateAfterMonths(DataHolder::filterOptions.BEATSAVER_EPOCH, this->hideOlderThan).count(); - - DataHolder::filterOptions.minUploadDate = timestamp; - DataHolder::filterOptions.minUploadDateInMonths = this->hideOlderThan; - DEBUG("Date {}", GetDateAfterMonths(DataHolder::filterOptions.BEATSAVER_EPOCH, this->hideOlderThan)); + auto timestamp = GetDateAfterMonths(BEATSAVER_EPOCH, this->hideOlderThan).count(); + DEBUG("Date {}", GetDateAfterMonths(BEATSAVER_EPOCH, this->hideOlderThan)); getPluginConfig().MinUploadDate.SetValue(timestamp); getPluginConfig().MinUploadDateInMonths.SetValue(this->hideOlderThan); @@ -116,7 +97,6 @@ custom_types::Helpers::Coroutine ViewControllers::FilterViewController::_UpdateF int seconds = minimumSongLength * 60; filtersChanged = true; - DataHolder::filterOptions.minLength = seconds; getPluginConfig().MinLength.SetValue(seconds); } @@ -125,7 +105,6 @@ custom_types::Helpers::Coroutine ViewControllers::FilterViewController::_UpdateF int seconds = maximumSongLength * 60; filtersChanged = true; - DataHolder::filterOptions.maxLength = seconds; getPluginConfig().MaxLength.SetValue(seconds); } @@ -135,48 +114,20 @@ custom_types::Helpers::Coroutine ViewControllers::FilterViewController::_UpdateF // Save to config getPluginConfig().Uploaders.SetValue(this->uploadersString); - - // Apply to filters - std::string copy = uploadersString; - if (copy.size() > 0) { - if (copy[0] == '!') { - copy.erase(0,1); - DataHolder::filterOptions.uploadersBlackList = true; - } else { - DataHolder::filterOptions.uploadersBlackList = false; - } - DataHolder::filterOptions.uploaders = split(toLower(copy), " "); - } else { - DataHolder::filterOptions.uploaders.clear(); - } } - - std::function uploadersStringFormat = [](std::string value) { - bool blacklist = false; - if (value.size() > 0) { - if (value[0] == '!') { - value.erase(0,1); - blacklist = true; - } - } else { - return (std::string) ""; - } - auto uploaders = split(value, " "); - - return fmt::format("{} {} uploader", (blacklist ? "Hiding": "Show only"), uploaders.size(), (uploaders.size() == 1 ? "" : "s") ); - }; - if (filtersChanged) { DEBUG("Filters changed"); + + // Update filter options state + DataHolder::filterOptions.LoadFromConfig(); + auto controller = fcInstance->SongListController; controller->filterChanged = true; controller->SortAndFilterSongs(controller->sort, controller->search, true); } else { DEBUG("Filters did not change"); } - - } UnityEngine::Sprite* GetBGSprite(std::string str) @@ -199,29 +150,10 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo coro(this->_UpdateFilterSettings()); }, 0.2f); - INFO("Filter View contoller activated"); - - // Get settings and set stuff - this->existingSongs=this->get_downloadedFilterOptions()->get_Item((int) DataHolder::filterOptions.downloadType); - this->existingScore=this->get_scoreFilterOptions()->get_Item((int) DataHolder::filterOptions.localScoreType); - this->minimumSongLength=DataHolder::filterOptions.minLength / 60.0f; - this->maximumSongLength=DataHolder::filterOptions.maxLength / 60.0f; - this->minimumNjs = DataHolder::filterOptions.minNJS; - this->maximumNjs = DataHolder::filterOptions.maxNJS; - this->minimumNps = DataHolder::filterOptions.minNPS; - this->maximumNps = DataHolder::filterOptions.maxNPS; - this->minimumStars = DataHolder::filterOptions.minStars; - this->maximumStars = DataHolder::filterOptions.maxStars; - this->minimumRating = DataHolder::filterOptions.minRating; - this->minimumVotes = DataHolder::filterOptions.minVotes; - this->hideOlderThan = getPluginConfig().MinUploadDateInMonths.GetValue(); + INFO("Filter View controller activated"); - // Custom string loader - this->uploadersString = getPluginConfig().Uploaders.GetValue(); - this->characteristic = this->get_characteristics()->get_Item((int) DataHolder::filterOptions.charFilter); - this->difficulty = this->get_difficulties()->get_Item((int) DataHolder::filterOptions.difficultyFilter); - this->rankedState = this->get_rankedFilterOptions()->get_Item((int) DataHolder::filterOptions.rankedType); - this->mods = this->get_modOptions()->get_Item((int) DataHolder::filterOptions.modRequirement); + // Load the values from the config + UpdateLocalState(); // Create bsml view BSML::parse_and_construct(Assets::FilterView_bsml, this->get_transform(), this); @@ -229,15 +161,14 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo auto x = this->get_gameObject()->get_transform().cast(); x->set_offsetMax(UnityEngine::Vector2(20.0f, 22.0f)); - auto maxUploadDate = BetterSongSearch::GetMonthsSinceDate(FilterOptions::BEATSAVER_EPOCH); + auto maxUploadDate = BetterSongSearch::GetMonthsSinceDate(BEATSAVER_EPOCH); coro(BetterSongSearch::UI::Util::BSMLStuff::MergeSliders(this->get_gameObject())); - // Apply formatter functions Manually cause Red did not implement parsing for them in bsml std::function DateTimeToStr = [](float monthsSinceFirstUpload) { - auto val = BetterSongSearch::GetTimepointAfterMonths(FilterOptions::BEATSAVER_EPOCH,monthsSinceFirstUpload); + auto val = BetterSongSearch::GetTimepointAfterMonths(BEATSAVER_EPOCH,monthsSinceFirstUpload); return fmt::format("{:%b:%Y}", fmt::localtime(system_clock::to_time_t(val))); }; @@ -265,7 +196,7 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo } // Format other values - std::function minLengthSliderFormatFunction = [](float value) { + std::function minLengthSliderFormatFunction = [](float value) { float totalSeconds = value * 60; int minutes = ((int)totalSeconds % 3600) / 60; int seconds = (int)totalSeconds % 60; @@ -276,12 +207,12 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo minimumSongLengthSlider->formatter = minLengthSliderFormatFunction; // Max length format - std::function maxLengthSliderFormatFunction = [](float value) { + std::function maxLengthSliderFormatFunction = [](float value) { float totalSeconds = value * 60; int minutes = ((int)totalSeconds % 3600) / 60; int seconds = (int)totalSeconds % 60; - if (value >= DataHolder::filterOptions.SONG_LENGTH_FILTER_MAX) { + if (value >= SONG_LENGTH_FILTER_MAX) { return (std::string) "Unlimited"; } else { return fmt::format("{:02}:{:02}", minutes, seconds); @@ -290,17 +221,17 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo maximumSongLengthSlider->formatter = maxLengthSliderFormatFunction; // Min rating format - std::function minRatingSliderFormatFunction = [](float value) { + std::function minRatingSliderFormatFunction = [](float value) { return fmt::format("{:.1f}%", value*100); }; minimumRatingSlider->formatter = minRatingSliderFormatFunction; // NJS format - std::function minNJSFormat = [](float value) { + std::function minNJSFormat = [](float value) { return fmt::format("{:.1f}", value); }; - std::function maxNJSFormat = [](float value) { - if (value >= DataHolder::filterOptions.NJS_FILTER_MAX) { + std::function maxNJSFormat = [](float value) { + if (value >= NJS_FILTER_MAX) { return (std::string) "Unlimited"; } return fmt::format("{:.1f}", value); @@ -309,11 +240,11 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo maximumNjsSlider->formatter = maxNJSFormat; // NPS format - std::function minNPSFormat = [](float value) { + std::function minNPSFormat = [](float value) { return fmt::format("{:.1f}", value); }; - std::function maxNPSFormat = [](float value) { - if (value >= DataHolder::filterOptions.NPS_FILTER_MAX) { + std::function maxNPSFormat = [](float value) { + if (value >= NPS_FILTER_MAX) { return (std::string) "Unlimited"; } return fmt::format("{:.1f}", value); @@ -322,18 +253,18 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo maximumNpsSlider->formatter = maxNPSFormat; // Stars formatting - std::function minStarFormat = [](float value) { + std::function minStarFormat = [](float value) { return fmt::format("{:.1f}", value); }; - std::function maxStarFormat = [](float value) { - if (value >= FilterOptions::STAR_FILTER_MAX) { + std::function maxStarFormat = [](float value) { + if (value >= STAR_FILTER_MAX) { return (std::string) "Unlimited"; } return fmt::format("{:.1f}", value); }; minStarsSetting->formatter = minStarFormat; maxStarsSetting->formatter = maxStarFormat; - std::function minimumVotesFormat = [](float value) { + std::function minimumVotesFormat = [](float value) { return fmt::format("{}", (int) value); }; minimumVotesSlider->formatter = minimumVotesFormat; @@ -356,6 +287,18 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo uploadersStringControl->formatter = uploadersStringFormat; + ForceFormatValues(); + + // I hate BSML sometimes + auto m = modsRequirementDropdown->dropdown->____modalView; + m->get_transform().cast()->set_pivot(Vector2(0.5f, 0.3f)); + + #ifdef HotReload + fileWatcher->filePath = "/sdcard/FilterView.bsml"; + #endif +} + +void ViewControllers::FilterViewController::ForceFormatValues() { // Force format values FormatSliderSettingValue(this->minStarsSetting); FormatSliderSettingValue(this->maxStarsSetting); @@ -368,14 +311,6 @@ void ViewControllers::FilterViewController::DidActivate(bool firstActivation, bo FormatSliderSettingValue(this->minimumRatingSlider); FormatSliderSettingValue(this->minimumVotesSlider); FormatStringSettingValue(this->uploadersStringControl); - - // I hate BSML some times - auto m = modsRequirementDropdown->dropdown->____modalView; - m->get_transform().cast()->set_pivot(UnityEngine::Vector2(0.5f, 0.3f)); - - #ifdef HotReload - fileWatcher->filePath = "/sdcard/FilterView.bsml"; - #endif } void ViewControllers::FilterViewController::UpdateFilterSettings() @@ -399,67 +334,9 @@ void ViewControllers::FilterViewController::OpenSponsorsLink() } -// Top buttons -void ViewControllers::FilterViewController::ClearFilters() -{ - DEBUG("ClearFilters FIRED"); - - // Reset config - getPluginConfig().DownloadType.SetValue(getPluginConfig().DownloadType.GetDefaultValue()); - getPluginConfig().LocalScoreType.SetValue(getPluginConfig().LocalScoreType.GetDefaultValue()); - getPluginConfig().CharacteristicType.SetValue(getPluginConfig().CharacteristicType.GetDefaultValue()); - getPluginConfig().RankedType.SetValue(getPluginConfig().RankedType.GetDefaultValue()); - getPluginConfig().DifficultyType.SetValue(getPluginConfig().DifficultyType.GetDefaultValue()); - getPluginConfig().RequirementType.SetValue(getPluginConfig().RequirementType.GetDefaultValue()); - - getPluginConfig().MinLength.SetValue(getPluginConfig().MinLength.GetDefaultValue()); - getPluginConfig().MaxLength.SetValue(getPluginConfig().MaxLength.GetDefaultValue()); - getPluginConfig().MinNJS.SetValue(getPluginConfig().MinNJS.GetDefaultValue()); - getPluginConfig().MaxNJS.SetValue(getPluginConfig().MaxNJS.GetDefaultValue()); - getPluginConfig().MinNPS.SetValue(getPluginConfig().MinNPS.GetDefaultValue()); - getPluginConfig().MaxNPS.SetValue(getPluginConfig().MaxNPS.GetDefaultValue()); - getPluginConfig().MinStars.SetValue(getPluginConfig().MinStars.GetDefaultValue()); - getPluginConfig().MaxStars.SetValue(getPluginConfig().MaxStars.GetDefaultValue()); - getPluginConfig().MinUploadDate.SetValue(getPluginConfig().MinUploadDate.GetDefaultValue()); - getPluginConfig().MinRating.SetValue(getPluginConfig().MinRating.GetDefaultValue()); - getPluginConfig().MinVotes.SetValue(getPluginConfig().MinVotes.GetDefaultValue()); - getPluginConfig().Uploaders.SetValue(getPluginConfig().Uploaders.GetDefaultValue()); - getPluginConfig().MinUploadDateInMonths.SetValue(getPluginConfig().MinUploadDateInMonths.GetDefaultValue()); - getPluginConfig().MinUploadDate.SetValue(getPluginConfig().MinUploadDate.GetDefaultValue()); - - - // Load to dataHolder - DataHolder::filterOptions.downloadType = (FilterOptions::DownloadFilterType) getPluginConfig().DownloadType.GetValue(); - DataHolder::filterOptions.localScoreType = (FilterOptions::LocalScoreFilterType) getPluginConfig().LocalScoreType.GetValue(); - DataHolder::filterOptions.charFilter = (FilterOptions::CharFilterType) getPluginConfig().CharacteristicType.GetValue(); - DataHolder::filterOptions.rankedType = (FilterOptions::RankedFilterType) getPluginConfig().RankedType.GetValue(); - DataHolder::filterOptions.difficultyFilter = (FilterOptions::DifficultyFilterType) getPluginConfig().DifficultyType.GetValue(); - DataHolder::filterOptions.modRequirement = (FilterOptions::RequirementType) getPluginConfig().RequirementType.GetValue(); - DataHolder::filterOptions.minLength = getPluginConfig().MinLength.GetValue(); - DataHolder::filterOptions.maxLength = getPluginConfig().MaxLength.GetValue(); - DataHolder::filterOptions.minNJS = getPluginConfig().MinNJS.GetValue(); - DataHolder::filterOptions.maxNJS = getPluginConfig().MaxNJS.GetValue(); - DataHolder::filterOptions.minNPS = getPluginConfig().MinNPS.GetValue(); - DataHolder::filterOptions.maxNPS = getPluginConfig().MaxNPS.GetValue(); - DataHolder::filterOptions.minStars = getPluginConfig().MinStars.GetValue(); - DataHolder::filterOptions.maxStars = getPluginConfig().MaxStars.GetValue(); - DataHolder::filterOptions.minUploadDate = getPluginConfig().MinUploadDate.GetValue(); - DataHolder::filterOptions.minRating = getPluginConfig().MinRating.GetValue(); - DataHolder::filterOptions.minVotes = getPluginConfig().MinVotes.GetValue(); - auto uploadersString = getPluginConfig().Uploaders.GetValue(); - if (uploadersString.size() > 0) { - if (uploadersString[0] == '!') { - uploadersString.erase(0,1); - DataHolder::filterOptions.uploadersBlackList = true; - } else { - DataHolder::filterOptions.uploadersBlackList = false; - } - DataHolder::filterOptions.uploaders = split(toLower(uploadersString), " "); - } else { - DataHolder::filterOptions.uploaders.clear(); - } - // Load to UI +void ViewControllers::FilterViewController::UpdateLocalState() { + // Load from dataHolder this->existingSongs=this->get_downloadedFilterOptions()->get_Item((int) DataHolder::filterOptions.downloadType); this->existingScore=this->get_scoreFilterOptions()->get_Item((int) DataHolder::filterOptions.localScoreType); this->characteristic = this->get_characteristics()->get_Item((int) DataHolder::filterOptions.charFilter); @@ -477,10 +354,13 @@ void ViewControllers::FilterViewController::ClearFilters() this->maximumStars = DataHolder::filterOptions.maxStars; this->minimumRating = DataHolder::filterOptions.minRating; this->minimumVotes = DataHolder::filterOptions.minVotes; + + // TODO: Maybe save it to the preset too this->hideOlderThan = getPluginConfig().MinUploadDateInMonths.GetValue(); this->uploadersString = getPluginConfig().Uploaders.GetValue(); +} - +void ViewControllers::FilterViewController::ForceRefreshUI() { // Refresh UI // Force format values SetSliderSettingValue(this->minimumSongLengthSlider, this->minimumSongLength); @@ -501,6 +381,43 @@ void ViewControllers::FilterViewController::ClearFilters() characteristicDropdown->set_Value(reinterpret_cast (this->characteristic.convert())); difficultyDropdown->set_Value(reinterpret_cast (this->difficulty.convert())); modsRequirementDropdown->set_Value(reinterpret_cast (this->mods.convert())); +} + +// Top buttons +void ViewControllers::FilterViewController::ClearFilters() +{ + DEBUG("ClearFilters FIRED"); + + // Reset config + getPluginConfig().DownloadType.SetValue(getPluginConfig().DownloadType.GetDefaultValue()); + getPluginConfig().LocalScoreType.SetValue(getPluginConfig().LocalScoreType.GetDefaultValue()); + getPluginConfig().CharacteristicType.SetValue(getPluginConfig().CharacteristicType.GetDefaultValue()); + getPluginConfig().RankedType.SetValue(getPluginConfig().RankedType.GetDefaultValue()); + getPluginConfig().DifficultyType.SetValue(getPluginConfig().DifficultyType.GetDefaultValue()); + getPluginConfig().RequirementType.SetValue(getPluginConfig().RequirementType.GetDefaultValue()); + getPluginConfig().MinLength.SetValue(getPluginConfig().MinLength.GetDefaultValue()); + getPluginConfig().MaxLength.SetValue(getPluginConfig().MaxLength.GetDefaultValue()); + getPluginConfig().MinNJS.SetValue(getPluginConfig().MinNJS.GetDefaultValue()); + getPluginConfig().MaxNJS.SetValue(getPluginConfig().MaxNJS.GetDefaultValue()); + getPluginConfig().MinNPS.SetValue(getPluginConfig().MinNPS.GetDefaultValue()); + getPluginConfig().MaxNPS.SetValue(getPluginConfig().MaxNPS.GetDefaultValue()); + getPluginConfig().MinStars.SetValue(getPluginConfig().MinStars.GetDefaultValue()); + getPluginConfig().MaxStars.SetValue(getPluginConfig().MaxStars.GetDefaultValue()); + getPluginConfig().MinUploadDate.SetValue(getPluginConfig().MinUploadDate.GetDefaultValue()); + getPluginConfig().MinRating.SetValue(getPluginConfig().MinRating.GetDefaultValue()); + getPluginConfig().MinVotes.SetValue(getPluginConfig().MinVotes.GetDefaultValue()); + getPluginConfig().Uploaders.SetValue(getPluginConfig().Uploaders.GetDefaultValue()); + getPluginConfig().MinUploadDateInMonths.SetValue(getPluginConfig().MinUploadDateInMonths.GetDefaultValue()); + getPluginConfig().MinUploadDate.SetValue(getPluginConfig().MinUploadDate.GetDefaultValue()); + + // Load to dataHolder + DataHolder::filterOptions.LoadFromConfig(); + + // Refresh FilterView state from settings and DataHolder + UpdateLocalState(); + + // Force refresh UI + ForceRefreshUI(); DEBUG("Filters changed"); auto controller = fcInstance->SongListController; @@ -512,8 +429,6 @@ void ViewControllers::FilterViewController::ShowPresets() DEBUG("ShowPresets FIRED"); } - - // StringW ViewControllers::FilterViewController::DateTimeToStr(int d) { // // FilterView.hideOlderThanOptions[d].ToString("MMM yyyy", CultureInfo.InvariantCulture); // } diff --git a/src/UI/ViewControllers/SongList.cpp b/src/UI/ViewControllers/SongList.cpp index 4ed00d0..7241317 100644 --- a/src/UI/ViewControllers/SongList.cpp +++ b/src/UI/ViewControllers/SongList.cpp @@ -87,21 +87,18 @@ const std::vector CHAR_FILTER_OPTIONS = {"Any", "Custom", "Standard const std::vector DIFFS = {"Easy", "Normal", "Hard", "Expert", "Expert+"}; const std::vector REQUIREMENTS = {"Any", "Noodle Extensions", "Mapping Extensions", "Chroma", "Cinema"}; -const std::chrono::system_clock::time_point BEATSAVER_EPOCH_TIME_POINT{ - std::chrono::seconds(FilterOptions::BEATSAVER_EPOCH)}; - std::string prevSearch; -SortMode prevSort = (SortMode) 0; +FilterTypes::SortMode prevSort = FilterTypes::SortMode::Newest; using SortFunction = std::function; //////////////////// UTILS ////////////////////// bool BetterSongSearch::UI::MeetsFilter(const SongDetailsCache::Song *song) { - auto const &filterOptions = DataHolder::filterOptionsCache; + auto& filterOptions = DataHolder::filterOptionsCache; std::string songHash = song->hash(); - if (filterOptions.uploaders.size() != 0) { + if (!filterOptions.uploaders.empty()) { if (std::find(filterOptions.uploaders.begin(), filterOptions.uploaders.end(), removeSpecialCharacter(toLower(song->uploaderName()))) != filterOptions.uploaders.end()) { if (filterOptions.uploadersBlackList) @@ -121,23 +118,23 @@ bool BetterSongSearch::UI::MeetsFilter(const SongDetailsCache::Song *song) { if (((int) song->upvotes + (int) song->downvotes) < filterOptions.minVotes) return false; // Skip if not needed - if (filterOptions.localScoreType != FilterOptions::LocalScoreFilterType::All) { + if (filterOptions.localScoreType != FilterTypes::LocalScoreFilter::All) { bool hasLocalScore = false; if (DataHolder::songsWithScores.contains(songHash)) { hasLocalScore = true; } if (hasLocalScore) { - if (filterOptions.localScoreType == FilterOptions::LocalScoreFilterType::HidePassed) + if (filterOptions.localScoreType == FilterTypes::LocalScoreFilter::HidePassed) return false; } else { - if (filterOptions.localScoreType == FilterOptions::LocalScoreFilterType::OnlyPassed) + if (filterOptions.localScoreType == FilterTypes::LocalScoreFilter::OnlyPassed) return false; } } - if (filterOptions.rankedType != FilterOptions::RankedFilterType::ShowAll) { + if (filterOptions.rankedType != FilterTypes::RankedFilter::ShowAll) { // if not the ranked that we want, skip - if (!hasFlags(song->rankedStates, rankMap.at(filterOptions.rankedType))) { + if (!hasFlags(song->rankedStates, RANK_MAP.at(filterOptions.rankedType))) { return false; } } @@ -160,13 +157,13 @@ bool BetterSongSearch::UI::MeetsFilter(const SongDetailsCache::Song *song) { // This is the most heavy filter, check it last - if (filterOptions.downloadType != FilterOptions::DownloadFilterType::All) { + if (filterOptions.downloadType != FilterTypes::DownloadFilter::All) { bool downloaded = SongCore::API::Loading::GetLevelByHash(songHash) != nullptr; if (downloaded) { - if (filterOptions.downloadType == FilterOptions::DownloadFilterType::HideDownloaded) + if (filterOptions.downloadType == FilterTypes::DownloadFilter::HideDownloaded) return false; } else { - if (filterOptions.downloadType == FilterOptions::DownloadFilterType::OnlyDownloaded) + if (filterOptions.downloadType == FilterTypes::DownloadFilter::OnlyDownloaded) return false; } } @@ -177,20 +174,20 @@ bool BetterSongSearch::UI::MeetsFilter(const SongDetailsCache::Song *song) { bool BetterSongSearch::UI::DifficultyCheck(const SongDetailsCache::SongDifficulty *diff, const SongDetailsCache::Song *song) { auto const ¤tFilter = DataHolder::filterOptionsCache; - if (currentFilter.skipFilter) { + if (currentFilter.isDefaultPreprocessed) { return true; } - if (currentFilter.rankedType != FilterOptions::RankedFilterType::ShowAll) { + if (currentFilter.rankedType != FilterTypes::RankedFilter::ShowAll) { // if not the ranked that we want, skip - if (!hasFlags(song->rankedStates, rankMap.at(currentFilter.rankedType))) { + if (!hasFlags(song->rankedStates, RANK_MAP.at(currentFilter.rankedType))) { return false; } } // Min and max stars - if (currentFilter.maxStars != FilterOptions::STAR_FILTER_MAX) { + if (currentFilter.maxStars != STAR_FILTER_MAX) { if (getStars(diff) > currentFilter.maxStars) { return false; } @@ -201,13 +198,13 @@ bool BetterSongSearch::UI::DifficultyCheck(const SongDetailsCache::SongDifficult } } - if (currentFilter.difficultyFilter != FilterOptions::DifficultyFilterType::All) { + if (currentFilter.difficultyFilter != FilterTypes::DifficultyFilter::All) { if (diff->difficulty != currentFilter.difficultyFilterPreprocessed) { return false; } } - if (currentFilter.charFilter != FilterOptions::CharFilterType::All) { + if (currentFilter.charFilter != FilterTypes::CharFilter::All) { if (diff->characteristic != currentFilter.charFilterPreprocessed) { return false; } @@ -216,21 +213,21 @@ bool BetterSongSearch::UI::DifficultyCheck(const SongDetailsCache::SongDifficult if (diff->njs < currentFilter.minNJS || diff->njs > currentFilter.maxNJS) return false; - if (currentFilter.modRequirement != FilterOptions::RequirementType::Any) { + if (currentFilter.modRequirement != FilterTypes::Requirement::Any) { switch (currentFilter.modRequirement) { - case FilterOptions::RequirementType::Chroma: + case FilterTypes::Requirement::Chroma: if (!hasFlags(diff->mods, MapMods::Chroma)) return false; break; - case FilterOptions::RequirementType::Cinema: + case FilterTypes::Requirement::Cinema: if (!hasFlags(diff->mods, MapMods::Cinema)) return false; break; - case FilterOptions::RequirementType::MappingExtensions: + case FilterTypes::Requirement::MappingExtensions: if (!hasFlags(diff->mods, MapMods::MappingExtensions)) return false; break; - case FilterOptions::RequirementType::NoodleExtensions: + case FilterTypes::Requirement::NoodleExtensions: if (!hasFlags(diff->mods, MapMods::NoodleExtensions)) return false; break; - case FilterOptions::RequirementType::None: + case FilterTypes::Requirement::None: if (!((diff->mods & (MapMods::NE | MapMods::ME)) == MapMods::None)) return false; break; default: @@ -249,23 +246,23 @@ bool BetterSongSearch::UI::DifficultyCheck(const SongDetailsCache::SongDifficult } -std::unordered_map sortFunctionMap = { - {SortMode::Newest, [](const SongDetailsCache::Song *x) // Newest +std::unordered_map sortFunctionMap = { + {FilterTypes::SortMode::Newest, [](const SongDetailsCache::Song *x) // Newest { return (x->uploadTimeUnix); }}, - {SortMode::Oldest, [](const SongDetailsCache::Song *x) // Oldest + {FilterTypes::SortMode::Oldest, [](const SongDetailsCache::Song *x) // Oldest { return (std::numeric_limits::max() - x->uploadTimeUnix); }}, - {SortMode::Latest_Ranked, [](const SongDetailsCache::Song *x) // Latest Ranked + {FilterTypes::SortMode::Latest_Ranked, [](const SongDetailsCache::Song *x) // Latest Ranked { return (hasFlags(x->rankedStates, (SongDetailsCache::RankedStates::BeatleaderRanked | SongDetailsCache::RankedStates::ScoresaberRanked))) ? x->rankedChangeUnix : 0.0f; }}, - {SortMode::Most_Stars, [](const SongDetailsCache::Song *x) // Most Stars + {FilterTypes::SortMode::Most_Stars, [](const SongDetailsCache::Song *x) // Most Stars { return x->max([x](const auto &diff) { bool passesFilter = DifficultyCheck(&diff, x); @@ -276,7 +273,7 @@ std::unordered_map sortFunctionMap = { } }); }}, - {SortMode::Least_Stars, [](const SongDetailsCache::Song *x) // Least Stars + {FilterTypes::SortMode::Least_Stars, [](const SongDetailsCache::Song *x) // Least Stars { return 420.0f - x->min([x](const auto &diff) { bool passesFilter = DifficultyCheck(&diff, x); @@ -287,11 +284,11 @@ std::unordered_map sortFunctionMap = { } }); }}, - {SortMode::Best_rated, [](const SongDetailsCache::Song *x) // Best rated + {FilterTypes::SortMode::Best_rated, [](const SongDetailsCache::Song *x) // Best rated { return x->rating(); }}, - {SortMode::Worst_rated, [](const SongDetailsCache::Song *x)//Worst rated + {FilterTypes::SortMode::Worst_rated, [](const SongDetailsCache::Song *x)//Worst rated { return 420.0f - (x->rating() != 0 ? x->rating() : 420.0f); }} @@ -323,7 +320,7 @@ void ViewControllers::SongListController::_UpdateSearchedSongsList() { bool currentSearchChanged = prevSearch != search; // Take a snapshot of current filter options - DataHolder::filterOptionsCache.cache(DataHolder::filterOptions); + DataHolder::filterOptionsCache = DataHolder::filterOptions; DEBUG("SEARCHING Cache"); DEBUG("Sort: {}", SortToString((int) sort)); @@ -422,7 +419,7 @@ void ViewControllers::SongListController::_UpdateSearchedSongsList() { DEBUG("Filtering"); int totalSongs = DataHolder::songDetails->songs.size(); DataHolder::filteredSongList.clear(); - if (DataHolder::filterOptionsCache.skipFilter) { + if (DataHolder::filterOptionsCache.IsDefault()) { DEBUG("Filtering skipped"); DataHolder::filteredSongList.reserve(totalSongs); for (auto &song: DataHolder::songDetails->songs) { @@ -874,7 +871,7 @@ void ViewControllers::SongListController::DidActivate(bool firstActivation, bool auto sortMode = getPluginConfig().SortMode.GetValue(); if (sortMode < get_sortModeSelections()->get_Count()) { selectedSortMode = get_sortModeSelections()->get_Item(sortMode); - sort = (SortMode) sortMode; + sort = (FilterTypes::SortMode) sortMode; } BSML::parse_and_construct(Assets::SongList_bsml, this->get_transform(), this); @@ -1015,7 +1012,7 @@ custom_types::Helpers::Coroutine ViewControllers::SongListController::UpdateData bool filtersChanged = false; - SortMode sort = prevSort; + FilterTypes::SortMode sort = prevSort; if (selectedSortMode != nullptr) { int index = get_sortModeSelections()->IndexOf(reinterpret_cast (selectedSortMode.convert())); if (index < 0) {} @@ -1023,7 +1020,7 @@ custom_types::Helpers::Coroutine ViewControllers::SongListController::UpdateData if (index != getPluginConfig().SortMode.GetValue()) { filtersChanged = true; getPluginConfig().SortMode.SetValue(index); - sort = (SortMode) index; + sort = (FilterTypes::SortMode) index; } } } @@ -1387,15 +1384,12 @@ void ViewControllers::SongListController::UpdateSearch() { } -void -ViewControllers::SongListController::SortAndFilterSongs(SortMode sort, std::string_view const search, bool resetTable) { +void ViewControllers::SongListController::SortAndFilterSongs(FilterTypes::SortMode sort, std::string_view const search, bool resetTable) { // Skip if not active - if (get_isActiveAndEnabled() == false) { - return; - } + if (!get_isActiveAndEnabled()) return; + this->sort = sort; this->search = search; - this->UpdateSearchedSongsList(); } diff --git a/src/UI/ViewControllers/SongListCell.cpp b/src/UI/ViewControllers/SongListCell.cpp index 00e7bee..3ff075a 100644 --- a/src/UI/ViewControllers/SongListCell.cpp +++ b/src/UI/ViewControllers/SongListCell.cpp @@ -91,7 +91,7 @@ namespace BetterSongSearch::UI::ViewControllers }); // If most stars - if (DataHolder::currentSort == SortMode::Most_Stars) { + if (DataHolder::currentSort == FilterTypes::SortMode::Most_Stars) { std::stable_sort(sortedDiffs.begin(), sortedDiffs.end(), [entry](const DiffIndex& a, const DiffIndex& b) { auto diff1 = - a.stars; @@ -102,7 +102,7 @@ namespace BetterSongSearch::UI::ViewControllers }); } // If least stars - if (DataHolder::currentSort == SortMode::Least_Stars) { + if (DataHolder::currentSort == FilterTypes::SortMode::Least_Stars) { std::stable_sort(sortedDiffs.begin(), sortedDiffs.end(), [entry](const DiffIndex& a, const DiffIndex& b) { auto diff1 = a.stars > 0 ? a.stars: -420.0f; auto diff2 = b.stars > 0 ? b.stars: -420.0f; diff --git a/src/Util/SongUtil.cpp b/src/Util/SongUtil.cpp index ef7c046..ca8841b 100644 --- a/src/Util/SongUtil.cpp +++ b/src/Util/SongUtil.cpp @@ -60,17 +60,19 @@ namespace BetterSongSearch::Util { SongDetailsCache::RankedStates GetTargetedRankLeaderboardService(const SongDetailsCache::SongDifficulty* diff) { auto& rStates = diff->song().rankedStates; + FilterProfile filterOptions = UI::DataHolder::filterOptionsCache; + // If song is scoresaber ranked - if (hasFlags(rStates, SongDetailsCache::RankedStates::ScoresaberRanked) && + if (hasFlags(rStates, RankedStates::ScoresaberRanked) && // And Not Filtering by BeatLeader ranked - UI::DataHolder::filterOptionsCache.rankedType != FilterOptions::RankedFilterType::BeatLeaderRanked && + UI::DataHolder::filterOptionsCache.rankedType != FilterTypes::RankedFilter::BeatLeaderRanked && ( // Beatleader is not preferred leaderboard - BetterSongSearch::UI::DataHolder::preferredLeaderboard != PreferredLeaderBoard::BeatLeader || + BetterSongSearch::UI::DataHolder::preferredLeaderboard != FilterTypes::PreferredLeaderBoard::BeatLeader || // Song has no BeatLeader rank - !hasFlags(rStates, SongDetailsCache::RankedStates::BeatleaderRanked) || + !hasFlags(rStates, RankedStates::BeatleaderRanked) || // Filtering by SS ranked - UI::DataHolder::filterOptionsCache.rankedType == FilterOptions::RankedFilterType::ScoreSaberRanked + UI::DataHolder::filterOptionsCache.rankedType == FilterTypes::RankedFilter::ScoreSaberRanked ) ) { return SongDetailsCache::RankedStates::ScoresaberRanked; diff --git a/src/Util/TextUtil.cpp b/src/Util/TextUtil.cpp index b332503..6364c86 100644 --- a/src/Util/TextUtil.cpp +++ b/src/Util/TextUtil.cpp @@ -19,6 +19,17 @@ namespace BetterSongSearch::Util { return ret; } + std::string join(std::vector strings, const std::string_view delimeter) { + if (strings.empty()) return ""; + + std::string ret; + for (size_t i = 0; i < strings.size(); i++) { + ret += strings[i]; + if (i != strings.size() - 1) ret += delimeter; + } + return ret; + } + /** * Removes special characters from a string */ diff --git a/src/main.cpp b/src/main.cpp index 42a99dc..c520b6f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,9 +29,7 @@ #define coro(coroutine) BSML::SharedCoroutineStarter::get_instance()->StartCoroutine(custom_types::Helpers::CoroutineHelper::New(coroutine)) using namespace BetterSongSearch::Util; - -inline modloader::ModInfo modInfo = {MOD_ID, VERSION, GIT_COMMIT}; // Stores the ID and version of our mod, and is sent to the modloader upon startup - +using namespace BetterSongSearch; // Called at the early stages of game loading BSS_EXPORT_FUNC void setup(CModInfo& info) { @@ -40,51 +38,23 @@ BSS_EXPORT_FUNC void setup(CModInfo& info) { modInfo.assign(info); getPluginConfig().Init(modInfo); + INFO("Completed setup!"); std::thread([]{ - auto& filterOptions = DataHolder::filterOptions; INFO("setting config values"); - filterOptions.downloadType = (FilterOptions::DownloadFilterType) getPluginConfig().DownloadType.GetValue(); - filterOptions.localScoreType = (FilterOptions::LocalScoreFilterType) getPluginConfig().LocalScoreType.GetValue(); - filterOptions.minLength = getPluginConfig().MinLength.GetValue(); - filterOptions.maxLength = getPluginConfig().MaxLength.GetValue(); - filterOptions.minNJS = getPluginConfig().MinNJS.GetValue(); - filterOptions.maxNJS = getPluginConfig().MaxNJS.GetValue(); - filterOptions.minNPS = getPluginConfig().MinNPS.GetValue(); - filterOptions.maxNPS = getPluginConfig().MaxNPS.GetValue(); - filterOptions.rankedType = (FilterOptions::RankedFilterType) getPluginConfig().RankedType.GetValue(); - filterOptions.minStars = getPluginConfig().MinStars.GetValue(); - filterOptions.maxStars = getPluginConfig().MaxStars.GetValue(); - filterOptions.minUploadDate = getPluginConfig().MinUploadDate.GetValue(); - filterOptions.minRating = getPluginConfig().MinRating.GetValue(); - filterOptions.minVotes = getPluginConfig().MinVotes.GetValue(); - filterOptions.charFilter = (FilterOptions::CharFilterType) getPluginConfig().CharacteristicType.GetValue(); - filterOptions.difficultyFilter = (FilterOptions::DifficultyFilterType) getPluginConfig().DifficultyType.GetValue(); - filterOptions.modRequirement = (FilterOptions::RequirementType) getPluginConfig().RequirementType.GetValue(); - filterOptions.minUploadDateInMonths = getPluginConfig().MinUploadDateInMonths.GetValue(); - + + // Load configs + DataHolder::filterOptions.LoadFromConfig(); + DataHolder::filterOptionsCache = DataHolder::filterOptions; + // Preferred Leaderboard std::string preferredLeaderboard = getPluginConfig().PreferredLeaderboard.GetValue(); - if (leaderBoardMap.contains(preferredLeaderboard)) { - DataHolder::preferredLeaderboard = leaderBoardMap.at(preferredLeaderboard); - } else { - DataHolder::preferredLeaderboard = PreferredLeaderBoard::ScoreSaber; - getPluginConfig().PreferredLeaderboard.SetValue("Scoresaber"); - } - - // Custom string loader - auto uploadersString = getPluginConfig().Uploaders.GetValue(); - if (!uploadersString.empty()) { - if (uploadersString[0] == '!') { - uploadersString.erase(0,1); - filterOptions.uploadersBlackList = true; - } else { - filterOptions.uploadersBlackList = false; - } - filterOptions.uploaders = split(toLower(uploadersString), " "); + if (LEADERBOARD_MAP.contains(preferredLeaderboard)) { + DataHolder::preferredLeaderboard = LEADERBOARD_MAP.at(preferredLeaderboard); } else { - filterOptions.uploaders.clear(); + DataHolder::preferredLeaderboard = FilterTypes::PreferredLeaderBoard::ScoreSaber; + getPluginConfig().PreferredLeaderboard.SetValue("Scoresaber"); } }).detach(); }