no longer crashes on startup :3

This commit is contained in:
HJfod 2024-08-20 00:31:41 +03:00
parent 90ed885d89
commit 1032d9afa8
7 changed files with 85 additions and 27 deletions

View file

@ -6,6 +6,8 @@
// todo: remove this header in 4.0.0 // todo: remove this header in 4.0.0
#include "Setting.hpp" #include "Setting.hpp"
#include "../utils/cocos.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 // todo in v4: these can be removed as well as the friend decl in UnresolvedCustomSettingV3
class ModSettingsManager; class ModSettingsManager;
@ -13,7 +15,6 @@ class LegacyCustomSettingToV3Node;
namespace geode { namespace geode {
class SettingNodeV3; class SettingNodeV3;
class JsonExpectedValue;
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> { class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
private: private:
@ -70,11 +71,28 @@ namespace geode {
class Impl; class Impl;
std::shared_ptr<Impl> m_impl; std::shared_ptr<Impl> m_impl;
Result<> parseSharedBase(JsonExpectedValue& json);
protected: protected:
Result<> parseShared(JsonExpectedValue& json);
Result<> isValidShared() const; Result<> isValidShared() const;
template <class T>
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: public:
GeodeSettingBaseV3();
std::string getName() const; std::string getName() const;
std::optional<std::string> getDescription() const; std::optional<std::string> getDescription() const;
std::optional<std::string> getEnableIf() const; std::optional<std::string> getEnableIf() const;
@ -116,6 +134,8 @@ namespace geode {
Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
TitleSettingV3();
std::string getTitle() const; std::string getTitle() const;
bool load(matjson::Value const& json) override; 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
UnresolvedCustomSettingV3();
bool load(matjson::Value const& json) override; bool load(matjson::Value const& json) override;
bool save(matjson::Value& json) const override; bool save(matjson::Value& json) const override;
SettingNodeV3* createNode(float width) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
BoolSettingV3();
bool getDefaultValue() const override; bool getDefaultValue() const override;
Result<> isValid(bool value) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
IntSettingV3();
int64_t getDefaultValue() const override; int64_t getDefaultValue() const override;
Result<> isValid(int64_t value) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
FloatSettingV3();
double getDefaultValue() const override; double getDefaultValue() const override;
Result<> isValid(double value) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
StringSettingV3();
std::string getDefaultValue() const override; std::string getDefaultValue() const override;
Result<> isValid(std::string_view value) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
FileSettingV3();
std::filesystem::path getDefaultValue() const override; std::filesystem::path getDefaultValue() const override;
Result<> isValid(std::filesystem::path const& value) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
Color3BSettingV3();
cocos2d::ccColor3B getDefaultValue() const override; cocos2d::ccColor3B getDefaultValue() const override;
Result<> isValid(cocos2d::ccColor3B value) 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; Result<> onParse(std::string const& key, std::string const& modID, matjson::Value const& json) override;
public: public:
Color4BSettingV3();
cocos2d::ccColor4B getDefaultValue() const override; cocos2d::ccColor4B getDefaultValue() const override;
Result<> isValid(cocos2d::ccColor4B value) const override; Result<> isValid(cocos2d::ccColor4B value) const override;

View file

@ -16,6 +16,7 @@
#define GEODE_PLATFORM_NAME "Windows" #define GEODE_PLATFORM_NAME "Windows"
#define GEODE_PLATFORM_EXTENSION ".dll" #define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win" #define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "win"
#define CC_TARGET_OS_WIN32 #define CC_TARGET_OS_WIN32
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__) #if defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
@ -47,6 +48,7 @@
#define GEODE_PLATFORM_NAME "iOS" #define GEODE_PLATFORM_NAME "iOS"
#define GEODE_PLATFORM_EXTENSION ".ios.dylib" #define GEODE_PLATFORM_EXTENSION ".ios.dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "ios" #define GEODE_PLATFORM_SHORT_IDENTIFIER "ios"
#define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "ios"
#define CC_TARGET_OS_IPHONE #define CC_TARGET_OS_IPHONE
#else #else
#define GEODE_IOS(...) #define GEODE_IOS(...)
@ -54,6 +56,7 @@
#define GEODE_IS_MACOS #define GEODE_IS_MACOS
#define GEODE_IS_DESKTOP #define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_EXTENSION ".dylib" #define GEODE_PLATFORM_EXTENSION ".dylib"
#define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "mac"
#define CC_TARGET_OS_MAC #define CC_TARGET_OS_MAC
#if TARGET_CPU_ARM64 #if TARGET_CPU_ARM64
@ -85,6 +88,7 @@
#define GEODE_IS_MOBILE #define GEODE_IS_MOBILE
#define GEODE_CALL #define GEODE_CALL
#define CC_TARGET_OS_ANDROID #define CC_TARGET_OS_ANDROID
#define GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH "android"
#if defined(__arm__) #if defined(__arm__)
#define GEODE_ANDROID32(...) __VA_ARGS__ #define GEODE_ANDROID32(...) __VA_ARGS__

View file

@ -321,7 +321,8 @@ namespace geode {
try { try {
return this->getJSONRef().template as<T>(); return this->getJSONRef().template as<T>();
} }
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); this->setError("invalid json type: {}", e);
} }
return std::nullopt; return std::nullopt;

View file

@ -164,9 +164,7 @@ SettingValue* Mod::getSetting(std::string_view const key) const {
} }
std::shared_ptr<SettingV3> Mod::getSettingV3(std::string_view const key) const { std::shared_ptr<SettingV3> Mod::getSettingV3(std::string_view const key) const {
auto sett = m_impl->m_settings->get(std::string(key)); return m_impl->m_settings->get(std::string(key));
(void)file::writeString(".AAAAAk2", fmt::format("got it: {}, {}", key, fmt::ptr(sett)));
return sett;
} }
void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) { void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) {

View file

@ -125,8 +125,6 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
} }
catch (...) { } catch (...) { }
return Ok(info);
auto root = checkJson(impl->m_rawJSON, checkerRoot); auto root = checkJson(impl->m_rawJSON, checkerRoot);
root.needs("geode").into(impl->m_geodeVersion); root.needs("geode").into(impl->m_geodeVersion);
@ -283,6 +281,7 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
impl->m_binaryName = impl->m_id + GEODE_PLATFORM_EXTENSION; impl->m_binaryName = impl->m_id + GEODE_PLATFORM_EXTENSION;
root.checkUnknownKeys(); root.checkUnknownKeys();
return root.ok(info); return root.ok(info);
} }

