From 0efde83fd4bbf4ef0fbac857e7f514eb4b00a2a3 Mon Sep 17 00:00:00 2001 From: HJfod <60038575+HJfod@users.noreply.github.com> Date: Mon, 9 Sep 2024 00:50:49 +0300 Subject: [PATCH] allow fine-grained parsing for settings --- loader/include/Geode/loader/SettingV3.hpp | 138 ++++++++++++++++++++-- loader/src/loader/SettingV3.cpp | 69 +++++++---- 2 files changed, 171 insertions(+), 36 deletions(-) diff --git a/loader/include/Geode/loader/SettingV3.hpp b/loader/include/Geode/loader/SettingV3.hpp index 1a78ffe5f..a46ce508f 100644 --- a/loader/include/Geode/loader/SettingV3.hpp +++ b/loader/include/Geode/loader/SettingV3.hpp @@ -26,9 +26,98 @@ namespace geode { std::shared_ptr m_impl; protected: + /** + * Only call this function if you aren't going to call + * `parseBaseProperties`, which will call it for you! + * If you don't want to call `parseBaseProperties`, at the very least + * you **must** call this! + * Select which properties you want to parse using the `parseX` + * functions + * @param key The setting's key as defined in `mod.json` + * @param modID The ID of the mod this settings is being parsed for + * @param json The current JSON checking instance being used. This + * should be the JSON object that defines the setting. If you aren't + * using Geode's JSON checking utilities, you can use the other + * overload of `init` + */ + void init(std::string const& key, std::string const& modID, JsonExpectedValue& json); + /** + * Only call this function if you aren't going to call + * `parseBaseProperties`, which will call it for you! + * If you don't want to call `parseBaseProperties`, at the very least + * you **must** call this! + * Select which properties you want to parse using the `parseX` + * functions + * @param key The setting's key as defined in `mod.json` + * @param modID The ID of the mod this settings is being parsed for + * @note If you are using Geode's JSON checking utilities + * (`checkJson` / `JsonExpectedValue`), you should be using the other + * overload that takes a `JsonExpectedValue&`! + */ void init(std::string const& key, std::string const& modID); - Result<> parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc = false); - void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc = false); + + /** + * Parses the `"name"` and `"description"` keys from the setting's + * definition in `mod.json` (if they exist), so their values can be + * accessed via `getName` and `getDescription`. + * @param json The current JSON checking instance being used. This + * should be the JSON object that defines the setting + * @warning In most cases, you should be using `parseBaseProperties` + * instead to do all of this in one go! + * If you do need the fine-grained control however, make sure to call + * `init` before calling these parsing functions! + */ + void parseNameAndDescription(JsonExpectedValue& json); + /** + * Parses the `"enable-if"` and `"enable-if-description"` keys from + * the setting's definition in `mod.json` (if they exist), so + * `shouldEnable` and `getEnableIfDescription` work. + * @param json The current JSON checking instance being used. This + * should be the JSON object that defines the setting + * @warning In most cases, you should be using `parseBaseProperties` + * instead to do all of this in one go! + * If you do need the fine-grained control however, make sure to call + * `init` before calling these parsing functions! + */ + void parseEnableIf(JsonExpectedValue& json); + /** + * Parses the `"requires-restart"` key from the setting's definition in + * `mod.json` (if they exist), so `requiresRestart` works. + * @param json The current JSON checking instance being used. This + * should be the JSON object that defines the setting + * @warning In most cases, you should be using `parseBaseProperties` + * instead to do all of this in one go! + * If you do need the fine-grained control however, make sure to call + * `init` before calling these parsing functions! + */ + void parseValueProperties(JsonExpectedValue& json); + + /** + * Parse all of the base properties such as `"name"` and `"description"` + * for this setting + * @param key The setting's key as defined in `mod.json` + * @param modID The ID of the mod this settings is being parsed for + * @param json The current JSON checking instance being used. If you + * aren't using Geode's JSON checking utilities, use the other overload + * of this function + * @note If you don't want to parse some of the base properties, such as + * `"requires-restart"` (because you're doing a cosmetic setting), then + * you can call `init` instead and then the specific `parseX` functions + */ + void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json); + /** + * Parse all of the base properties such as `"name"` and `"description"` + * for this setting + * @param key The setting's key as defined in `mod.json` + * @param modID The ID of the mod this settings is being parsed for + * @param json The JSON value. If you are using Geode's JSON checking + * utilities (`checkJson` / `JsonExpectedValue`), you should use the + * other overload directly! + * @note If you don't want to parse some of the base properties, such as + * `"requires-restart"` (because you're doing a cosmetic setting), then + * you can call `init` instead and then the specific `parseX` functions + */ + Result<> parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& json); /** * Mark that the value of this setting has changed. This should be @@ -80,6 +169,10 @@ namespace geode { * Whether this setting requires a restart on change */ bool requiresRestart() const; + /** + * Get the platforms this setting is available on + */ + std::vector getPlatforms() const; virtual bool load(matjson::Value const& json) = 0; virtual bool save(matjson::Value& json) const = 0; @@ -91,9 +184,15 @@ namespace geode { */ virtual void reset() = 0; - [[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]] + [[deprecated( + "This function will be removed alongside legacy settings in 4.0.0! " + "You should NOT be implementing it for your own custom setting classes" + )]] virtual std::optional convertToLegacy() const; - [[deprecated("This function will be removed alongside legacy settings in 4.0.0!")]] + [[deprecated( + "This function will be removed alongside legacy settings in 4.0.0! " + "You should NOT be implementing it for your own custom setting classes" + )]] virtual std::optional> convertToLegacyValue() const; }; @@ -123,15 +222,17 @@ namespace geode { protected: /** - * Parse shared value, including the default value for this setting - * @param key The key of the setting - * @param modID The ID of the mod this setting is being parsed for - * @param json The current JSON checking instance being used. If you - * aren't using Geode's JSON checking utilities, use the other overload - * of this function + * Parses the `"default"` key from the setting's definition in + * `mod.json`. The key may also be defined per-platform, i.e. + * `"default": { "win": ..., "android": ... }` + * @param json The current JSON checking instance being used. This + * should be the JSON object that defines the setting + * @warning In most cases, you should be using `parseBaseProperties` + * instead to do all of this in one go! + * If you do need the fine-grained control however, make sure to call + * `init` before calling these parsing functions! */ - void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json) { - SettingV3::parseBaseProperties(key, modID, json, false); + void parseDefaultValue(JsonExpectedValue& json) { auto root = json.needs("default"); // Check if this is a platform-specific default value if (root.isObject() && root.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) { @@ -142,6 +243,19 @@ namespace geode { } m_impl->value = m_impl->defaultValue; } + + /** + * Parse shared value, including the default value for this setting + * @param key The key of the setting + * @param modID The ID of the mod this setting is being parsed for + * @param json The current JSON checking instance being used. If you + * aren't using Geode's JSON checking utilities, use the other overload + * of this function + */ + void parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json) { + SettingV3::parseBaseProperties(key, modID, json); + this->parseDefaultValue(json); + } /** * Parse shared value, including the default value for this setting * @param key The key of the setting diff --git a/loader/src/loader/SettingV3.cpp b/loader/src/loader/SettingV3.cpp index 43e8e937c..1c7b8c2e5 100644 --- a/loader/src/loader/SettingV3.cpp +++ b/loader/src/loader/SettingV3.cpp @@ -460,6 +460,7 @@ class SettingV3::GeodeImpl { public: std::string modID; std::string key; + std::vector platforms; std::optional name; std::optional description; std::optional enableIf; @@ -471,33 +472,49 @@ class SettingV3::GeodeImpl { SettingV3::SettingV3() : m_impl(std::make_shared()) {} SettingV3::~SettingV3() = default; -Result<> SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& value, bool onlyNameAndDesc) { - auto json = checkJson(value, "SettingV3"); - this->parseBaseProperties(key, modID, json, onlyNameAndDesc); - return json.ok(); +void SettingV3::init(std::string const& key, std::string const& modID) { + m_impl->key = key; + m_impl->modID = modID; } -void SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc) { +void SettingV3::init(std::string const& key, std::string const& modID, JsonExpectedValue& json) { this->init(key, modID); - value.needs("type"); - value.has("platforms"); - value.has("name").into(m_impl->name); - value.has("description").into(m_impl->description); - if (!onlyNameAndDesc) { - value.has("requires-restart").into(m_impl->requiresRestart); - value.has("enable-if") - .template mustBe("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> { - GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID)); - GEODE_UNWRAP(tree->check()); - m_impl->enableIfTree = std::move(tree); - return Ok(); - }) - .into(m_impl->enableIf); - value.has("enable-if-description").into(m_impl->enableIfDescription); + + // Keys every setting must have + json.needs("type"); + for (auto& plat : json.has("platforms").items()) { + m_impl->platforms.push_back(PlatformID::from(plat.template get())); } } -void SettingV3::init(std::string const& key, std::string const& modID) { - m_impl->key = key; - m_impl->modID = modID; + +void SettingV3::parseNameAndDescription(JsonExpectedValue& json) { + json.needs("name").into(m_impl->name); + json.has("description").into(m_impl->description); +} +void SettingV3::parseEnableIf(JsonExpectedValue& json) { + json.has("enable-if") + .template mustBe("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> { + GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID)); + GEODE_UNWRAP(tree->check()); + m_impl->enableIfTree = std::move(tree); + return Ok(); + }) + .into(m_impl->enableIf); + json.has("enable-if-description").into(m_impl->enableIfDescription); +} +void SettingV3::parseValueProperties(JsonExpectedValue& json) { + json.has("requires-restart").into(m_impl->requiresRestart); +} + +Result<> SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, matjson::Value const& value) { + auto json = checkJson(value, "SettingV3"); + this->parseBaseProperties(key, modID, json); + return json.ok(); +} +void SettingV3::parseBaseProperties(std::string const& key, std::string const& modID, JsonExpectedValue& json) { + this->init(key, modID, json); + this->parseNameAndDescription(json); + this->parseValueProperties(json); + this->parseEnableIf(json); } std::string SettingV3::getKey() const { @@ -540,6 +557,9 @@ std::optional SettingV3::getEnableIfDescription() const { bool SettingV3::requiresRestart() const { return m_impl->requiresRestart; } +std::vector SettingV3::getPlatforms() const { + return m_impl->platforms; +} Mod* SettingV3::getMod() const { return Loader::get()->getInstalledMod(m_impl->modID); } @@ -573,7 +593,8 @@ TitleSettingV3::TitleSettingV3(PrivateMarker) : m_impl(std::make_shared()) Result> TitleSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto ret = std::make_shared(PrivateMarker()); auto root = checkJson(json, "TitleSettingV3"); - ret->parseBaseProperties(key, modID, root, true); + ret->init(key, modID, root); + ret->parseNameAndDescription(root); root.checkUnknownKeys(); return root.ok(ret); }