From 972349389cf5deddb832cf8113f187757f5a2dd6 Mon Sep 17 00:00:00 2001 From: pongo1231 Date: Thu, 20 Feb 2025 12:51:40 +0100 Subject: [PATCH] Treewide: .ini configs -> .json configs (#3719) Successor to #2717. Converts all config files to new JSON format on write while keeping compatibility with old .ini config files on read. Values are written as their actual type instead of stringifying everything. Also includes a bunch of (mostly) related cleanups and new logs plastered around in config related code. --- ChaosMod/Components/CrossingChallenge.cpp | 86 ++++----- ChaosMod/Components/CrossingChallenge.h | 2 +- ChaosMod/Components/EffectDispatchTimer.cpp | 7 +- ChaosMod/Components/Voting.cpp | 2 +- ChaosMod/Effects/EffectConfig.h | 71 ++++--- ChaosMod/Main.cpp | 7 +- ChaosMod/Util/OptionsFile.h | 176 +++++++++++------- ChaosMod/Util/OptionsManager.h | 25 +-- ChaosMod/Util/Random.h | 3 +- ConfigApp/MainWindow.xaml.cs | 79 +++++--- ConfigApp/OptionsManager.cs | 22 +-- ConfigApp/Tabs/MetaTab.cs | 12 +- ConfigApp/Tabs/Settings/ColorsTab.cs | 6 +- ConfigApp/Tabs/Settings/GeneralTab.cs | 24 ++- ConfigApp/Tabs/Settings/ModesTab.cs | 24 +-- ConfigApp/Tabs/Settings/ShortcutsTab.cs | 10 +- ConfigApp/Tabs/Settings/SoundsTab.cs | 2 +- ConfigApp/Tabs/Voting/DiscordTab.cs | 28 +-- ConfigApp/Tabs/Voting/GeneralTab.cs | 32 ++-- ConfigApp/Tabs/Voting/TwitchTab.cs | 16 +- ConfigApp/Utils.cs | 76 ++++++-- .../Workshop/WorkshopSettingsDialog.xaml.cs | 2 +- Shared/OptionsFile.cs | 145 +++++++-------- .../TwitchChatVotingProxy.cs | 14 +- .../VotingReceiver/DiscordVotingReceiver.cs | 6 +- .../VotingReceiver/TwitchVotingReceiver.cs | 6 +- 26 files changed, 500 insertions(+), 383 deletions(-) diff --git a/ChaosMod/Components/CrossingChallenge.cpp b/ChaosMod/Components/CrossingChallenge.cpp index 31bb7836d..72396b3ce 100644 --- a/ChaosMod/Components/CrossingChallenge.cpp +++ b/ChaosMod/Components/CrossingChallenge.cpp @@ -24,7 +24,7 @@ void CrossingChallenge::SetStartParams() _SET_WEATHER_TYPE_TRANSITION(m_StartWeatherType1, m_StartWeatherType2, m_StartWeatherPercent); SET_CLOCK_TIME(m_ClockHours, m_ClockMinutes, m_ClockSeconds); REMOVE_ALL_PED_WEAPONS(player, false); - for (WeaponInfo weapon : m_StartWeapons) + for (const auto &weapon : m_StartWeapons) GIVE_WEAPON_TO_PED(player, weapon.hash, weapon.ammo, false, false); CLEAR_PLAYER_WANTED_LEVEL(PLAYER_ID()); @@ -196,28 +196,28 @@ static std::string GetWeaponOptionName(Hash weapon) void CrossingChallenge::SaveConfig() { - m_ConfigFile.SetValue("StartEnabled", m_StartEnabled); - m_ConfigFile.SetValue("StartLocationX", m_StartLocation.x); - m_ConfigFile.SetValue("StartLocationY", m_StartLocation.y); - m_ConfigFile.SetValue("StartLocationZ", m_StartLocation.z); - m_ConfigFile.SetValue("StartVehicle", m_StartVehicleHash); - m_ConfigFile.SetValue("StartHeading", m_StartHeading); - m_ConfigFile.SetValue("StartCameraHeading", m_StartCameraHeading); - m_ConfigFile.SetValue("StartWeather1", m_StartWeatherType1); - m_ConfigFile.SetValue("StartWeather2", m_StartWeatherType2); - m_ConfigFile.SetValue("StartWeatherPercent", m_StartWeatherPercent); - m_ConfigFile.SetValue("StartHours", m_ClockHours); - m_ConfigFile.SetValue("StartMinutes", m_ClockMinutes); - m_ConfigFile.SetValue("StartSeconds", m_ClockSeconds); - - for (WeaponInfo weapon : m_StartWeapons) - m_ConfigFile.SetValue(GetWeaponOptionName(weapon.hash), weapon.ammo); - - m_ConfigFile.SetValue("EndEnabled", m_EndEnabled); - m_ConfigFile.SetValue("EndLocationX", m_EndLocation.x); - m_ConfigFile.SetValue("EndLocationY", m_EndLocation.y); - m_ConfigFile.SetValue("EndLocationZ", m_EndLocation.z); - m_ConfigFile.SetValue("EndRadius", m_EndRadius); + m_ConfigFile.SetValue("StartEnabled", m_StartEnabled); + m_ConfigFile.SetValue("StartLocationX", m_StartLocation.x); + m_ConfigFile.SetValue("StartLocationY", m_StartLocation.y); + m_ConfigFile.SetValue("StartLocationZ", m_StartLocation.z); + m_ConfigFile.SetValue("StartVehicle", m_StartVehicleHash); + m_ConfigFile.SetValue("StartHeading", m_StartHeading); + m_ConfigFile.SetValue("StartCameraHeading", m_StartCameraHeading); + m_ConfigFile.SetValue("StartWeather1", m_StartWeatherType1); + m_ConfigFile.SetValue("StartWeather2", m_StartWeatherType2); + m_ConfigFile.SetValue("StartWeatherPercent", m_StartWeatherPercent); + m_ConfigFile.SetValue("StartHours", m_ClockHours); + m_ConfigFile.SetValue("StartMinutes", m_ClockMinutes); + m_ConfigFile.SetValue("StartSeconds", m_ClockSeconds); + + for (const auto &weapon : m_StartWeapons) + m_ConfigFile.SetValue(GetWeaponOptionName(weapon.hash), weapon.ammo); + + m_ConfigFile.SetValue("EndEnabled", m_EndEnabled); + m_ConfigFile.SetValue("EndLocationX", m_EndLocation.x); + m_ConfigFile.SetValue("EndLocationY", m_EndLocation.y); + m_ConfigFile.SetValue("EndLocationZ", m_EndLocation.z); + m_ConfigFile.SetValue("EndRadius", m_EndRadius); m_ConfigFile.WriteFile(); } @@ -259,42 +259,42 @@ void CrossingChallenge::CaptureEnd() CrossingChallenge::CrossingChallenge() { - m_Enabled = g_OptionsManager.GetConfigValue({ "EnableCrossingChallenge" }, false); + m_Enabled = g_OptionsManager.GetConfigValue({ "EnableCrossingChallenge" }, false); if (!m_Enabled) return; - m_StartEnabled = m_ConfigFile.ReadValue("StartEnabled", false); + m_StartEnabled = m_ConfigFile.ReadValue({ "StartEnabled" }, false); if (m_StartEnabled) { - m_StartLocation = Vector3(m_ConfigFile.ReadValue("StartLocationX", 0.f), - m_ConfigFile.ReadValue("StartLocationY", 0.f), - m_ConfigFile.ReadValue("StartLocationZ", 0.f)); - m_StartVehicleHash = m_ConfigFile.ReadValue("StartVehicle", 0); - m_StartHeading = m_ConfigFile.ReadValue("StartHeading", 0.f); - m_StartCameraHeading = m_ConfigFile.ReadValue("StartCameraHeading", 0.f); - m_StartWeatherType1 = m_ConfigFile.ReadValue("StartWeather1", 0); - m_StartWeatherType2 = m_ConfigFile.ReadValue("StartWeather2", 0); - m_StartWeatherPercent = m_ConfigFile.ReadValue("StartWeatherPercent", 0.f); - m_ClockHours = m_ConfigFile.ReadValue("StartHours", 0); - m_ClockMinutes = m_ConfigFile.ReadValue("StartMinutes", 0); - m_ClockSeconds = m_ConfigFile.ReadValue("StartSeconds", 0); + m_StartLocation = Vector3(m_ConfigFile.ReadValue({ "StartLocationX" }, 0.f), + m_ConfigFile.ReadValue({ "StartLocationY" }, 0.f), + m_ConfigFile.ReadValue({ "StartLocationZ" }, 0.f)); + m_StartVehicleHash = m_ConfigFile.ReadValue({ "StartVehicle" }, 0); + m_StartHeading = m_ConfigFile.ReadValue({ "StartHeading" }, 0.f); + m_StartCameraHeading = m_ConfigFile.ReadValue({ "StartCameraHeading" }, 0.f); + m_StartWeatherType1 = m_ConfigFile.ReadValue({ "StartWeather1" }, 0); + m_StartWeatherType2 = m_ConfigFile.ReadValue({ "StartWeather2" }, 0); + m_StartWeatherPercent = m_ConfigFile.ReadValue({ "StartWeatherPercent" }, 0.f); + m_ClockHours = m_ConfigFile.ReadValue({ "StartHours" }, 0); + m_ClockMinutes = m_ConfigFile.ReadValue({ "StartMinutes" }, 0); + m_ClockSeconds = m_ConfigFile.ReadValue({ "StartSeconds" }, 0); for (Hash hash : Memory::GetAllWeapons()) { - int ammo = m_ConfigFile.ReadValue(GetWeaponOptionName(hash), -1); + int ammo = m_ConfigFile.ReadValue({ GetWeaponOptionName(hash) }, -1); if (ammo >= 0) m_StartWeapons.emplace_back(WeaponInfo { hash, ammo }); } } - m_EndEnabled = m_ConfigFile.ReadValue("EndEnabled", false); + m_EndEnabled = m_ConfigFile.ReadValue({ "EndEnabled" }, false); if (m_EndEnabled) { - m_EndLocation = Vector3(m_ConfigFile.ReadValue("EndLocationX", 0.f), - m_ConfigFile.ReadValue("EndLocationY", 0.f), - m_ConfigFile.ReadValue("EndLocationZ", 0.f)); - m_EndRadius = m_ConfigFile.ReadValue("EndRadius", 0.f); + m_EndLocation = + Vector3(m_ConfigFile.ReadValue({ "EndLocationX" }, 0.f), m_ConfigFile.ReadValue({ "EndLocationY" }, 0.f), + m_ConfigFile.ReadValue({ "EndLocationZ" }, 0.f)); + m_EndRadius = m_ConfigFile.ReadValue({ "EndRadius" }, 0.f); } if (ComponentExists()) diff --git a/ChaosMod/Components/CrossingChallenge.h b/ChaosMod/Components/CrossingChallenge.h index f5cb1403c..f7b376a78 100644 --- a/ChaosMod/Components/CrossingChallenge.h +++ b/ChaosMod/Components/CrossingChallenge.h @@ -19,7 +19,7 @@ class CrossingChallenge : public Component int ammo; }; - OptionsFile m_ConfigFile { "chaosmod/configs/crossing.ini", { "chaosmod/crossing.ini" } }; + OptionsFile m_ConfigFile { { "chaosmod/configs/cached/crossingchallenge.json", "chaosmod/crossing.ini" } }; bool m_Enabled = false; diff --git a/ChaosMod/Components/EffectDispatchTimer.cpp b/ChaosMod/Components/EffectDispatchTimer.cpp index a6131a96b..3f99fbf61 100644 --- a/ChaosMod/Components/EffectDispatchTimer.cpp +++ b/ChaosMod/Components/EffectDispatchTimer.cpp @@ -13,8 +13,11 @@ EffectDispatchTimer::EffectDispatchTimer(const std::array &timerColor) m_DrawTimerBar = !g_OptionsManager.GetConfigValue({ "DisableTimerBarDraw" }, OPTION_DEFAULT_NO_EFFECT_BAR); m_EffectSpawnTime = g_OptionsManager.GetConfigValue({ "NewEffectSpawnTime" }, OPTION_DEFAULT_EFFECT_SPAWN_TIME); - m_DistanceChaosState.EnableDistanceBasedEffectDispatch = g_OptionsManager.GetConfigValue( - { "EnableDistanceBasedEffectDispatch" }, OPTION_DEFAULT_DISTANCE_BASED_DISPATCH_ENABLED); + m_DistanceChaosState.EnableDistanceBasedEffectDispatch = + g_OptionsManager.GetConfigValue({ "EffectDispatchMode", " EnableDistanceBasedEffectDispatch " }, + OPTION_DEFAULT_DISTANCE_BASED_DISPATCH_ENABLED) + ? true + : false; m_DistanceChaosState.DistanceToActivateEffect = g_OptionsManager.GetConfigValue({ "DistanceToActivateEffect" }, OPTION_DEFAULT_EFFECT_SPAWN_DISTANCE); m_DistanceChaosState.DistanceType = static_cast( diff --git a/ChaosMod/Components/Voting.cpp b/ChaosMod/Components/Voting.cpp index a856dd5ca..04c6200d5 100644 --- a/ChaosMod/Components/Voting.cpp +++ b/ChaosMod/Components/Voting.cpp @@ -20,7 +20,7 @@ Voting::Voting(const std::array &textColor) : Component(), m_TextColor( { m_EnableVoting = g_OptionsManager.GetVotingValue({ "EnableVoting", "EnableTwitchVoting" }, OPTION_DEFAULT_TWITCH_VOTING_ENABLED); - m_VoteablePrefix = g_OptionsManager.GetVotingValue({ "VoteablePrefix" }, ""); + m_VoteablePrefix = g_OptionsManager.GetVotingValue({ "VoteablePrefix" }); } void Voting::OnModPauseCleanup(PauseCleanupFlags cleanupFlags) diff --git a/ChaosMod/Effects/EffectConfig.h b/ChaosMod/Effects/EffectConfig.h index d088bdc93..34a48e52d 100644 --- a/ChaosMod/Effects/EffectConfig.h +++ b/ChaosMod/Effects/EffectConfig.h @@ -29,10 +29,13 @@ namespace EffectConfig return -1; } - inline void ReadConfig(const char *configPath, auto &out, std::vector compatConfigPaths = {}) + inline void ReadConfig(std::vector lookupPaths, auto &out) { - OptionsFile effectsFile(configPath, compatConfigPaths); + DEBUG_LOG("Parsing effect config"); + OptionsFile effectsFile(lookupPaths); + + bool isJson = effectsFile.GetFoundFileName().ends_with(".json"); for (auto &[effectId, effectMetadata] : g_RegisteredEffectsMetadata) { struct ConfigValues @@ -59,35 +62,47 @@ namespace EffectConfig // HACK: Store EffectCustomName seperately std::string valueEffectName; - auto value = effectsFile.ReadValueString({ std::string(effectId) }); - if (!value.empty()) + if (isJson) { - size_t splitIndex = GetNextDelimiterOffset(value); - for (int j = 0;; j++) - { - // Effect-Name override - if (j == 6) - { - auto split = value.substr(0, splitIndex); - // Trim surrounding quotations - if (split.length() >= 2 && split[0] == '\"' && split[split.length() - 1] == '\"') - split = split.substr(1, split.size() - 2); - // Names can't be "0" to support older configs - if (!split.empty() && split != "0") - valueEffectName = split; - } + auto jsonArray = effectsFile.ReadValue({ std::string(effectId) }); + for (size_t i = 0; i < jsonArray.size(); i++) + if (i == 6) + valueEffectName = jsonArray[i]; else + configValues.ValuesRaw[i] = jsonArray[i]; + } + else + { + auto value = effectsFile.ReadValue({ std::string(effectId) }); + if (!value.empty()) + { + size_t splitIndex = GetNextDelimiterOffset(value); + for (int i = 0;; i++) { - const auto &split = value.substr(0, splitIndex); - - Util::TryParse(split, configValues.ValuesRaw[j]); + // Effect-Name override + if (i == 6) + { + auto split = value.substr(0, splitIndex); + // Trim surrounding quotations + if (split.length() >= 2 && split[0] == '\"' && split[split.length() - 1] == '\"') + split = split.substr(1, split.size() - 2); + // Names can't be "0" to support older configs + if (!split.empty() && split != "0") + valueEffectName = split; + } + else + { + const auto &split = value.substr(0, splitIndex); + + Util::TryParse(split, configValues.ValuesRaw[i]); + } + + if (splitIndex == value.npos) + break; + + value = value.substr(splitIndex + 1); + splitIndex = GetNextDelimiterOffset(value); } - - if (splitIndex == value.npos) - break; - - value = value.substr(splitIndex + 1); - splitIndex = GetNextDelimiterOffset(value); } } @@ -148,5 +163,7 @@ namespace EffectConfig out.emplace(EffectIdentifier(std::string(effectId)), effectData); } + + DEBUG_LOG("Parsed effect config"); } } diff --git a/ChaosMod/Main.cpp b/ChaosMod/Main.cpp index c5c4052b3..7746975ab 100644 --- a/ChaosMod/Main.cpp +++ b/ChaosMod/Main.cpp @@ -56,7 +56,8 @@ static void ParseEffectsFile() { g_EnabledEffects.clear(); - EffectConfig::ReadConfig("chaosmod/configs/effects.ini", g_EnabledEffects, { "chaosmod/effects.ini" }); + EffectConfig::ReadConfig( + { "chaosmod/configs/effects.json", "chaosmod/configs/effects.ini", "chaosmod/effects.ini" }, g_EnabledEffects); } static void Init() @@ -139,7 +140,9 @@ static void Init() const auto &effectTimerColor = ParseConfigColorString( g_OptionsManager.GetConfigValue({ "EffectTimedTimerColor" }, OPTION_DEFAULT_TIMED_COLOR)); - g_Random.SetSeed(g_OptionsManager.GetConfigValue({ "Seed" }, 0)); + auto seed = g_OptionsManager.GetConfigValue({ "Seed" }); + if (!seed.empty()) + g_Random.SetSeed(std::hash {}(seed)); g_RandomNoDeterm.SetSeed(GetTickCount64()); std::set blacklistedComponentNames; diff --git a/ChaosMod/Util/OptionsFile.h b/ChaosMod/Util/OptionsFile.h index f6c2a0e7c..e97eefdff 100644 --- a/ChaosMod/Util/OptionsFile.h +++ b/ChaosMod/Util/OptionsFile.h @@ -4,6 +4,8 @@ #include "Util/Text.h" #include "Util/TryParse.h" +#include + #include #include #include @@ -12,14 +14,12 @@ class OptionsFile { private: - const char *m_FileName; - const char *m_FoundFileName; - std::vector m_CompatFileNames; - std::unordered_map m_Options; + std::vector m_LookupFilePaths; + std::string_view m_FoundFilePath; + std::unordered_map m_Options; public: - OptionsFile(const char *fileName, std::vector compatFileNames = {}) - : m_FileName(fileName), m_FoundFileName(fileName), m_CompatFileNames(compatFileNames) + OptionsFile(std::vector lookupFilePaths) : m_LookupFilePaths(lookupFilePaths) { Reset(); } @@ -28,102 +28,146 @@ class OptionsFile { m_Options.clear(); - auto readData = [&](const char *fileName) + bool dataRead = false; + for (auto filePath : m_LookupFilePaths) { - std::ifstream file(fileName); + std::ifstream file(filePath.data()); if (file.fail()) - return false; - - std::string line; - line.resize(128); - while (file.getline(line.data(), 128)) - { - const auto &key = StringTrim(line.substr(0, line.find("="))); + continue; - // Ignore line if there's no "=" - if (line == key) - continue; + DEBUG_LOG("Parsing config file \"" << filePath << "\""); - const auto &value = StringTrim( - line.substr(line.find("=") + 1).substr(0, line.find('\n'))); // Also do trimming of newline - - m_Options.emplace(key, value); + if (filePath.ends_with(".json")) + { + std::string fileContent; + file >> fileContent; + try + { + auto json = nlohmann::json::parse(fileContent); + for (const auto &[key, value] : json.items()) + m_Options.emplace(key, value); + } + catch (nlohmann::json::exception &) + { + break; + } } + else if (filePath.ends_with(".ini")) + { + std::string line; + line.resize(128); + while (file.getline(line.data(), 128)) + { + const auto &key = StringTrim(line.substr(0, line.find("="))); - return true; - }; + // Ignore line if there's no "=" + if (line == key) + continue; - if (!readData(m_FileName)) - { - bool dataRead = false; - for (auto compatFileName : m_CompatFileNames) - if ((dataRead = readData(compatFileName))) - { - m_FoundFileName = compatFileName; - break; + const auto &value = StringTrim( + line.substr(line.find("=") + 1).substr(0, line.find('\n'))); // Also do trimming of newline + + m_Options.emplace(key, value); } + } + else + { + DEBUG_LOG("Config file \"" << filePath << "\" has invalid file extension, continuing search"); + continue; + } - if (!dataRead) - LOG("Config file " << m_FileName << " not found!"); + DEBUG_LOG("Parsed config file \"" << filePath << "\""); + m_FoundFilePath = filePath; + dataRead = true; + break; } + + if (!dataRead) + LOG("Could not load config file " << m_LookupFilePaths[0] << "!"); } inline void WriteFile() { - std::ofstream file(m_FoundFileName, std::ofstream::out | std::ofstream::trunc); + const auto &filePath = m_LookupFilePaths[0]; + + auto dir = filePath.substr(0, filePath.find_last_of('/')); + if (dir != filePath) + std::filesystem::create_directory(dir); + std::ofstream file(filePath.data(), std::ofstream::out | std::ofstream::trunc); if (!file) { - LOG("Couldn't write config file " << m_FoundFileName); + LOG("Couldn't write config file " << filePath << "!"); return; } - for (auto &[key, value] : m_Options) - file << key << "=" << value << std::endl; - } - template inline T ReadValue(const std::string &key, T defaultValue) const - { - return ReadValue(std::vector { key }, defaultValue); - } + nlohmann::json json; + for (const auto &[key, value] : m_Options) + json[key] = value; + file << json.dump(4); - template inline T ReadValue(const std::vector &keys, T defaultValue) const - { - for (const auto &key : keys) - { - const auto &result = m_Options.find(key); + LOG("Wrote config file " << filePath); - if (result != m_Options.end() && !result->second.empty()) + for (const auto &filePath : m_LookupFilePaths) + if (filePath != m_FoundFilePath) { - T parsedResult; - if (Util::TryParse(result->second, parsedResult)) - return parsedResult; + std::error_code error; + std::filesystem::remove(filePath, error); } - } - - return defaultValue; } - inline std::string ReadValueString(const std::vector &keys, const std::string &defaultValue = {}) const + template inline T ReadValue(const std::vector &lookupKeys, T defaultValue = {}) const { - for (const auto &key : keys) + for (const auto &key : lookupKeys) { const auto &result = m_Options.find(key); - if (result != m_Options.end()) - return result->second; + { + DEBUG_LOG("Reading value of key \"" << key << "\" from config file \"" << m_FoundFilePath << "\""); + + try + { + const auto &value = result->second; + if constexpr (std::is_base_of() || std::is_base_of()) + { + if (!value.is_string()) + return defaultValue; + + return value; + } + else if (value.is_string()) + { + T parsedResult; + std::string strValue = value; + if (!Util::TryParse(strValue.c_str(), parsedResult)) + return defaultValue; + + return parsedResult; + } + + return value; + } + catch (nlohmann::json::exception &) + { + // We aren't interested in potential other matches + LOG("WARNING: Config file \"" << m_FoundFilePath << "\" has invalid value for key \"" << key + << "\", reverting to default value!"); + break; + } + } } return defaultValue; } - inline void SetValueString(const std::string &key, const std::string &value) + template inline void SetValue(const std::string &key, T value) { - if (m_Options.contains(key)) - m_Options[key] = value; - else - m_Options.emplace(key, value); + DEBUG_LOG("Writing value \"" << value << "\" for key \"" << key << "\" to config file \"" + << m_LookupFilePaths[0] << "\""); + m_Options[key] = value; } - template inline void SetValue(const std::string &key, T value) + + inline std::string_view GetFoundFileName() const { - SetValueString(key, std::to_string(value)); + return m_FoundFilePath; } }; \ No newline at end of file diff --git a/ChaosMod/Util/OptionsManager.h b/ChaosMod/Util/OptionsManager.h index ef1e0f5b2..6431eb3d2 100644 --- a/ChaosMod/Util/OptionsManager.h +++ b/ChaosMod/Util/OptionsManager.h @@ -9,9 +9,10 @@ class OptionsManager { private: - OptionsFile m_ConfigFile { "chaosmod/configs/config.ini", { "chaosmod/config.ini" } }; - OptionsFile m_TwitchFile { "chaosmod/configs/voting.ini", - { "chaosmod/configs/twitch.ini", "chaosmod/twitch.ini" } }; + OptionsFile m_ConfigFile { { "chaosmod/configs/config.json", "chaosmod/configs/config.ini", + "chaosmod/config.ini" } }; + OptionsFile m_TwitchFile { { "chaosmod/configs/voting.json", "chaosmod/configs/voting.ini", + "chaosmod/configs/twitch.ini", "chaosmod/twitch.ini" } }; public: void Reset() @@ -20,29 +21,21 @@ class OptionsManager m_TwitchFile.Reset(); } - template inline T GetConfigValue(const std::vector &keys, T defaultValue) + template inline T GetConfigValue(const std::vector &lookupKeys, T defaultValue = {}) { - return GetOptionValue(m_ConfigFile, keys, defaultValue); + return GetOptionValue(m_ConfigFile, lookupKeys, defaultValue); } - template inline T GetVotingValue(const std::vector &keys, T defaultValue) + template inline T GetVotingValue(const std::vector &lookupKeys, T defaultValue = {}) { - return GetOptionValue(m_TwitchFile, keys, defaultValue); + return GetOptionValue(m_TwitchFile, lookupKeys, defaultValue); } private: template inline T GetOptionValue(const OptionsFile &optionsFile, const std::vector &keys, T defaultValue = {}) { - if constexpr (std::is_same::type, std::string>() - || std::is_same::type, char *>()) - { - return optionsFile.ReadValueString(keys, defaultValue); - } - else - { - return optionsFile.ReadValue(keys, defaultValue); - } + return optionsFile.ReadValue(keys, defaultValue); } }; diff --git a/ChaosMod/Util/Random.h b/ChaosMod/Util/Random.h index 96de0fb4c..157e8ccdc 100644 --- a/ChaosMod/Util/Random.h +++ b/ChaosMod/Util/Random.h @@ -11,8 +11,7 @@ class Random public: inline void SetSeed(int seed) { - if (seed > 0) - m_Random.seed(seed); + m_Random.seed(seed); } inline int GetRandomInt(int lower, int upper) diff --git a/ConfigApp/MainWindow.xaml.cs b/ConfigApp/MainWindow.xaml.cs index 6f8b44761..5279a6e6b 100644 --- a/ConfigApp/MainWindow.xaml.cs +++ b/ConfigApp/MainWindow.xaml.cs @@ -7,6 +7,7 @@ using ConfigApp.Tabs; using ConfigApp.Tabs.Settings; using ConfigApp.Tabs.Voting; +using Newtonsoft.Json.Linq; using static ConfigApp.Effects; namespace ConfigApp @@ -160,25 +161,29 @@ private EffectData GetEffectData(string effectId) private void ParseConfigFile() { // Meta Effects - meta_effects_spawn_dur.Text = OptionsManager.ConfigFile.ReadValue("NewMetaEffectSpawnTime", "600"); - meta_effects_timed_dur.Text = OptionsManager.ConfigFile.ReadValue("MetaEffectDur", "95"); - meta_effects_short_timed_dur.Text = OptionsManager.ConfigFile.ReadValue("MetaShortEffectDur", "65"); + meta_effects_spawn_dur.Text = $"{OptionsManager.ConfigFile.ReadValue("NewMetaEffectSpawnTime", 600)}"; + meta_effects_timed_dur.Text = $"{OptionsManager.ConfigFile.ReadValue("MetaEffectDur", 95)}"; + meta_effects_short_timed_dur.Text = $"{OptionsManager.ConfigFile.ReadValue("MetaShortEffectDur", 65)}"; } private void WriteConfigFile() { // Meta Effects - OptionsManager.ConfigFile.WriteValue("NewMetaEffectSpawnTime", meta_effects_spawn_dur.Text); - OptionsManager.ConfigFile.WriteValue("MetaEffectDur", meta_effects_timed_dur.Text); - OptionsManager.ConfigFile.WriteValue("MetaShortEffectDur", meta_effects_short_timed_dur.Text); + OptionsManager.ConfigFile.WriteValueAsInt("NewMetaEffectSpawnTime", meta_effects_spawn_dur.Text); + OptionsManager.ConfigFile.WriteValueAsInt("MetaEffectDur", meta_effects_timed_dur.Text); + OptionsManager.ConfigFile.WriteValueAsInt("MetaShortEffectDur", meta_effects_short_timed_dur.Text); } private void ParseEffectsFile() { + bool isJson = OptionsManager.EffectsFile.FoundFilePath.EndsWith(".json"); foreach (string key in OptionsManager.EffectsFile.GetKeys()) { - var value = OptionsManager.EffectsFile.ReadValue(key); - var effectData = Utils.ValueStringToEffectData(value); + EffectData effectData; + if (isJson) + effectData = Utils.ValuesArrayToEffectData(OptionsManager.EffectsFile.ReadValue(key)); + else + effectData = Utils.ValuesArrayToEffectData(OptionsManager.EffectsFile.ReadValue(key)); m_EffectDataMap?.Add(key, effectData); } @@ -186,18 +191,23 @@ private void ParseEffectsFile() private void WriteEffectsFile() { - foreach (var pair in EffectsMap) + foreach (var (effectId, _) in EffectsMap) { - var effectData = GetEffectData(pair.Key); - - OptionsManager.EffectsFile.WriteValue(pair.Key, $"{(m_TreeMenuItemsMap?[pair.Key].IsChecked is true ? 1 : 0)}" - + $",{(int)effectData.TimedType.GetValueOrDefault(EffectTimedType.NotTimed)}" - + $",{effectData.CustomTime.GetValueOrDefault(0)}" - + $",{effectData.WeightMult.GetValueOrDefault(0)}" - + $",{(effectData.TimedType.GetValueOrDefault(EffectTimedType.NotTimed) == EffectTimedType.Permanent ? 1 : 0)}" - + $",{(effectData.ExcludedFromVoting.GetValueOrDefault(false) ? 1 : 0)}" - + $",\"{(string.IsNullOrEmpty(effectData.CustomName) ? "" : effectData.CustomName)}\"" - + $",{effectData.ShortcutKeycode.GetValueOrDefault(0)}"); + var effectData = GetEffectData(effectId); + + var jsonArray = new JArray + { + m_TreeMenuItemsMap?[effectId].IsChecked, + (int)effectData.TimedType.GetValueOrDefault(EffectTimedType.NotTimed), + effectData.CustomTime.GetValueOrDefault(0), + effectData.WeightMult.GetValueOrDefault(0), + effectData.TimedType.GetValueOrDefault(EffectTimedType.NotTimed) == EffectTimedType.Permanent, + effectData.ExcludedFromVoting.GetValueOrDefault(false), + string.IsNullOrEmpty(effectData.CustomName) ? "" : effectData.CustomName, + effectData.ShortcutKeycode.GetValueOrDefault(0) + }; + + OptionsManager.EffectsFile.WriteValue(effectId, jsonArray); } OptionsManager.EffectsFile.WriteFile(); @@ -347,12 +357,35 @@ private void NoCopyPastePreviewExecuted(object sender, ExecutedRoutedEventArgs e private void OnUserSaveClick(object sender, RoutedEventArgs e) { - if (OptionsManager.ConfigFile.HasCompatFile("config.ini") || OptionsManager.TwitchFile.HasCompatFile("twitch.ini") - || OptionsManager.EffectsFile.HasCompatFile("effects.ini")) - if (MessageBox.Show("Note: Config files reside inside the configs/ subdirectory now. Clicking OK will move the files there. " + + // Config migration stuff + bool oldIniFilesExist = false; + if (OptionsManager.ConfigFile.FoundFilePath == "config.ini" || OptionsManager.VotingFile.FoundFilePath == "twitch.ini" + || OptionsManager.EffectsFile.FoundFilePath == "effects.ini") + { + oldIniFilesExist = true; + if (MessageBox.Show("Config files reside inside the configs/ subdirectory now. Clicking OK will move the config files there. " + "If you want to play older versions of the mod you will have to move them back. Continue?", "ChaosModV", MessageBoxButton.OKCancel, MessageBoxImage.Warning) != MessageBoxResult.OK) return; + } + + if (oldIniFilesExist || OptionsManager.ConfigFile.FoundFilePath == "configs/config.ini" || OptionsManager.VotingFile.FoundFilePath == "configs/twitch.ini" || OptionsManager.VotingFile.FoundFilePath == "configs/voting.ini" + || OptionsManager.EffectsFile.FoundFilePath == "configs/effects.ini") + { + oldIniFilesExist = true; + if (MessageBox.Show("WARNING: Starting with mod version 2.2 config files are automatically migrated to the new JSON format. Clicking OK will migrate your config files. " + + "This will prevent you from using earlier mod versions with your existing config. Your old config files will be backed up to the configs/old/ directory. Continue?", "ChaosModV", MessageBoxButton.OKCancel, MessageBoxImage.Warning) + != MessageBoxResult.OK) + return; + } + + if (oldIniFilesExist) + { + Directory.CreateDirectory("configs/old"); + File.Move(OptionsManager.ConfigFile.FoundFilePath, $"configs/old/{Path.GetFileName(OptionsManager.ConfigFile.FoundFilePath)}", true); + File.Move(OptionsManager.VotingFile.FoundFilePath, $"configs/old/{Path.GetFileName(OptionsManager.VotingFile.FoundFilePath)}", true); + File.Move(OptionsManager.EffectsFile.FoundFilePath, $"configs/old/{Path.GetFileName(OptionsManager.EffectsFile.FoundFilePath)}", true); + } WriteConfigFile(); WriteEffectsFile(); @@ -384,7 +417,7 @@ private void OnUserResetClick(object sender, RoutedEventArgs e) MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) - OptionsManager.TwitchFile.ResetFile(); + OptionsManager.VotingFile.ResetFile(); Init(); diff --git a/ConfigApp/OptionsManager.cs b/ConfigApp/OptionsManager.cs index 8a5763d20..7c852fcfb 100644 --- a/ConfigApp/OptionsManager.cs +++ b/ConfigApp/OptionsManager.cs @@ -5,17 +5,17 @@ namespace ConfigApp { public static class OptionsManager { - public static OptionsFile ConfigFile { get; } = new OptionsFile("configs/config.ini", "config.ini"); - public static OptionsFile TwitchFile { get; } = new OptionsFile("configs/voting.ini", "configs/twitch.ini", "twitch.ini"); - public static OptionsFile EffectsFile { get; } = new OptionsFile("configs/effects.ini", "effects.ini"); + public static OptionsFile ConfigFile { get; } = new OptionsFile("configs/config.json", "configs/config.ini", "config.ini"); + public static OptionsFile VotingFile { get; } = new OptionsFile("configs/voting.json", "configs/voting.ini", "configs/twitch.ini", "twitch.ini"); + public static OptionsFile EffectsFile { get; } = new OptionsFile("configs/effects.json", "configs/effects.ini", "effects.ini"); // These are written to manually - public static OptionsFile WorkshopFile { get; } = new OptionsFile("configs/workshop.ini"); + public static OptionsFile WorkshopFile { get; } = new OptionsFile("configs/workshop.json", "configs/workshop.ini"); public static void ReadFiles() { ConfigFile.ReadFile(); - TwitchFile.ReadFile(); + VotingFile.ReadFile(); EffectsFile.ReadFile(); WorkshopFile.ReadFile(); } @@ -23,14 +23,13 @@ public static void ReadFiles() public static void WriteFiles() { ConfigFile.WriteFile(); - TwitchFile.WriteFile(); + VotingFile.WriteFile(); EffectsFile.WriteFile(); } public static void ResetFiles() { // Exclude TwitchFile as that one is reset separately - ConfigFile.ResetFile(); EffectsFile.ResetFile(); } @@ -43,12 +42,9 @@ static void deleteFiles(string[] files) File.Delete(file); } - if (ConfigFile.HasCompatFile()) - deleteFiles(ConfigFile.GetCompatFiles()); - if (TwitchFile.HasCompatFile()) - deleteFiles(TwitchFile.GetCompatFiles()); - if (EffectsFile.HasCompatFile()) - deleteFiles(EffectsFile.GetCompatFiles()); + deleteFiles(ConfigFile.CompatFilePaths); + deleteFiles(VotingFile.CompatFilePaths); + deleteFiles(EffectsFile.CompatFilePaths); } } } diff --git a/ConfigApp/Tabs/MetaTab.cs b/ConfigApp/Tabs/MetaTab.cs index adf657942..3b1e7dfc1 100644 --- a/ConfigApp/Tabs/MetaTab.cs +++ b/ConfigApp/Tabs/MetaTab.cs @@ -72,18 +72,18 @@ protected override void InitContent() public override void OnLoadValues() { if (m_MetaEffectDispatchTimer is not null) - m_MetaEffectDispatchTimer.Text = OptionsManager.ConfigFile.ReadValue("NewMetaEffectSpawnTime", "600"); + m_MetaEffectDispatchTimer.Text = $"{OptionsManager.ConfigFile.ReadValue("NewMetaEffectSpawnTime", 600)}"; if (m_MetaEffectDuration is not null) - m_MetaEffectDuration.Text = OptionsManager.ConfigFile.ReadValue("MetaEffectDur", "95"); + m_MetaEffectDuration.Text = $"{OptionsManager.ConfigFile.ReadValue("MetaEffectDur", 95)}"; if (m_MetaEffectShortDuration is not null) - m_MetaEffectShortDuration.Text = OptionsManager.ConfigFile.ReadValue("MetaShortEffectDur", "65"); + m_MetaEffectShortDuration.Text = $"{OptionsManager.ConfigFile.ReadValue("MetaShortEffectDur", 65)}"; } public override void OnSaveValues() { - OptionsManager.ConfigFile.WriteValue("NewMetaEffectSpawnTime", m_MetaEffectDispatchTimer?.Text); - OptionsManager.ConfigFile.WriteValue("MetaEffectDur", m_MetaEffectDuration?.Text); - OptionsManager.ConfigFile.WriteValue("MetaShortEffectDur", m_MetaEffectShortDuration?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("NewMetaEffectSpawnTime", m_MetaEffectDispatchTimer?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("MetaEffectDur", m_MetaEffectDuration?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("MetaShortEffectDur", m_MetaEffectShortDuration?.Text); } } } diff --git a/ConfigApp/Tabs/Settings/ColorsTab.cs b/ConfigApp/Tabs/Settings/ColorsTab.cs index 160446e33..280162aca 100644 --- a/ConfigApp/Tabs/Settings/ColorsTab.cs +++ b/ConfigApp/Tabs/Settings/ColorsTab.cs @@ -53,11 +53,11 @@ protected override void InitContent() public override void OnLoadValues() { if (OptionsManager.ConfigFile.HasKey("EffectTimerColor") && m_TimerBarColor is not null) - m_TimerBarColor.SelectedColor = (Color)ColorConverter.ConvertFromString(OptionsManager.ConfigFile.ReadValue("EffectTimerColor")); + m_TimerBarColor.SelectedColor = (Color)ColorConverter.ConvertFromString(OptionsManager.ConfigFile.ReadValue("EffectTimerColor")); if (OptionsManager.ConfigFile.HasKey("EffectTextColor") && m_EffectTextColor is not null) - m_EffectTextColor.SelectedColor = (Color)ColorConverter.ConvertFromString(OptionsManager.ConfigFile.ReadValue("EffectTextColor")); + m_EffectTextColor.SelectedColor = (Color)ColorConverter.ConvertFromString(OptionsManager.ConfigFile.ReadValue("EffectTextColor")); if (OptionsManager.ConfigFile.HasKey("EffectTimedTimerColor") && m_EffectTimerBarColor is not null) - m_EffectTimerBarColor.SelectedColor = (Color)ColorConverter.ConvertFromString(OptionsManager.ConfigFile.ReadValue("EffectTimedTimerColor")); + m_EffectTimerBarColor.SelectedColor = (Color)ColorConverter.ConvertFromString(OptionsManager.ConfigFile.ReadValue("EffectTimedTimerColor")); } public override void OnSaveValues() diff --git a/ConfigApp/Tabs/Settings/GeneralTab.cs b/ConfigApp/Tabs/Settings/GeneralTab.cs index e2c68eac8..f32cdd5e8 100644 --- a/ConfigApp/Tabs/Settings/GeneralTab.cs +++ b/ConfigApp/Tabs/Settings/GeneralTab.cs @@ -37,7 +37,11 @@ protected override void InitContent() grid.PushRowSpacedPair("Don't draw effect text", m_DisableDrawEffectText = Utils.GenerateCommonCheckBox()); grid.PopRow(); - grid.PushRowSpacedPair("Random Seed (Leave empty for random seed every time)", m_RandomSeed = Utils.GenerateCommonNumericOnlyTextBox()); + grid.PushRowSpacedPair("Random Seed (Leave empty for random seed every time)", m_RandomSeed = new TextBox() + { + Width = 200f, + Height = 20f + }); grid.PushRowSpacedPair("Enable effect group weighting", m_EnableEffectGroupWeighting = Utils.GenerateCommonCheckBox()); grid.PopRow(); @@ -53,21 +57,21 @@ protected override void InitContent() public override void OnLoadValues() { if (m_DisableDrawTimer is not null) - m_DisableDrawTimer.IsChecked = OptionsManager.ConfigFile.ReadValueBool("DisableTimerBarDraw", false); + m_DisableDrawTimer.IsChecked = OptionsManager.ConfigFile.ReadValue("DisableTimerBarDraw", false); if (m_DisableDrawEffectText is not null) - m_DisableDrawEffectText.IsChecked = OptionsManager.ConfigFile.ReadValueBool("DisableEffectTextDraw", false); + m_DisableDrawEffectText.IsChecked = OptionsManager.ConfigFile.ReadValue("DisableEffectTextDraw", false); if (m_RandomSeed is not null) - m_RandomSeed.Text = OptionsManager.ConfigFile.ReadValue("Seed"); + m_RandomSeed.Text = OptionsManager.ConfigFile.ReadValue("Seed"); if (m_MaxRunningEffects is not null) - m_MaxRunningEffects.Text = OptionsManager.ConfigFile.ReadValue("MaxParallelRunningEffects", "99"); + m_MaxRunningEffects.Text = $"{OptionsManager.ConfigFile.ReadValue("MaxParallelRunningEffects", 99)}"; if (m_EnableEffectGroupWeighting is not null) - m_EnableEffectGroupWeighting.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableGroupWeightingAdjustments", true); + m_EnableEffectGroupWeighting.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableGroupWeightingAdjustments", true); if (m_DisableModOnStartup is not null) - m_DisableModOnStartup.IsChecked = OptionsManager.ConfigFile.ReadValueBool("DisableStartup", false); + m_DisableModOnStartup.IsChecked = OptionsManager.ConfigFile.ReadValue("DisableStartup", false); if (m_EnableFailsafe is not null) - m_EnableFailsafe.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableFailsafe", true); + m_EnableFailsafe.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableFailsafe", true); if (m_EnableModSplashTexts is not null) - m_EnableModSplashTexts.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableModSplashTexts", true); + m_EnableModSplashTexts.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableModSplashTexts", true); } public override void OnSaveValues() @@ -75,7 +79,7 @@ public override void OnSaveValues() OptionsManager.ConfigFile.WriteValue("DisableTimerBarDraw", m_DisableDrawTimer?.IsChecked); OptionsManager.ConfigFile.WriteValue("DisableEffectTextDraw", m_DisableDrawEffectText?.IsChecked); OptionsManager.ConfigFile.WriteValue("Seed", m_RandomSeed?.Text); - OptionsManager.ConfigFile.WriteValue("MaxParallelRunningEffects", m_MaxRunningEffects?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("MaxParallelRunningEffects", m_MaxRunningEffects?.Text); OptionsManager.ConfigFile.WriteValue("EnableGroupWeightingAdjustments", m_EnableEffectGroupWeighting?.IsChecked); OptionsManager.ConfigFile.WriteValue("DisableStartup", m_DisableModOnStartup?.IsChecked); OptionsManager.ConfigFile.WriteValue("EnableFailsafe", m_EnableFailsafe?.IsChecked); diff --git a/ConfigApp/Tabs/Settings/ModesTab.cs b/ConfigApp/Tabs/Settings/ModesTab.cs index 878815ac1..eda977a82 100644 --- a/ConfigApp/Tabs/Settings/ModesTab.cs +++ b/ConfigApp/Tabs/Settings/ModesTab.cs @@ -121,30 +121,30 @@ public override void OnLoadValues() { if (m_DispatchMode is not null) { - m_DispatchMode.SelectedIndex = !OptionsManager.ConfigFile.ReadValueBool("EnableDistanceBasedEffectDispatch", false) ? 0 : 1; + m_DispatchMode.SelectedIndex = !OptionsManager.ConfigFile.ReadValue("EffectDispatchMode", false, "EnableDistanceBasedEffectDispatch") ? 0 : 1; UpdateDispatchModeGridVisibility(); } if (m_EffectDispatchTimer is not null) - m_EffectDispatchTimer.Text = OptionsManager.ConfigFile.ReadValue("NewEffectSpawnTime", "30"); + m_EffectDispatchTimer.Text = $"{OptionsManager.ConfigFile.ReadValue("NewEffectSpawnTime", 30)}"; if (m_TimedEffectDuration is not null) - m_TimedEffectDuration.Text = OptionsManager.ConfigFile.ReadValue("EffectTimedDur", "90"); + m_TimedEffectDuration.Text = $"{OptionsManager.ConfigFile.ReadValue("EffectTimedDur", 90)}"; if (m_ShortTimedEffectDuration is not null) - m_ShortTimedEffectDuration.Text = OptionsManager.ConfigFile.ReadValue("EffectTimedShortDur", "30"); + m_ShortTimedEffectDuration.Text = $"{OptionsManager.ConfigFile.ReadValue("EffectTimedShortDur", 30)}"; if (m_DistanceBasedDispatchDistance is not null) - m_DistanceBasedDispatchDistance.Text = OptionsManager.ConfigFile.ReadValue("DistanceToActivateEffect", "250"); + m_DistanceBasedDispatchDistance.Text = $"{OptionsManager.ConfigFile.ReadValue("DistanceToActivateEffect", 250)}"; if (m_DistanceBasedDispatchType is not null) - m_DistanceBasedDispatchType.SelectedIndex = OptionsManager.ConfigFile.ReadValueInt("DistanceType", 0); + m_DistanceBasedDispatchType.SelectedIndex = OptionsManager.ConfigFile.ReadValue("DistanceType", 0); if (m_EnableCrossingChallenge is not null) - m_EnableCrossingChallenge.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableCrossingChallenge", false); + m_EnableCrossingChallenge.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableCrossingChallenge", false); } public override void OnSaveValues() { - OptionsManager.ConfigFile.WriteValue("NewEffectSpawnTime", m_EffectDispatchTimer?.Text); - OptionsManager.ConfigFile.WriteValue("EffectTimedDur", m_TimedEffectDuration?.Text); - OptionsManager.ConfigFile.WriteValue("EffectTimedShortDur", m_ShortTimedEffectDuration?.Text); - OptionsManager.ConfigFile.WriteValue("EnableDistanceBasedEffectDispatch", m_DispatchMode?.SelectedIndex); - OptionsManager.ConfigFile.WriteValue("DistanceToActivateEffect", m_DistanceBasedDispatchDistance?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("NewEffectSpawnTime", m_EffectDispatchTimer?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("EffectTimedDur", m_TimedEffectDuration?.Text); + OptionsManager.ConfigFile.WriteValueAsInt("EffectTimedShortDur", m_ShortTimedEffectDuration?.Text); + OptionsManager.ConfigFile.WriteValue("EffectDispatchMode", m_DispatchMode?.SelectedIndex); + OptionsManager.ConfigFile.WriteValueAsInt("DistanceToActivateEffect", m_DistanceBasedDispatchDistance?.Text); OptionsManager.ConfigFile.WriteValue("DistanceType", m_DistanceBasedDispatchType?.SelectedIndex); OptionsManager.ConfigFile.WriteValue("EnableCrossingChallenge", m_EnableCrossingChallenge?.IsChecked); } diff --git a/ConfigApp/Tabs/Settings/ShortcutsTab.cs b/ConfigApp/Tabs/Settings/ShortcutsTab.cs index 128939e5d..8a71c26d0 100644 --- a/ConfigApp/Tabs/Settings/ShortcutsTab.cs +++ b/ConfigApp/Tabs/Settings/ShortcutsTab.cs @@ -46,15 +46,15 @@ protected override void InitContent() public override void OnLoadValues() { if (m_EnableToggleModShortcut is not null) - m_EnableToggleModShortcut.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableToggleModShortcut", true); + m_EnableToggleModShortcut.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableToggleModShortcut", true); if (m_EnableClearActiveEffectsShortcut is not null) - m_EnableClearActiveEffectsShortcut.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableClearEffectsShortcut", true); + m_EnableClearActiveEffectsShortcut.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableClearEffectsShortcut", true); if (m_EnablePauseTimerShortcut is not null) - m_EnablePauseTimerShortcut.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnablePauseTimerShortcut", false); + m_EnablePauseTimerShortcut.IsChecked = OptionsManager.ConfigFile.ReadValue("EnablePauseTimerShortcut", false); if (m_EnableEffectsMenu is not null) - m_EnableEffectsMenu.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableDebugMenu", false); + m_EnableEffectsMenu.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableDebugMenu", false); if (m_EnableAntiSoftlockShortcut is not null) - m_EnableAntiSoftlockShortcut.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EnableAntiSoftlockShortcut", true); + m_EnableAntiSoftlockShortcut.IsChecked = OptionsManager.ConfigFile.ReadValue("EnableAntiSoftlockShortcut", true); } public override void OnSaveValues() diff --git a/ConfigApp/Tabs/Settings/SoundsTab.cs b/ConfigApp/Tabs/Settings/SoundsTab.cs index a706cf9f7..7e6819777 100644 --- a/ConfigApp/Tabs/Settings/SoundsTab.cs +++ b/ConfigApp/Tabs/Settings/SoundsTab.cs @@ -32,7 +32,7 @@ protected override void InitContent() public override void OnLoadValues() { if (m_UseMCI is not null) - m_UseMCI.IsChecked = OptionsManager.ConfigFile.ReadValueBool("EffectSoundUseMCI", false); + m_UseMCI.IsChecked = OptionsManager.ConfigFile.ReadValue("EffectSoundUseMCI", false); } public override void OnSaveValues() diff --git a/ConfigApp/Tabs/Voting/DiscordTab.cs b/ConfigApp/Tabs/Voting/DiscordTab.cs index 23e7f6556..e5e558148 100644 --- a/ConfigApp/Tabs/Voting/DiscordTab.cs +++ b/ConfigApp/Tabs/Voting/DiscordTab.cs @@ -71,16 +71,8 @@ protected override void InitContent() }); PopRow(); - PushRowSpacedPair("Server ID", m_GuildId = new TextBox() - { - Width = 125f, - Height = 20f - }); - PushRowSpacedPair("Channel ID", m_ChannelId = new TextBox() - { - Width = 125f, - Height = 20f - }); + PushRowSpacedPair("Server ID", m_GuildId = Utils.GenerateCommonNumericOnlyTextBox(default, 125f, 20f)); + PushRowSpacedPair("Channel ID", m_ChannelId = Utils.GenerateCommonNumericOnlyTextBox(default, 125f, 20f)); SetElementsEnabled(false); } @@ -89,24 +81,24 @@ public override void OnLoadValues() { if (m_EnableDiscordVoting is not null) { - m_EnableDiscordVoting.IsChecked = OptionsManager.TwitchFile.ReadValueBool("EnableVotingDiscord", false); + m_EnableDiscordVoting.IsChecked = OptionsManager.VotingFile.ReadValue("EnableVotingDiscord", false); SetElementsEnabled(m_EnableDiscordVoting.IsChecked.GetValueOrDefault()); } if (m_Token is not null) - m_Token.Password = OptionsManager.TwitchFile.ReadValue("DiscordBotToken"); + m_Token.Password = OptionsManager.VotingFile.ReadValue("DiscordBotToken"); if (m_GuildId is not null) - m_GuildId.Text = OptionsManager.TwitchFile.ReadValue("DiscordGuildId"); + m_GuildId.Text = OptionsManager.VotingFile.ReadValue("DiscordGuildId"); if (m_ChannelId is not null) - m_ChannelId.Text = OptionsManager.TwitchFile.ReadValue("DiscordChannelId"); + m_ChannelId.Text = OptionsManager.VotingFile.ReadValue("DiscordChannelId"); } public override void OnSaveValues() { - OptionsManager.TwitchFile.WriteValue("EnableVotingDiscord", m_EnableDiscordVoting?.IsChecked); - OptionsManager.TwitchFile.WriteValue("DiscordBotToken", m_Token?.Password); - OptionsManager.TwitchFile.WriteValue("DiscordGuildId", m_GuildId?.Text); - OptionsManager.TwitchFile.WriteValue("DiscordChannelId", m_ChannelId?.Text); + OptionsManager.VotingFile.WriteValue("EnableVotingDiscord", m_EnableDiscordVoting?.IsChecked); + OptionsManager.VotingFile.WriteValue("DiscordBotToken", m_Token?.Password); + OptionsManager.VotingFile.WriteValue("DiscordGuildId", m_GuildId?.Text); + OptionsManager.VotingFile.WriteValue("DiscordChannelId", m_ChannelId?.Text); } } } diff --git a/ConfigApp/Tabs/Voting/GeneralTab.cs b/ConfigApp/Tabs/Voting/GeneralTab.cs index 85b311222..3e983ea00 100644 --- a/ConfigApp/Tabs/Voting/GeneralTab.cs +++ b/ConfigApp/Tabs/Voting/GeneralTab.cs @@ -132,36 +132,36 @@ public override void OnLoadValues() { if (m_EnableVoting is not null) { - m_EnableVoting.IsChecked = OptionsManager.TwitchFile.ReadValueBool("EnableVoting", false, "EnableTwitchVoting"); + m_EnableVoting.IsChecked = OptionsManager.VotingFile.ReadValue("EnableVoting", false, "EnableTwitchVoting"); SetGridsEnabled(m_EnableVoting.IsChecked.GetValueOrDefault()); } if (m_OverlayMode is not null) - m_OverlayMode.SelectedIndex = OptionsManager.TwitchFile.ReadValueInt("VotingOverlayMode", 0, "TwitchVotingOverlayMode"); + m_OverlayMode.SelectedIndex = OptionsManager.VotingFile.ReadValue("VotingOverlayMode", 0, "TwitchVotingOverlayMode"); if (m_EnableRandomEffect is not null) - m_EnableRandomEffect.IsChecked = OptionsManager.TwitchFile.ReadValueBool("RandomEffectVoteableEnable", true, "TwitchRandomEffectVoteableEnable"); + m_EnableRandomEffect.IsChecked = OptionsManager.VotingFile.ReadValue("RandomEffectVoteableEnable", true, "TwitchRandomEffectVoteableEnable"); if (m_SecsBeforeVoting is not null) - m_SecsBeforeVoting.Text = OptionsManager.TwitchFile.ReadValue("VotingSecsBeforeVoting", "0", "TwitchVotingSecsBeforeVoting"); + m_SecsBeforeVoting.Text = $"{OptionsManager.VotingFile.ReadValue("VotingSecsBeforeVoting", 0, "TwitchVotingSecsBeforeVoting")}"; if (m_PermittedUserNames is not null) - m_PermittedUserNames.Text = OptionsManager.TwitchFile.ReadValue("PermittedUsernames", null, "TwitchPermittedUsernames"); + m_PermittedUserNames.Text = OptionsManager.VotingFile.ReadValue("PermittedUsernames", null, "TwitchPermittedUsernames"); if (m_VoteablePrefix is not null) - m_VoteablePrefix.Text = OptionsManager.TwitchFile.ReadValue("VoteablePrefix", ""); + m_VoteablePrefix.Text = OptionsManager.VotingFile.ReadValue("VoteablePrefix", ""); if (m_EnableProportionalVoting is not null) - m_EnableProportionalVoting.IsChecked = OptionsManager.TwitchFile.ReadValueBool("VotingChanceSystem", false, "TwitchVotingChanceSystem"); + m_EnableProportionalVoting.IsChecked = OptionsManager.VotingFile.ReadValue("VotingChanceSystem", false, "TwitchVotingChanceSystem"); if (m_EnableProportionalVotingRetainInitialChance is not null) - m_EnableProportionalVotingRetainInitialChance.IsChecked = OptionsManager.TwitchFile.ReadValueBool("VotingChanceSystemRetainChance", true, + m_EnableProportionalVotingRetainInitialChance.IsChecked = OptionsManager.VotingFile.ReadValue("VotingChanceSystemRetainChance", true, "TwitchVotingChanceSystemRetainChance"); } public override void OnSaveValues() { - OptionsManager.TwitchFile.WriteValue("EnableVoting", m_EnableVoting?.IsChecked); - OptionsManager.TwitchFile.WriteValue("VotingOverlayMode", m_OverlayMode?.SelectedIndex); - OptionsManager.TwitchFile.WriteValue("RandomEffectVoteableEnable", m_EnableRandomEffect?.IsChecked); - OptionsManager.TwitchFile.WriteValue("VotingSecsBeforeVoting", m_SecsBeforeVoting?.Text); - OptionsManager.TwitchFile.WriteValue("PermittedUsernames", m_PermittedUserNames?.Text); - OptionsManager.TwitchFile.WriteValue("VoteablePrefix", m_VoteablePrefix?.Text); - OptionsManager.TwitchFile.WriteValue("VotingChanceSystem", m_EnableProportionalVoting?.IsChecked); - OptionsManager.TwitchFile.WriteValue("VotingChanceSystemRetainChance", m_EnableProportionalVotingRetainInitialChance?.IsChecked); + OptionsManager.VotingFile.WriteValue("EnableVoting", m_EnableVoting?.IsChecked); + OptionsManager.VotingFile.WriteValue("VotingOverlayMode", m_OverlayMode?.SelectedIndex); + OptionsManager.VotingFile.WriteValue("RandomEffectVoteableEnable", m_EnableRandomEffect?.IsChecked); + OptionsManager.VotingFile.WriteValueAsInt("VotingSecsBeforeVoting", m_SecsBeforeVoting?.Text); + OptionsManager.VotingFile.WriteValue("PermittedUsernames", m_PermittedUserNames?.Text); + OptionsManager.VotingFile.WriteValue("VoteablePrefix", m_VoteablePrefix?.Text); + OptionsManager.VotingFile.WriteValue("VotingChanceSystem", m_EnableProportionalVoting?.IsChecked); + OptionsManager.VotingFile.WriteValue("VotingChanceSystemRetainChance", m_EnableProportionalVotingRetainInitialChance?.IsChecked); } } } diff --git a/ConfigApp/Tabs/Voting/TwitchTab.cs b/ConfigApp/Tabs/Voting/TwitchTab.cs index b9b9faf16..b212849fb 100644 --- a/ConfigApp/Tabs/Voting/TwitchTab.cs +++ b/ConfigApp/Tabs/Voting/TwitchTab.cs @@ -70,23 +70,23 @@ public override void OnLoadValues() { if (m_EnableTwitchVoting is not null) { - m_EnableTwitchVoting.IsChecked = OptionsManager.TwitchFile.ReadValueBool("EnableVotingTwitch", false); + m_EnableTwitchVoting.IsChecked = OptionsManager.VotingFile.ReadValue("EnableVotingTwitch", false); SetElementsEnabled(m_EnableTwitchVoting.IsChecked.GetValueOrDefault()); } if (m_ChannelName is not null) - m_ChannelName.Text = OptionsManager.TwitchFile.ReadValue("TwitchChannelName"); + m_ChannelName.Text = OptionsManager.VotingFile.ReadValue("TwitchChannelName"); if (m_UserName is not null) - m_UserName.Text = OptionsManager.TwitchFile.ReadValue("TwitchUserName"); + m_UserName.Text = OptionsManager.VotingFile.ReadValue("TwitchUserName"); if (m_Token is not null) - m_Token.Password = OptionsManager.TwitchFile.ReadValue("TwitchChannelOAuth"); + m_Token.Password = OptionsManager.VotingFile.ReadValue("TwitchChannelOAuth"); } public override void OnSaveValues() { - OptionsManager.TwitchFile.WriteValue("EnableVotingTwitch", m_EnableTwitchVoting?.IsChecked); - OptionsManager.TwitchFile.WriteValue("TwitchChannelName", m_ChannelName?.Text); - OptionsManager.TwitchFile.WriteValue("TwitchUserName", m_UserName?.Text); - OptionsManager.TwitchFile.WriteValue("TwitchChannelOAuth", m_Token?.Password); + OptionsManager.VotingFile.WriteValue("EnableVotingTwitch", m_EnableTwitchVoting?.IsChecked); + OptionsManager.VotingFile.WriteValue("TwitchChannelName", m_ChannelName?.Text); + OptionsManager.VotingFile.WriteValue("TwitchUserName", m_UserName?.Text); + OptionsManager.VotingFile.WriteValue("TwitchChannelOAuth", m_Token?.Password); } } } diff --git a/ConfigApp/Utils.cs b/ConfigApp/Utils.cs index f0caa799b..fde29255b 100644 --- a/ConfigApp/Utils.cs +++ b/ConfigApp/Utils.cs @@ -2,47 +2,76 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using Newtonsoft.Json.Linq; namespace ConfigApp { public static class Utils { - public static EffectData ValueStringToEffectData(string? value) + public static EffectData ValuesArrayToEffectData(T? value) { var effectData = new EffectData(); if (value is null) return effectData; - // Split by comma, ignoring commas in between quotation marks - var values = Regex.Split(value, ",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))"); + dynamic[] values; + bool isNewConfig = true; + switch (value) + { + case JArray arrayValue: + values = arrayValue.Children().ToArray(); + break; + case string strValue: + // Split by comma, ignoring commas in between quotation marks + values = Regex.Split(strValue, ",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))"); + isNewConfig = false; + break; + default: + throw new NotImplementedException("Invalid Value Type"); + } - /* Has compatibility checks as previous mod versions had less options */ + // .ini configs stored everything (except CustomName) as an int + // Also has compatibility checks as very ancient mod versions had less options if (values.Length >= 4) { - if (int.TryParse(values[0], out int enabled)) - effectData.Enabled = enabled != 0; - - if (Enum.TryParse(values[1], out Effects.EffectTimedType timedType)) - effectData.TimedType = timedType != Effects.EffectTimedType.NotTimed ? timedType : null; - if (int.TryParse(values[2], out int customTime)) - effectData.CustomTime = customTime; - if (int.TryParse(values[3], out int weightMult)) - effectData.WeightMult = weightMult; + int enabled = isNewConfig ? (bool)values[0] ? 1 : 0 : int.Parse(values[0]); + effectData.Enabled = enabled != 0; + + Effects.EffectTimedType timedType = (Effects.EffectTimedType)(isNewConfig ? values[1] : Enum.Parse(typeof(Effects.EffectTimedType), values[1])); + effectData.TimedType = timedType != Effects.EffectTimedType.NotTimed ? timedType : null; + + int customTime = isNewConfig ? values[2] : int.Parse(values[2]); + effectData.CustomTime = customTime; + + int weightMult = isNewConfig ? values[3] : int.Parse(values[3]); + effectData.WeightMult = weightMult; } - if (values.Length >= 5 && int.TryParse(values[4], out int tmp) && tmp != 0) - effectData.TimedType = Effects.EffectTimedType.Permanent; + if (values.Length >= 5) + { + int isPermanent = isNewConfig ? (bool)values[4] ? 1 : 0 : int.Parse(values[4]); + if (isPermanent != 0) + effectData.TimedType = Effects.EffectTimedType.Permanent; + } - if (values.Length >= 6 && int.TryParse(values[5], out tmp)) - effectData.ExcludedFromVoting = tmp != 0; + if (values.Length >= 6) + { + int excludedFromVoting = isNewConfig ? (bool)values[5] ? 1 : 0 : int.Parse(values[5]); + effectData.ExcludedFromVoting = excludedFromVoting != 0; + } if (values.Length >= 7) - effectData.CustomName = values[6] == "0" ? null : values[6].Trim('\"'); + effectData.CustomName = values[6] == "0" ? null : ((string)values[6]).Trim('\"'); - if (values.Length >= 8 && int.TryParse(values[7], out int shortcut)) + if (values.Length >= 8) + { + int shortcut = isNewConfig ? values[7] : int.Parse(values[7]); effectData.ShortcutKeycode = shortcut; + } + + // New JSON-exclusive stored values below here, meaning we can properly use the stored type return effectData; } @@ -91,5 +120,14 @@ public static TextBox GenerateCommonNumericOnlyTextBox(int maxLength = 6, double return textBox; } + + public static bool IsNumeric(this T value) + { + if (value is null) + return false; + + var t = Nullable.GetUnderlyingType(value.GetType()) ?? value.GetType(); + return t.IsPrimitive || t == typeof(decimal); + } } } diff --git a/ConfigApp/Workshop/WorkshopSettingsDialog.xaml.cs b/ConfigApp/Workshop/WorkshopSettingsDialog.xaml.cs index 11d083ac9..32eacc661 100644 --- a/ConfigApp/Workshop/WorkshopSettingsDialog.xaml.cs +++ b/ConfigApp/Workshop/WorkshopSettingsDialog.xaml.cs @@ -15,7 +15,7 @@ public WorkshopSettingsDialog() { InitializeComponent(); - workshop_custom_url.Text = OptionsManager.WorkshopFile.ReadValue("WorkshopCustomUrl"); + workshop_custom_url.Text = OptionsManager.WorkshopFile.ReadValue("WorkshopCustomUrl"); workshop_custom_url.Watermark = Info.WORKSHOP_DEFAULT_URL; } diff --git a/Shared/OptionsFile.cs b/Shared/OptionsFile.cs index 0e2f977df..740ceb274 100644 --- a/Shared/OptionsFile.cs +++ b/Shared/OptionsFile.cs @@ -1,17 +1,22 @@ using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Shared { public class OptionsFile { - private readonly string m_FileName; - private readonly string[] m_CompatFileNames; - private Dictionary m_Options = new(); + public string FoundFilePath { get; private set; } = string.Empty; + public string[] CompatFilePaths { get; private set; } - public OptionsFile(string fileName, params string[] compatFileNames) + private readonly string m_FilePath; + private bool m_IsJson = false; + private Dictionary m_Options = new(); + + public OptionsFile(string filePath, params string[] compatFilePaths) { - m_FileName = fileName; - m_CompatFileNames = compatFileNames; + m_FilePath = filePath; + CompatFilePaths = compatFilePaths; } public bool HasKey(params string[] keys) @@ -31,7 +36,7 @@ public IEnumerable GetKeys() yield return option.Key; } - public string? ReadValue(string key, string? defaultValue = null, params string[] compatKeys) + public T? ReadValue(string key, T? defaultValue = default, params string[] compatKeys) { var keys = compatKeys.Prepend(key); foreach (var _key in keys) @@ -39,56 +44,37 @@ public IEnumerable GetKeys() if (!m_Options.ContainsKey(_key) || m_Options[_key] is null) continue; - return m_Options[_key]; - } - - return defaultValue; - } - - public int ReadValueInt(string key, int defaultValue, params string[] compatKeys) - { - if (!int.TryParse(ReadValue(key, null, compatKeys), out int result)) - result = defaultValue; - - return result; - } - - public long ReadValueLong(string key, long defaultValue, params string[] compatKeys) - { - if (!long.TryParse(ReadValue(key, null, compatKeys), out long result)) - result = defaultValue; - - return result; - } + if (typeof(T) == typeof(bool) && !m_IsJson) + { + string? str = ReadValue(_key, $"{((bool?)Convert.ChangeType(defaultValue, typeof(bool)) == true ? "1" : "0")}"); + if (str is null) + return defaultValue; - public bool ReadValueBool(string key, bool defaultValue, params string[] compatKeys) - { - var keys = compatKeys.Prepend(key); - foreach (var _key in keys) - { - if (!m_Options.ContainsKey(_key) || m_Options[_key] == null) - continue; + if (int.TryParse(str, out int result)) + return (T?)Convert.ChangeType(result != 0, typeof(T)); + } - if (int.TryParse(ReadValue(_key), out int result)) - return result != 0; + return m_Options[_key].ToObject(); } return defaultValue; } - public void WriteValue(string key, string? value) + public void WriteValue(string key, T? value) { - m_Options[key] = value is null || string.IsNullOrEmpty(value) ? null : value; + if (value is null || (value is string && string.IsNullOrEmpty(value as string))) + m_Options[key] = null; + else if (value is JObject o) + m_Options[key] = o; + else if (value is JArray a) + m_Options[key] = a; + else + m_Options[key] = new JValue(value); } - public void WriteValue(string key, int? value) + public void WriteValueAsInt(string key, string? value) { - WriteValue(key, $"{value}"); - } - - public void WriteValue(string key, bool? value) - { - WriteValue(key, value is true ? 1 : 0); + m_Options[key] = value is null || string.IsNullOrEmpty(value) ? null : new JValue(int.Parse(value)); } public void ReadFile() @@ -106,14 +92,17 @@ public void ReadFile() } string? data; - if ((data = readData(m_FileName)) == null) + if ((data = readData(m_FilePath)) != null) + FoundFilePath = m_FilePath; + else { bool dataRead = false; - foreach (var compatFileName in m_CompatFileNames) + foreach (var compatFileName in CompatFilePaths) { if ((data = readData(compatFileName)) != null) { dataRead = true; + FoundFilePath = compatFileName; break; } } @@ -124,46 +113,62 @@ public void ReadFile() m_Options.Clear(); - if (data is not null) + if (data is not null && FoundFilePath is not null) { - foreach (string line in data.Split('\n')) + if (FoundFilePath.EndsWith(".json")) + { + m_IsJson = true; + + var json = JObject.Parse(data); + foreach (var (key, value) in json) + m_Options[key] = value; + } + else if (FoundFilePath.EndsWith(".ini")) { - if (!line.Contains('=')) - continue; + m_IsJson = false; - var keyValuePair = line.Split('=', 2, System.StringSplitOptions.RemoveEmptyEntries - | System.StringSplitOptions.TrimEntries); + foreach (string line in data.Split('\n')) + { + if (!line.Contains('=')) + continue; - m_Options[keyValuePair[0]] = keyValuePair.Length == 2 ? keyValuePair[1] : null; + var keyValuePair = line.Split('=', 2, System.StringSplitOptions.RemoveEmptyEntries + | System.StringSplitOptions.TrimEntries); + + m_Options[keyValuePair[0]] = keyValuePair.Length == 2 ? new JValue(keyValuePair[1]) : null; + } } } } public void WriteFile() { - if (m_FileName is null) + if (m_FilePath is null) return; - string data = string.Empty; + FoundFilePath = m_FilePath; - foreach (var option in m_Options) - data += $"{option.Key}={option.Value}\n"; + var json = new JObject(); + foreach (var (key, value) in m_Options) + json[key] = value; - var directory = Path.GetDirectoryName(m_FileName); + var directory = Path.GetDirectoryName(m_FilePath); if (directory is not null) Directory.CreateDirectory(directory); - File.WriteAllText(m_FileName, data); + + File.WriteAllText(m_FilePath, JsonConvert.SerializeObject(json)); } public void ResetFile() { - if (m_FileName is null) + if (m_FilePath is null) return; - var directory = Path.GetDirectoryName(m_FileName); + var directory = Path.GetDirectoryName(m_FilePath); if (directory is not null) Directory.CreateDirectory(directory); - File.WriteAllText(m_FileName, null); + + File.WriteAllText(m_FilePath, null); // Reset all options too m_Options = new(); @@ -171,7 +176,7 @@ public void ResetFile() public bool HasCompatFile() { - foreach (var compatFileName in m_CompatFileNames) + foreach (var compatFileName in CompatFilePaths) { if (File.Exists(compatFileName)) return true; @@ -179,15 +184,5 @@ public bool HasCompatFile() return false; } - - public bool HasCompatFile(string fileName) - { - return m_CompatFileNames.Contains(fileName) && File.Exists(fileName); - } - - public string[] GetCompatFiles() - { - return m_CompatFileNames; - } } } diff --git a/TwitchChatVotingProxy/TwitchChatVotingProxy.cs b/TwitchChatVotingProxy/TwitchChatVotingProxy.cs index a7fa37a35..87a503e69 100644 --- a/TwitchChatVotingProxy/TwitchChatVotingProxy.cs +++ b/TwitchChatVotingProxy/TwitchChatVotingProxy.cs @@ -32,22 +32,22 @@ private static async Task Main(string[] args) m_Logger.Information("Starting chaos mod twitch proxy"); m_Logger.Information("==============================="); - var config = new OptionsFile("chaosmod/configs/voting.ini", "chaosmod/configs/twitch.ini", "chaosmod/twitch.ini"); + var config = new OptionsFile("chaosmod/configs/voting.json", "chaosmod/configs/voting.ini", "chaosmod/configs/twitch.ini", "chaosmod/twitch.ini"); config.ReadFile(); var mutex = new Mutex(false, "ChaosModVVotingMutex"); mutex.WaitOne(); - var votingMode = (EVotingMode)config.ReadValueInt("VotingChanceSystem", 0, "TwitchVotingChanceSystem"); - var overlayMode = (EOverlayMode)config.ReadValueInt("VotingOverlayMode", 0, "TwitchVotingOverlayMode"); - var retainInitialVotes = config.ReadValueBool("VotingChanceSystemRetainChance", false, "TwitchVotingChanceSystemRetainChance"); + var votingMode = (EVotingMode)config.ReadValue("VotingChanceSystem", 0, "TwitchVotingChanceSystem"); + var overlayMode = (EOverlayMode)config.ReadValue("VotingOverlayMode", 0, "TwitchVotingOverlayMode"); + var retainInitialVotes = config.ReadValue("VotingChanceSystemRetainChance", false, "TwitchVotingChanceSystemRetainChance"); // Check if OBS overlay should be shown OverlayServer.OverlayServer? overlayServer = null; if (overlayMode == EOverlayMode.OVERLAY_OBS) { // Create component - var overlayServerPort = config.ReadValueInt("OverlayServerPort", 9091); + var overlayServerPort = config.ReadValue("OverlayServerPort", 9091); var overlayServerConfig = new OverlayServerConfig(votingMode, retainInitialVotes, overlayServerPort); overlayServer = new OverlayServer.OverlayServer(overlayServerConfig); } @@ -56,9 +56,9 @@ private static async Task Main(string[] args) var chaosPipe = new ChaosPipeClient(); var votingReceivers = new List<(string Name, IVotingReceiver VotingReceiver)>(); - if (config.ReadValueBool("EnableVotingTwitch", false)) + if (config.ReadValue("EnableVotingTwitch", false)) votingReceivers.Add(("Twitch", new TwitchVotingReceiver(config, chaosPipe))); - if (config.ReadValueBool("EnableVotingDiscord", false)) + if (config.ReadValue("EnableVotingDiscord", false)) votingReceivers.Add(("Discord", new DiscordVotingReceiver(config, chaosPipe))); foreach (var votingReceiver in votingReceivers) diff --git a/TwitchChatVotingProxy/VotingReceiver/DiscordVotingReceiver.cs b/TwitchChatVotingProxy/VotingReceiver/DiscordVotingReceiver.cs index a1d658e66..07898d04c 100644 --- a/TwitchChatVotingProxy/VotingReceiver/DiscordVotingReceiver.cs +++ b/TwitchChatVotingProxy/VotingReceiver/DiscordVotingReceiver.cs @@ -26,9 +26,9 @@ class DiscordVotingReceiver : IVotingReceiver public DiscordVotingReceiver(OptionsFile config, ChaosPipeClient chaosPipe) { - m_BotToken = config.ReadValue("DiscordBotToken"); - m_GuildId = (ulong)config.ReadValueLong("DiscordGuildId", 0); - m_ChannelId = (ulong)config.ReadValueLong("DiscordChannelId", 0); + m_BotToken = config.ReadValue("DiscordBotToken"); + m_GuildId = config.ReadValue("DiscordGuildId", 0); + m_ChannelId = config.ReadValue("DiscordChannelId", 0); m_ChaosPipe = chaosPipe; } diff --git a/TwitchChatVotingProxy/VotingReceiver/TwitchVotingReceiver.cs b/TwitchChatVotingProxy/VotingReceiver/TwitchVotingReceiver.cs index 37af5479d..45621da2c 100644 --- a/TwitchChatVotingProxy/VotingReceiver/TwitchVotingReceiver.cs +++ b/TwitchChatVotingProxy/VotingReceiver/TwitchVotingReceiver.cs @@ -30,9 +30,9 @@ class TwitchVotingReceiver : IVotingReceiver public TwitchVotingReceiver(OptionsFile config, ChaosPipeClient chaosPipe) { - m_ChannelName = config.ReadValue("TwitchChannelName"); - m_OAuth = config.ReadValue("TwitchChannelOAuth"); - m_UserName = config.ReadValue("TwitchUserName"); + m_ChannelName = config.ReadValue("TwitchChannelName"); + m_OAuth = config.ReadValue("TwitchChannelOAuth"); + m_UserName = config.ReadValue("TwitchUserName"); m_ChaosPipe = chaosPipe; }