View file

@ -67,6 +67,8 @@ public:
std::optional<std::string> enableIf; std::optional<std::string> enableIf;
}; };
geode::detail::GeodeSettingBaseV3::GeodeSettingBaseV3() : m_impl(std::make_shared<Impl>()) {}
std::string geode::detail::GeodeSettingBaseV3::getName() const { std::string geode::detail::GeodeSettingBaseV3::getName() const {
return m_impl->name.value_or(this->getKey()); return m_impl->name.value_or(this->getKey());
} }
@ -77,7 +79,11 @@ std::optional<std::string> geode::detail::GeodeSettingBaseV3::getEnableIf() cons
return m_impl->enableIf; 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("name").into(m_impl->name);
json.has("description").into(m_impl->description); json.has("description").into(m_impl->description);
json.has("enable-if").into(m_impl->enableIf); json.has("enable-if").into(m_impl->enableIf);
@ -94,6 +100,8 @@ public:
std::string title; std::string title;
}; };
TitleSettingV3::TitleSettingV3() : m_impl(std::make_shared<Impl>()) {}
std::string TitleSettingV3::getTitle() const { std::string TitleSettingV3::getTitle() const {
return m_impl->title; return m_impl->title;
} }
@ -126,6 +134,8 @@ void TitleSettingV3::reset() {}
// replace this comment with it // replace this comment with it
// put it riiiiiight here // put it riiiiiight here
UnresolvedCustomSettingV3::UnresolvedCustomSettingV3() : m_impl(std::make_shared<Impl>()) {}
Result<> UnresolvedCustomSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { Result<> UnresolvedCustomSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
m_impl->json = json; m_impl->json = json;
return Ok(); return Ok();
@ -167,6 +177,8 @@ public:
bool defaultValue; bool defaultValue;
}; };
BoolSettingV3::BoolSettingV3() : m_impl(std::make_shared<Impl>()) {}
bool& BoolSettingV3::getValueMut() const { bool& BoolSettingV3::getValueMut() const {
return m_impl->value; 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) { Result<> BoolSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "BoolSettingV3"); auto root = checkJson(json, "BoolSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
m_impl->value = m_impl->defaultValue; m_impl->value = m_impl->defaultValue;
root.checkUnknownKeys(); root.checkUnknownKeys();
@ -233,6 +244,8 @@ public:
} controls; } controls;
}; };
IntSettingV3::IntSettingV3() : m_impl(std::make_shared<Impl>()) {}
int64_t& IntSettingV3::getValueMut() const { int64_t& IntSettingV3::getValueMut() const {
return m_impl->value; 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) { Result<> IntSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "IntSettingV3"); auto root = checkJson(json, "IntSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
m_impl->value = m_impl->defaultValue; m_impl->value = m_impl->defaultValue;
root.has("min").into(m_impl->minValue); root.has("min").into(m_impl->minValue);
@ -362,6 +374,8 @@ public:
} controls; } controls;
}; };
FloatSettingV3::FloatSettingV3() : m_impl(std::make_shared<Impl>()) {}
double& FloatSettingV3::getValueMut() const { double& FloatSettingV3::getValueMut() const {
return m_impl->value; 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) { Result<> FloatSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "FloatSettingV3"); auto root = checkJson(json, "FloatSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
m_impl->value = m_impl->defaultValue; m_impl->value = m_impl->defaultValue;
root.has("min").into(m_impl->minValue); root.has("min").into(m_impl->minValue);
@ -483,6 +496,8 @@ public:
std::optional<std::vector<std::string>> oneOf; std::optional<std::vector<std::string>> oneOf;
}; };
StringSettingV3::StringSettingV3() : m_impl(std::make_shared<Impl>()) {}
std::string& StringSettingV3::getValueMut() const { std::string& StringSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -517,8 +532,7 @@ std::optional<std::vector<std::string>> StringSettingV3::getEnumOptions() const
Result<> StringSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { Result<> StringSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "StringSettingV3"); auto root = checkJson(json, "StringSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
m_impl->value = m_impl->defaultValue; m_impl->value = m_impl->defaultValue;
root.has("match").into(m_impl->match); root.has("match").into(m_impl->match);
@ -566,6 +580,8 @@ public:
std::optional<std::vector<utils::file::FilePickOptions::Filter>> filters; std::optional<std::vector<utils::file::FilePickOptions::Filter>> filters;
}; };
FileSettingV3::FileSettingV3() : m_impl(std::make_shared<Impl>()) {}
std::filesystem::path& FileSettingV3::getValueMut() const { std::filesystem::path& FileSettingV3::getValueMut() const {
return m_impl->value; return m_impl->value;
} }
@ -584,9 +600,7 @@ std::optional<std::vector<utils::file::FilePickOptions::Filter>> FileSettingV3::
Result<> FileSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) { Result<> FileSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "FileSettingV3"); auto root = checkJson(json, "FileSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
// Replace known paths like `{gd-save-dir}/` // Replace known paths like `{gd-save-dir}/`
try { try {
@ -655,6 +669,8 @@ public:
ccColor3B defaultValue; ccColor3B defaultValue;
}; };
Color3BSettingV3::Color3BSettingV3() : m_impl(std::make_shared<Impl>()) {}
ccColor3B& Color3BSettingV3::getValueMut() const { ccColor3B& Color3BSettingV3::getValueMut() const {
return m_impl->value; 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) { Result<> Color3BSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "Color3BSettingV3"); auto root = checkJson(json, "Color3BSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
m_impl->value = m_impl->defaultValue; m_impl->value = m_impl->defaultValue;
root.checkUnknownKeys(); root.checkUnknownKeys();
@ -710,6 +725,8 @@ public:
ccColor4B defaultValue; ccColor4B defaultValue;
}; };
Color4BSettingV3::Color4BSettingV3() : m_impl(std::make_shared<Impl>()) {}
ccColor4B& Color4BSettingV3::getValueMut() const { ccColor4B& Color4BSettingV3::getValueMut() const {
return m_impl->value; 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) { Result<> Color4BSettingV3::onParse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto root = checkJson(json, "Color4BSettingV3"); auto root = checkJson(json, "Color4BSettingV3");
GEODE_UNWRAP(this->parseShared(root)); GEODE_UNWRAP(this->parseShared(root, m_impl->defaultValue));
root.needs("default").into(m_impl->defaultValue);
m_impl->value = m_impl->defaultValue; m_impl->value = m_impl->defaultValue;
root.checkUnknownKeys(); root.checkUnknownKeys();

View file

@ -320,6 +320,7 @@ JsonExpectedValue JsonExpectedValue::has(std::string_view key) {
if (!this->assertIs(matjson::Type::Object)) { if (!this->assertIs(matjson::Type::Object)) {
return JsonExpectedValue(); return JsonExpectedValue();
} }
m_impl->knownKeys.insert(std::string(key));
if (!m_impl->scope.contains(key)) { if (!m_impl->scope.contains(key)) {
return JsonExpectedValue(); return JsonExpectedValue();
} }
@ -332,6 +333,7 @@ JsonExpectedValue JsonExpectedValue::needs(std::string_view key) {
if (!this->assertIs(matjson::Type::Object)) { if (!this->assertIs(matjson::Type::Object)) {
return JsonExpectedValue(); return JsonExpectedValue();
} }
m_impl->knownKeys.insert(std::string(key));
if (!m_impl->scope.contains(key)) { if (!m_impl->scope.contains(key)) {
this->setError("missing required key {}", key); this->setError("missing required key {}", key);
return JsonExpectedValue(); return JsonExpectedValue();
@ -352,8 +354,9 @@ std::vector<std::pair<std::string, JsonExpectedValue>> JsonExpectedValue::proper
return res; return res;
} }
void JsonExpectedValue::checkUnknownKeys() { void JsonExpectedValue::checkUnknownKeys() {
for (auto& [key, _] : this->properties()) { if (this->hasError()) return;
if (!m_impl->knownKeys.count(key)) { for (auto&& [key, _] : this->properties()) {
if (!m_impl->knownKeys.contains(key)) {
log::warn("{} contains unknown key \"{}\"", m_impl->scopeName, key); log::warn("{} contains unknown key \"{}\"", m_impl->scopeName, key);
} }
} }
@ -401,7 +404,8 @@ std::vector<JsonExpectedValue> JsonExpectedValue::items() {
} }
JsonExpectedValue::operator bool() const { 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() { Result<> JsonExpectedValue::ok() {