mod settings order is now saved

This commit is contained in:
HJfod 2022-09-24 18:46:47 +03:00
parent 8e629cb2cf
commit d10e52202c
9 changed files with 154 additions and 125 deletions

View file

@ -147,7 +147,7 @@ namespace geode {
/**
* Create ModInfo from a parsed json document
*/
static Result<ModInfo> create(nlohmann::json const& json);
static Result<ModInfo> create(ModJson const& json);
private:
/**
@ -155,7 +155,7 @@ namespace geode {
* compatibility if we update the mod.json
* format
*/
static Result<ModInfo> createFromSchemaV010(nlohmann::json const& json);
static Result<ModInfo> createFromSchemaV010(ModJson const& json);
};
/**

View file

@ -11,6 +11,8 @@
#include <regex>
namespace geode {
using ModJson = nlohmann::ordered_json;
class SettingNode;
class BoolSetting;
class IntSetting;
@ -35,7 +37,7 @@ namespace geode {
static Result<std::shared_ptr<Setting>> parse(
std::string const& type,
std::string const& key,
JsonMaybeObject& obj
JsonMaybeObject<ModJson>& obj
);
public:
@ -44,7 +46,7 @@ namespace geode {
// Load from mod.json
static Result<std::shared_ptr<Setting>> parse(
std::string const& key,
nlohmann::json const& json
ModJson const& json
);
// Load value from saved settings
virtual bool load(nlohmann::json const& json) = 0;
@ -98,7 +100,7 @@ namespace geode {
static Result<std::shared_ptr<Class>> parse(
std::string const& key,
JsonMaybeObject& obj
JsonMaybeObject<ModJson>& obj
) {
auto res = std::make_shared<Class>();
@ -211,7 +213,7 @@ namespace geode {
return Ok();
}
Result<> parseMinMax(JsonMaybeObject& obj) {
Result<> parseMinMax(JsonMaybeObject<ModJson>& obj) {
obj.has("min").intoAs<ValueType>(m_min);
obj.has("max").intoAs<ValueType>(m_max);
return Ok();
@ -242,7 +244,7 @@ namespace geode {
return Ok();
}
Result<> parseOneOf(JsonMaybeObject& obj) {
Result<> parseOneOf(JsonMaybeObject<ModJson>& obj) {
std::unordered_set<ValueType> oneOf {};
for (auto& item : obj.has("one-of").iterate()) {
oneOf.insert(item.get<ValueType>());
@ -277,7 +279,7 @@ namespace geode {
return Ok();
}
Result<> parseMatch(JsonMaybeObject& obj) {
Result<> parseMatch(JsonMaybeObject<ModJson>& obj) {
obj.has("match").intoAs<std::string>(m_matchRegex);
return Ok();
}
@ -292,7 +294,7 @@ namespace geode {
protected:\
bool m_##name = default;\
public:\
Result<> parse##Name(JsonMaybeObject& obj) {\
Result<> parse##Name(JsonMaybeObject<ModJson>& obj) {\
obj.has(json).into(m_##name);\
return Ok();\
}\
@ -309,7 +311,7 @@ namespace geode {
size_t m_bigArrowStep = 1;
public:
Result<> parseArrows(JsonMaybeObject& obj) {
Result<> parseArrows(JsonMaybeObject<ModJson>& obj) {
obj.has("arrows").into(m_hasArrows);
obj.has("arrow-step").into(m_arrowStep);
obj.has("big-arrows").into(m_hasBigArrows);
@ -338,7 +340,7 @@ namespace geode {
std::optional<ValueType> m_sliderStep = std::nullopt;
public:
Result<> parseSlider(JsonMaybeObject& obj) {
Result<> parseSlider(JsonMaybeObject<ModJson>& obj) {
obj.has("slider").into(m_hasSlider);
obj.has("slider-step").intoAs<ValueType>(m_sliderStep);
return Ok();

View file

@ -6,6 +6,7 @@
#include <set>
namespace geode {
template<class Json>
struct JsonChecker;
namespace {
@ -71,25 +72,32 @@ namespace geode {
template<class T>
using JsonValueValidator = bool(*)(T const&);
template<class Json>
struct JsonMaybeObject;
template<class Json>
struct JsonMaybeValue;
struct GEODE_DLL JsonMaybeSomething {
template<class Json>
struct JsonMaybeSomething {
protected:
JsonChecker& m_checker;
nlohmann::json& m_json;
JsonChecker<Json>& m_checker;
Json& m_json;
std::string m_hierarchy;
bool m_hasValue;
friend struct JsonMaybeObject<Json>;
friend struct JsonMaybeValue<Json>;
void setError(std::string const& error);
public:
nlohmann::json& json() {
Json& json() {
return m_json;
}
JsonMaybeSomething(
JsonChecker& checker,
nlohmann::json& json,
JsonChecker<Json>& checker,
Json& json,
std::string const& hierarchy,
bool hasValue
) : m_checker(checker),
@ -104,23 +112,28 @@ namespace geode {
}
};
struct GEODE_DLL JsonMaybeValue : JsonMaybeSomething {
template<class Json>
struct JsonMaybeValue : public JsonMaybeSomething<Json> {
bool m_inferType = true;
JsonMaybeValue(
JsonChecker& checker,
nlohmann::json& json,
JsonChecker<Json>& checker,
Json& json,
std::string const& hierarchy,
bool hasValue
) : JsonMaybeSomething(checker, json, hierarchy, hasValue) {}
) : JsonMaybeSomething<Json>(checker, json, hierarchy, hasValue) {}
JsonMaybeSomething<Json>& self() {
return *static_cast<JsonMaybeSomething<Json>*>(this);
}
template<nlohmann::detail::value_t T>
JsonMaybeValue as() {
JsonMaybeValue<Json> as() {
if (this->isError()) return *this;
if (!jsonConvertibleTo(m_json.type(), T)) {
if (!jsonConvertibleTo(self().m_json.type(), T)) {
this->setError(
m_hierarchy + ": Invalid type \"" +
m_json.type_name() + "\", expected \"" +
self().m_hierarchy + ": Invalid type \"" +
self().m_json.type_name() + "\", expected \"" +
jsonValueTypeToString(T) + "\""
);
}
@ -129,13 +142,13 @@ namespace geode {
}
template<nlohmann::detail::value_t... T>
JsonMaybeValue asOneOf() {
JsonMaybeValue<Json> asOneOf() {
if (this->isError()) return *this;
bool isOneOf = (... || jsonConvertibleTo(m_json.type(), T));
bool isOneOf = (... || jsonConvertibleTo(self().m_json.type(), T));
if (!isOneOf) {
this->setError(
m_hierarchy + ": Invalid type \"" +
m_json.type_name() + "\", expected one of \"" +
self().m_hierarchy + ": Invalid type \"" +
self().m_json.type_name() + "\", expected one of \"" +
(jsonValueTypeToString(T), ...) + "\""
);
}
@ -144,56 +157,57 @@ namespace geode {
}
template<nlohmann::detail::value_t T>
JsonMaybeValue is() {
JsonMaybeValue<Json> is() {
if (this->isError()) return *this;
m_hasValue = jsonConvertibleTo(m_json.type(), T);
self().m_hasValue = jsonConvertibleTo(self().m_json.type(), T);
m_inferType = false;
return *this;
}
template<class T>
JsonMaybeValue validate(JsonValueValidator<T> validator) {
JsonMaybeValue<Json> validate(JsonValueValidator<T> validator) {
if (this->isError()) return *this;
try {
if (!validator(m_json.get<T>())) {
this->setError(m_hierarchy + ": Invalid value format");
if (!validator(self().m_json.get<T>())) {
this->setError(self().m_hierarchy + ": Invalid value format");
}
} catch(...) {
this->setError(
m_hierarchy + ": Invalid type \"" +
std::string(m_json.type_name()) + "\""
self().m_hierarchy + ": Invalid type \"" +
std::string(self().m_json.type_name()) + "\""
);
}
return *this;
}
template<class T>
JsonMaybeValue inferType() {
JsonMaybeValue<Json> inferType() {
if (this->isError() || !m_inferType) return *this;
return this->as<getJsonType<T>()>();
}
JsonMaybeValue intoRaw(nlohmann::json& target) {
template<class T>
JsonMaybeValue<Json> intoRaw(T& target) {
if (this->isError()) return *this;
target = m_json;
target = self().m_json;
return *this;
}
template<class T>
JsonMaybeValue into(T& target) {
JsonMaybeValue<Json> into(T& target) {
return this->intoAs<T, T>(target);
}
template<class A, class T>
JsonMaybeValue intoAs(T& target) {
JsonMaybeValue<Json> intoAs(T& target) {
this->inferType<A>();
if (this->isError()) return *this;
try {
target = m_json.get<A>();
target = self().m_json.get<A>();
} catch(...) {
this->setError(
m_hierarchy + ": Invalid type \"" +
std::string(m_json.type_name()) + "\""
self().m_hierarchy + ": Invalid type \"" +
std::string(self().m_json.type_name()) + "\""
);
}
return *this;
@ -204,17 +218,17 @@ namespace geode {
this->inferType<T>();
if (this->isError()) return T();
try {
return m_json.get<T>();
return self().m_json.get<T>();
} catch(...) {
this->setError(
m_hierarchy + ": Invalid type to get \"" +
std::string(m_json.type_name()) + "\""
self().m_hierarchy + ": Invalid type to get \"" +
std::string(self().m_json.type_name()) + "\""
);
}
return T();
}
JsonMaybeObject obj();
JsonMaybeObject<Json> obj();
template<class T>
struct Iterator {
@ -238,31 +252,31 @@ namespace geode {
}
};
Iterator<JsonMaybeValue> iterate() {
Iterator<JsonMaybeValue<Json>> iterate() {
this->as<value_t::array>();
Iterator<JsonMaybeValue> iter;
Iterator<JsonMaybeValue<Json>> iter;
if (this->isError()) return iter;
size_t i = 0;
for (auto& obj : m_json) {
for (auto& obj : self().m_json) {
iter.m_values.emplace_back(
m_checker, obj,
m_hierarchy + "." + std::to_string(i++),
m_hasValue
self().m_checker, obj,
self().m_hierarchy + "." + std::to_string(i++),
self().m_hasValue
);
}
return iter;
}
Iterator<std::pair<std::string, JsonMaybeValue>> items() {
Iterator<std::pair<std::string, JsonMaybeValue<Json>>> items() {
this->as<value_t::object>();
Iterator<std::pair<std::string, JsonMaybeValue>> iter;
Iterator<std::pair<std::string, JsonMaybeValue<Json>>> iter;
if (this->isError()) return iter;
for (auto& [k, v] : m_json.items()) {
iter.m_values.emplace_back(k, JsonMaybeValue(
m_checker, v,
m_hierarchy + "." + k,
m_hasValue
for (auto& [k, v] : self().m_json.items()) {
iter.m_values.emplace_back(k, JsonMaybeValue<Json>(
self().m_checker, v,
self().m_hierarchy + "." + k,
self().m_hasValue
));
}
@ -270,65 +284,69 @@ namespace geode {
}
};
struct GEODE_DLL JsonMaybeObject : JsonMaybeSomething {
template<class Json>
struct JsonMaybeObject : JsonMaybeSomething<Json> {
std::set<std::string> m_knownKeys;
JsonMaybeObject(
JsonChecker& checker,
nlohmann::json& json,
JsonChecker<Json>& checker,
Json& json,
std::string const& hierarchy,
bool hasValue
) : JsonMaybeSomething(checker, json, hierarchy, hasValue) {}
) : JsonMaybeSomething<Json>(checker, json, hierarchy, hasValue) {}
JsonMaybeSomething<Json>& self() {
return *static_cast<JsonMaybeSomething<Json>*>(this);
}
void addKnownKey(std::string const& key) {
m_knownKeys.insert(key);
}
nlohmann::json& json() {
return m_json;
Json& json() {
return self().m_json;
}
JsonMaybeValue emptyValue() {
return JsonMaybeValue(m_checker, m_json, "", false);
JsonMaybeValue<Json> emptyValue() {
return JsonMaybeValue(self().m_checker, self().m_json, "", false);
}
JsonMaybeValue has(std::string const& key) {
JsonMaybeValue<Json> has(std::string const& key) {
this->addKnownKey(key);
if (this->isError()) return emptyValue();
if (!m_json.contains(key) || m_json[key].is_null()) {
if (!self().m_json.contains(key) || self().m_json[key].is_null()) {
return emptyValue();
}
return JsonMaybeValue(m_checker, m_json[key], key, true);
return JsonMaybeValue<Json>(self().m_checker, self().m_json[key], key, true);
}
JsonMaybeValue needs(std::string const& key) {
JsonMaybeValue<Json> needs(std::string const& key) {
this->addKnownKey(key);
if (this->isError()) return emptyValue();
if (!m_json.contains(key)) {
if (!self().m_json.contains(key)) {
this->setError(
m_hierarchy + " is missing required key \"" + key + "\""
self().m_hierarchy + " is missing required key \"" + key + "\""
);
return emptyValue();
}
return JsonMaybeValue(m_checker, m_json[key], key, true);
return JsonMaybeValue<Json>(self().m_checker, self().m_json[key], key, true);
}
void checkUnknownKeys() {
for (auto& [key, _] : m_json.items()) {
for (auto& [key, _] : self().m_json.items()) {
if (!m_knownKeys.count(key)) {
Log::get() << m_hierarchy + " contains unknown key \"" + key + "\"";
Log::get() << self().m_hierarchy + " contains unknown key \"" + key + "\"";
}
}
}
};
struct GEODE_DLL JsonChecker {
template<class Json>
struct JsonChecker {
std::variant<std::monostate, std::string> m_result;
nlohmann::json& m_json;
Json& m_json;
JsonChecker(
nlohmann::json& json
) : m_json(json), m_result(std::monostate()) {}
JsonChecker(Json& json) : m_json(json), m_result(std::monostate()) {}
bool isError() const {
return std::holds_alternative<std::string>(m_result);
@ -338,8 +356,27 @@ namespace geode {
return std::get<std::string>(m_result);
}
JsonMaybeValue root(std::string const& hierarchy) {
JsonMaybeValue<Json> root(std::string const& hierarchy) {
return JsonMaybeValue(*this, m_json, hierarchy, true);
}
};
template<class Json>
void JsonMaybeSomething<Json>::setError(std::string const& error) {
m_checker.m_result = error;
}
template<class Json>
bool JsonMaybeSomething<Json>::isError() const {
return m_checker.isError() || !m_hasValue;
}
template<class Json>
JsonMaybeObject<Json> JsonMaybeValue<Json>::obj() {
this->as<value_t::object>();
return JsonMaybeObject(
self().m_checker, self().m_json,
self().m_hierarchy, self().m_hasValue
);
}
}

View file

@ -6,13 +6,14 @@
#define GITHUB_DONT_RATE_LIMIT_ME_PLS 0
static Result<nlohmann::json> readJSON(ghc::filesystem::path const& path) {
template<class Json = nlohmann::json>
static Result<Json> readJSON(ghc::filesystem::path const& path) {
auto indexJsonData = file_utils::readString(path);
if (!indexJsonData) {
return Err("Unable to read " + path.string());
}
try {
return Ok(nlohmann::json::parse(indexJsonData.value()));
return Ok(Json::parse(indexJsonData.value()));
} catch(std::exception& e) {
return Err("Error parsing JSON: " + std::string(e.what()));
}
@ -311,7 +312,7 @@ void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
return;
}
auto readModJson = readJSON(dir / "mod.json");
auto readModJson = readJSON<ModJson>(dir / "mod.json");
if (!readModJson) {
Log::get() << Severity::Warning
<< "Error reading mod.json: "

View file

@ -86,13 +86,4 @@ Result<std::string> fetch(std::string const& url) {
return Err("Error getting info: " + std::string(curl_easy_strerror(res)));
}
Result<nlohmann::json> fetchJSON(std::string const& url) {
auto res = fetch(url);
if (!res) return Err(res.error());
try {
return Ok(nlohmann::json::parse(res.value()));
} catch(std::exception& e) {
return Err(e.what());
}
}

View file

@ -5,11 +5,21 @@
USE_GEODE_NAMESPACE();
Result<std::string> fetch(std::string const& url);
Result<nlohmann::json> fetchJSON(std::string const& url);
Result<> fetchFile(
std::string const& url,
ghc::filesystem::path const& into,
std::function<int(double, double)> prog = nullptr
);
template<class Json = nlohmann::json>
Result<Json> fetchJSON(std::string const& url) {
auto res = fetch(url);
if (!res) return Err(res.error());
try {
return Ok(Json::parse(res.value()));
} catch(std::exception& e) {
return Err(e.what());
}
}

View file

@ -1,16 +0,0 @@
#include <Geode/utils/JsonValidation.hpp>
USE_GEODE_NAMESPACE();
void JsonMaybeSomething::setError(std::string const& error) {
m_checker.m_result = error;
}
bool JsonMaybeSomething::isError() const {
return m_checker.isError() || !m_hasValue;
}
JsonMaybeObject JsonMaybeValue::obj() {
this->as<value_t::object>();
return JsonMaybeObject(m_checker, m_json, m_hierarchy, m_hasValue);
}

View file

@ -11,7 +11,7 @@ static std::string sanitizeDetailsData(unsigned char* start, unsigned char* end)
return string_utils::replace(std::string(start, end), "\r", "");
}
Result<ModInfo> ModInfo::createFromSchemaV010(nlohmann::json const& rawJson) {
Result<ModInfo> ModInfo::createFromSchemaV010(ModJson const& rawJson) {
ModInfo info;
auto json = rawJson;
@ -66,7 +66,7 @@ Result<ModInfo> ModInfo::createFromSchemaV010(nlohmann::json const& rawJson) {
for (auto& [key, value] : root.has("settings").items()) {
auto sett = Setting::parse(key, value.json());
PROPAGATE(sett);
info.m_settings.insert({ key, sett.value() });
info.m_settings.push_back({ key, sett.value() });
}
if (auto resources = root.has("resources").obj()) {
@ -112,7 +112,7 @@ Result<ModInfo> ModInfo::createFromSchemaV010(nlohmann::json const& rawJson) {
return Ok(info);
}
Result<ModInfo> ModInfo::create(nlohmann::json const& json) {
Result<ModInfo> ModInfo::create(ModJson const& json) {
// Check mod.json target version
auto schema = LOADER_VERSION;
if (json.contains("geode") && json["geode"].is_string()) {
@ -170,11 +170,15 @@ Result<ModInfo> ModInfo::createFromFile(ghc::filesystem::path const& path) {
try {
auto read = file_utils::readString(path);
if (!read) return Err(read.error());
auto res = ModInfo::create(nlohmann::json::parse(read.value()));
if (!res) return res;
auto info = res.value();
info.m_path = path;
return Ok(info);
try {
auto res = ModInfo::create(ModJson::parse(read.value()));
if (!res) return res;
auto info = res.value();
info.m_path = path;
return Ok(info);
} catch(std::exception& e) {
return Err("Unable to parse mod.json: " + std::string(e.what()));
}
} catch(std::exception const& e) {
return Err(e.what());
}
@ -195,9 +199,9 @@ Result<ModInfo> ModInfo::createFromGeodeFile(ghc::filesystem::path const& path)
if (!read || !readSize) {
return Err("\"" + path.string() + "\": Unable to read mod.json");
}
nlohmann::json json;
ModJson json;
try {
json = nlohmann::json::parse(std::string(read, read + readSize));
json = ModJson::parse(std::string(read, read + readSize));
} catch(std::exception const& e) {
delete[] read;
return Err<>(e.what());

View file

@ -15,7 +15,7 @@ std::string Setting::getKey() const {
Result<std::shared_ptr<Setting>> Setting::parse(
std::string const& type,
std::string const& key,
JsonMaybeObject& obj
JsonMaybeObject<ModJson>& obj
) {
switch (hash(type.c_str())) {
case hash("bool"): return BoolSetting::parse(key, obj);
@ -35,7 +35,7 @@ Result<std::shared_ptr<Setting>> Setting::parse(
Result<std::shared_ptr<Setting>> Setting::parse(
std::string const& key,
nlohmann::json const& rawJson
ModJson const& rawJson
) {
if (rawJson.is_object()) {
auto json = rawJson;