From 1032d9afa8ccbc45880941ac4a4615aee55f5765 Mon Sep 17 00:00:00 2001 From: HJfod <60038575+HJfod@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:31:41 +0300 Subject: [PATCH] no longer crashes on startup :3 --- loader/include/Geode/loader/SettingV3.hpp | 40 +++++++++++++++- loader/include/Geode/platform/cplatform.h | 4 ++ loader/include/Geode/utils/JsonValidation.hpp | 3 +- loader/src/loader/Mod.cpp | 4 +- loader/src/loader/ModMetadataImpl.cpp | 3 +- loader/src/loader/SettingV3.cpp | 48 ++++++++++++------- loader/src/utils/JsonValidation.cpp | 10 ++-- 7 files changed, 85 insertions(+), 27 deletions(-) diff --git a/loader/include/Geode/loader/SettingV3.hpp b/loader/include/Geode/loader/SettingV3.hpp index 794bea31..91af75ad 100644 --- a/loader/include/Geode/loader/SettingV3.hpp +++ b/loader/include/Geode/loader/SettingV3.hpp @@ -6,6 +6,8 @@ // todo: remove this header in 4.0.0 #include "Setting.hpp" #include "../utils/cocos.hpp" +// this unfortunately has to be included because of C++ templates +#include "../utils/JsonValidation.hpp" // todo in v4: these can be removed as well as the friend decl in UnresolvedCustomSettingV3 class ModSettingsManager; @@ -13,7 +15,6 @@ class LegacyCustomSettingToV3Node; namespace geode { class SettingNodeV3; - class JsonExpectedValue; class GEODE_DLL SettingV3 : public std::enable_shared_from_this { private: @@ -70,11 +71,28 @@ namespace geode { class Impl; std::shared_ptr m_impl; + Result<> parseSharedBase(JsonExpectedValue& json); + protected: - Result<> parseShared(JsonExpectedValue& json); Result<> isValidShared() const; + template + Result<> parseShared(JsonExpectedValue& json, T& defaultValue) { + GEODE_UNWRAP(this->parseSharedBase(json)); + auto value = json.needs("default"); + // Check if this is a platform-specific default value + if (value.isObject() && value.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) { + value.needs(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH).into(defaultValue); + } + else { + value.into(defaultValue); + } + return Ok(); + } + public: + GeodeSettingBaseV3(); + std::string getName() const; std::optional getDescription() const; std::optional getEnableIf() const; @@ -116,6 +134,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + TitleSettingV3(); + std::string getTitle() const; bool load(matjson::Value const& json) override; @@ -138,6 +158,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + UnresolvedCustomSettingV3(); + bool load(matjson::Value const& json) override; bool save(matjson::Value& json) const override; SettingNodeV3* createNode(float width) override; @@ -159,6 +181,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + BoolSettingV3(); + bool getDefaultValue() const override; Result<> isValid(bool value) const override; @@ -180,6 +204,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + IntSettingV3(); + int64_t getDefaultValue() const override; Result<> isValid(int64_t value) const override; @@ -212,6 +238,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + FloatSettingV3(); + double getDefaultValue() const override; Result<> isValid(double value) const override; @@ -244,6 +272,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + StringSettingV3(); + std::string getDefaultValue() const override; Result<> isValid(std::string_view value) const override; @@ -269,6 +299,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + FileSettingV3(); + std::filesystem::path getDefaultValue() const override; Result<> isValid(std::filesystem::path const& value) const override; @@ -292,6 +324,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + Color3BSettingV3(); + cocos2d::ccColor3B getDefaultValue() const override; Result<> isValid(cocos2d::ccColor3B value) const override; @@ -313,6 +347,8 @@ namespace geode { Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; public: + Color4BSettingV3(); + cocos2d::ccColor4B getDefaultValue() const override; Result<> isValid(cocos2d::ccColor4B value) const override; diff --git a/loader/include/Geode/platform/cplatform.h b/loader/include/Geode/platform/cplatform.h index c930d4c2..54676313 100644 --- a/loader/include/Geode/platform/cplatform.h +++ b/loader/include/Geode/platform/cplatform.h @@ -16,6 +16,7 @@ #define GEODE_PLATFORM_NAME "Windows" #define GEODE_PLATFORM_EXTENSION ".dll" #define GEODE_PLATFORM_SHORT_IDENTIFIER "win" + #define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "win" #define CC_TARGET_OS_WIN32 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__) @@ -47,6 +48,7 @@ #define GEODE_PLATFORM_NAME "iOS" #define GEODE_PLATFORM_EXTENSION ".ios.dylib" #define GEODE_PLATFORM_SHORT_IDENTIFIER "ios" + #define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "ios" #define CC_TARGET_OS_IPHONE #else #define GEODE_IOS(...) @@ -54,6 +56,7 @@ #define GEODE_IS_MACOS #define GEODE_IS_DESKTOP #define GEODE_PLATFORM_EXTENSION ".dylib" + #define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "mac" #define CC_TARGET_OS_MAC #if TARGET_CPU_ARM64 @@ -85,6 +88,7 @@ #define GEODE_IS_MOBILE #define GEODE_CALL #define CC_TARGET_OS_ANDROID + #define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "android" #if defined(__arm__) #define GEODE_ANDROID32(...) __VA_ARGS__ diff --git a/loader/include/Geode/utils/JsonValidation.hpp b/loader/include/Geode/utils/JsonValidation.hpp index 6b0cf6d1..3f0d6ef8 100644 --- a/loader/include/Geode/utils/JsonValidation.hpp +++ b/loader/include/Geode/utils/JsonValidation.hpp @@ -321,7 +321,8 @@ namespace geode { try { return this->getJSONRef().template as(); } - catch(matjson::JsonException const& e) { + // matjson can throw variant exceptions too so you need to do this + catch(std::exception const& e) { this->setError("invalid json type: {}", e); } return std::nullopt; diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp index bb2d5c46..fa54fb97 100644 --- a/loader/src/loader/Mod.cpp +++ b/loader/src/loader/Mod.cpp @@ -164,9 +164,7 @@ SettingValue* Mod::getSetting(std::string_view const key) const { } std::shared_ptr Mod::getSettingV3(std::string_view const key) const { - auto sett = m_impl->m_settings->get(std::string(key)); - (void)file::writeString(".AAAAAk2", fmt::format("got it: {}, {}", key, fmt::ptr(sett))); - return sett; + return m_impl->m_settings->get(std::string(key)); } void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr value) { diff --git a/loader/src/loader/ModMetadataImpl.cpp b/loader/src/loader/ModMetadataImpl.cpp index d7e397c2..e4029d37 100644 --- a/loader/src/loader/ModMetadataImpl.cpp +++ b/loader/src/loader/ModMetadataImpl.cpp @@ -125,8 +125,6 @@ Result ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs } catch (...) { } - return Ok(info); - auto root = checkJson(impl->m_rawJSON, checkerRoot); root.needs("geode").into(impl->m_geodeVersion); @@ -283,6 +281,7 @@ Result ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs impl->m_binaryName = impl->m_id + GEODE_PLATFORM_EXTENSION; root.checkUnknownKeys(); + return root.ok(info); } diff --git a/loader/src/loader/SettingV3.cpp b/loader/src/loader/SettingV3.cpp index f1cec18d..e919dd9e 100644 --- a/loader/src/loader/SettingV3.cpp +++ b/loader/src/loader/SettingV3.cpp @@ -67,6 +67,8 @@ public: std::optional enableIf; }; +geode::detail::GeodeSettingBaseV3::GeodeSettingBaseV3() : m_impl(std::make_shared()) {} + std::string geode::detail::GeodeSettingBaseV3::getName() const { return m_impl->name.value_or(this->getKey()); } @@ -77,7 +79,11 @@ std::optional geode::detail::GeodeSettingBaseV3::getEnableIf() cons return m_impl->enableIf; } -Result<> geode::detail::GeodeSettingBaseV3::parseShared(JsonExpectedValue& json) { +Result<> geode::detail::GeodeSettingBaseV3::parseSharedBase(JsonExpectedValue& json) { + // Mark keys that have been checked before-hand + json.needs("type"); + json.has("platforms"); + json.has("name").into(m_impl->name); json.has("description").into(m_impl->description); json.has("enable-if").into(m_impl->enableIf); @@ -94,6 +100,8 @@ public: std::string title; }; +TitleSettingV3::TitleSettingV3() : m_impl(std::make_shared()) {} + std::string TitleSettingV3::getTitle() const { return m_impl->title; } @@ -126,6 +134,8 @@ void TitleSettingV3::reset() {} // replace this comment with it // put it riiiiiight here +UnresolvedCustomSettingV3::UnresolvedCustomSettingV3() : m_impl(std::make_shared()) {} + Result<> UnresolvedCustomSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { m_impl->json = json; return Ok(); @@ -167,6 +177,8 @@ public: bool defaultValue; }; +BoolSettingV3::BoolSettingV3() : m_impl(std::make_shared()) {} + bool& BoolSettingV3::getValueMut() const { return m_impl->value; } @@ -181,8 +193,7 @@ Result<> BoolSettingV3::isValid(bool value) const { Result<> BoolSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "BoolSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); m_impl->value = m_impl->defaultValue; root.checkUnknownKeys(); @@ -233,6 +244,8 @@ public: } controls; }; +IntSettingV3::IntSettingV3() : m_impl(std::make_shared()) {} + int64_t& IntSettingV3::getValueMut() const { return m_impl->value; } @@ -282,8 +295,7 @@ bool IntSettingV3::isInputEnabled() const { Result<> IntSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "IntSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); m_impl->value = m_impl->defaultValue; root.has("min").into(m_impl->minValue); @@ -362,6 +374,8 @@ public: } controls; }; +FloatSettingV3::FloatSettingV3() : m_impl(std::make_shared()) {} + double& FloatSettingV3::getValueMut() const { return m_impl->value; } @@ -411,8 +425,7 @@ bool FloatSettingV3::isInputEnabled() const { Result<> FloatSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "FloatSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); m_impl->value = m_impl->defaultValue; root.has("min").into(m_impl->minValue); @@ -483,6 +496,8 @@ public: std::optional> oneOf; }; +StringSettingV3::StringSettingV3() : m_impl(std::make_shared()) {} + std::string& StringSettingV3::getValueMut() const { return m_impl->value; } @@ -517,8 +532,7 @@ std::optional> StringSettingV3::getEnumOptions() const Result<> StringSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "StringSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); m_impl->value = m_impl->defaultValue; root.has("match").into(m_impl->match); @@ -566,6 +580,8 @@ public: std::optional> filters; }; +FileSettingV3::FileSettingV3() : m_impl(std::make_shared()) {} + std::filesystem::path& FileSettingV3::getValueMut() const { return m_impl->value; } @@ -584,9 +600,7 @@ std::optional> FileSettingV3:: Result<> FileSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "FileSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); // Replace known paths like `{gd-save-dir}/` try { @@ -655,6 +669,8 @@ public: ccColor3B defaultValue; }; +Color3BSettingV3::Color3BSettingV3() : m_impl(std::make_shared()) {} + ccColor3B& Color3BSettingV3::getValueMut() const { return m_impl->value; } @@ -669,8 +685,7 @@ Result<> Color3BSettingV3::isValid(ccColor3B value) const { Result<> Color3BSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "Color3BSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); m_impl->value = m_impl->defaultValue; root.checkUnknownKeys(); @@ -710,6 +725,8 @@ public: ccColor4B defaultValue; }; +Color4BSettingV3::Color4BSettingV3() : m_impl(std::make_shared()) {} + ccColor4B& Color4BSettingV3::getValueMut() const { return m_impl->value; } @@ -724,8 +741,7 @@ Result<> Color4BSettingV3::isValid(ccColor4B value) const { Result<> Color4BSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { auto root = checkJson(json, "Color4BSettingV3"); - GEODE_UNWRAP(this->parseShared(root)); - root.needs("default").into(m_impl->defaultValue); + GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue)); m_impl->value = m_impl->defaultValue; root.checkUnknownKeys(); diff --git a/loader/src/utils/JsonValidation.cpp b/loader/src/utils/JsonValidation.cpp index 7af8adf9..3cd4489b 100644 --- a/loader/src/utils/JsonValidation.cpp +++ b/loader/src/utils/JsonValidation.cpp @@ -320,6 +320,7 @@ JsonExpectedValue JsonExpectedValue::has(std::string_view key) { if (!this->assertIs(matjson::Type::Object)) { return JsonExpectedValue(); } + m_impl->knownKeys.insert(std::string(key)); if (!m_impl->scope.contains(key)) { return JsonExpectedValue(); } @@ -332,6 +333,7 @@ JsonExpectedValue JsonExpectedValue::needs(std::string_view key) { if (!this->assertIs(matjson::Type::Object)) { return JsonExpectedValue(); } + m_impl->knownKeys.insert(std::string(key)); if (!m_impl->scope.contains(key)) { this->setError("missing required key {}", key); return JsonExpectedValue(); @@ -352,8 +354,9 @@ std::vector> JsonExpectedValue::proper return res; } void JsonExpectedValue::checkUnknownKeys() { - for (auto& [key, _] : this->properties()) { - if (!m_impl->knownKeys.count(key)) { + if (this->hasError()) return; + for (auto&& [key, _] : this->properties()) { + if (!m_impl->knownKeys.contains(key)) { log::warn("{} contains unknown key \"{}\"", m_impl->scopeName, key); } } @@ -401,7 +404,8 @@ std::vector JsonExpectedValue::items() { } JsonExpectedValue::operator bool() const { - return !this->hasError(); + // The shared check is because null values should evaluate to false so `obj.has("key")` evaluates to false + return m_impl->shared && !this->hasError(); } Result<> JsonExpectedValue::ok() {