diff --git a/loader/include/Geode/loader/Loader.hpp b/loader/include/Geode/loader/Loader.hpp index f36ef6f5..2ec88781 100644 --- a/loader/include/Geode/loader/Loader.hpp +++ b/loader/include/Geode/loader/Loader.hpp @@ -35,7 +35,9 @@ namespace geode { EnableFailed, MissingDependency, PresentIncompatibility, - UnzipFailed + UnzipFailed, + UnsupportedVersion, + UnsupportedGeodeVersion, }; Type type; std::variant cause; diff --git a/loader/include/Geode/loader/ModMetadata.hpp b/loader/include/Geode/loader/ModMetadata.hpp index de9f3e19..493af50f 100644 --- a/loader/include/Geode/loader/ModMetadata.hpp +++ b/loader/include/Geode/loader/ModMetadata.hpp @@ -161,6 +161,11 @@ namespace geode { */ [[nodiscard]] std::optional getGameVersion() const; + /** + * Gets the target Geode version for the current platform. + */ + [[nodiscard]] VersionInfo getGeodeVersion() const; + /** * Checks if mod can be installed on the current GD version. * Returns Ok() if it can, Err otherwise. diff --git a/loader/include/Geode/utils/VersionInfo.hpp b/loader/include/Geode/utils/VersionInfo.hpp index 392a3c4b..3aca9db6 100644 --- a/loader/include/Geode/utils/VersionInfo.hpp +++ b/loader/include/Geode/utils/VersionInfo.hpp @@ -228,6 +228,8 @@ namespace geode { std::string toString() const; friend GEODE_DLL std::string format_as(ComparableVersionInfo const& version); }; + + bool GEODE_DLL semverCompare(VersionInfo const& current, VersionInfo const& target); } template diff --git a/loader/src/loader/LoaderImpl.cpp b/loader/src/loader/LoaderImpl.cpp index 16efefb5..868c6458 100644 --- a/loader/src/loader/LoaderImpl.cpp +++ b/loader/src/loader/LoaderImpl.cpp @@ -148,10 +148,8 @@ VersionInfo Loader::Impl::maxModVersion() { }; } -bool Loader::Impl::isModVersionSupported(VersionInfo const& version) { - return - version >= this->minModVersion() && - version <= this->maxModVersion(); +bool Loader::Impl::isModVersionSupported(VersionInfo const& target) { + return semverCompare(this->getVersion(), target); } // Data saving @@ -421,6 +419,37 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) { m_refreshingModCount -= 1; }; + { // version checking + auto res = node->getMetadata().checkGameVersion(); + if (!res) { + m_problems.push_back({ + LoadProblem::Type::UnsupportedVersion, + node, + res.unwrapErr() + }); + log::error("Unsupported game version: {}", res.unwrapErr()); + m_refreshingModCount -= 1; + log::popNest(); + return; + } + + if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) { + m_problems.push_back({ + LoadProblem::Type::UnsupportedGeodeVersion, + node, + fmt::format( + "Geode version {} is not supported (current: {})", + node->getMetadata().getGeodeVersion().toString(), + this->getVersion().toString() + ) + }); + log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion()); + m_refreshingModCount -= 1; + log::popNest(); + return; + } + } + if (early) { auto res = unzipFunction(); if (!res) { diff --git a/loader/src/loader/ModImpl.cpp b/loader/src/loader/ModImpl.cpp index 43e89ef4..9b7255a8 100644 --- a/loader/src/loader/ModImpl.cpp +++ b/loader/src/loader/ModImpl.cpp @@ -357,8 +357,6 @@ bool Mod::Impl::getLaunchFlag(std::string_view const name) const { // Loading, Toggling, Installing Result<> Mod::Impl::loadBinary() { - // i dont know where to put this so ill just plop it here - GEODE_UNWRAP(m_metadata.checkGameVersion()); log::debug("Loading binary for mod {}", m_metadata.getID()); if (m_enabled) diff --git a/loader/src/loader/ModMetadataImpl.cpp b/loader/src/loader/ModMetadataImpl.cpp index 77bca83e..1ab858b6 100644 --- a/loader/src/loader/ModMetadataImpl.cpp +++ b/loader/src/loader/ModMetadataImpl.cpp @@ -67,7 +67,7 @@ Result ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs JsonChecker checker(impl->m_rawJSON); auto root = checker.root(checkerRoot).obj(); - root.addKnownKey("geode"); + root.needs("geode").into(impl->m_geodeVersion); root.addKnownKey("gd"); // Check GD version @@ -194,23 +194,23 @@ Result ModMetadata::Impl::create(ModJson const& json) { "specified, or its formatting is invalid (required: \"[v]X.X.X\")!" ); } - if (schema < Loader::get()->minModVersion()) { - return Err( - "[mod.json] is built for an older version (" + schema.toString() + - ") of Geode (current: " + Loader::get()->getVersion().toString() + - "). Please update the mod to the latest version, " - "and if the problem persists, contact the developer " - "to update it." - ); - } - if (schema > Loader::get()->maxModVersion()) { - return Err( - "[mod.json] is built for a newer version (" + schema.toString() + - ") of Geode (current: " + Loader::get()->getVersion().toString() + - "). You need to update Geode in order to use " - "this mod." - ); - } + // if (schema < Loader::get()->minModVersion()) { + // return Err( + // "[mod.json] is built for an older version (" + schema.toString() + + // ") of Geode (current: " + Loader::get()->getVersion().toString() + + // "). Please update the mod to the latest version, " + // "and if the problem persists, contact the developer " + // "to update it." + // ); + // } + // if (schema > Loader::get()->maxModVersion()) { + // return Err( + // "[mod.json] is built for a newer version (" + schema.toString() + + // ") of Geode (current: " + Loader::get()->getVersion().toString() + + // "). You need to update Geode in order to use " + // "this mod." + // ); + // } // Handle mod.json data based on target if (schema < VersionInfo(0, 1, 0)) { @@ -407,6 +407,10 @@ std::optional ModMetadata::getGameVersion() const { return m_impl->m_gdVersion; } +VersionInfo ModMetadata::getGeodeVersion() const { + return m_impl->m_geodeVersion; +} + Result<> ModMetadata::checkGameVersion() const { if (!m_impl->m_gdVersion.empty()) { auto const ver = m_impl->m_gdVersion; diff --git a/loader/src/loader/ModMetadataImpl.hpp b/loader/src/loader/ModMetadataImpl.hpp index e716587e..77c65f4f 100644 --- a/loader/src/loader/ModMetadataImpl.hpp +++ b/loader/src/loader/ModMetadataImpl.hpp @@ -17,6 +17,7 @@ namespace geode { std::string m_name; std::string m_developer; std::string m_gdVersion; + VersionInfo m_geodeVersion; std::optional m_description; std::optional m_details; std::optional m_changelog; diff --git a/loader/src/ui/internal/list/ProblemsListCell.cpp b/loader/src/ui/internal/list/ProblemsListCell.cpp index 859df03a..f409b8c8 100644 --- a/loader/src/ui/internal/list/ProblemsListCell.cpp +++ b/loader/src/ui/internal/list/ProblemsListCell.cpp @@ -95,6 +95,16 @@ bool ProblemsListCell::init(LoadProblem problem, ProblemsListPopup* list, CCSize message = fmt::format("{} has failed unzipping", cause); m_longMessage = problem.message; break; + case LoadProblem::Type::UnsupportedVersion: + icon = "info-alert.png"_spr; + message = fmt::format("{} is incompatible with this version of GD", cause); + m_longMessage = problem.message; + break; + case LoadProblem::Type::UnsupportedGeodeVersion: + icon = "info-alert.png"_spr; + message = fmt::format("{} is incompatible with this version of Geode", cause); + m_longMessage = problem.message; + break; } m_problem = std::move(problem); diff --git a/loader/src/utils/VersionInfo.cpp b/loader/src/utils/VersionInfo.cpp index 885a1e91..40a28e89 100644 --- a/loader/src/utils/VersionInfo.cpp +++ b/loader/src/utils/VersionInfo.cpp @@ -182,3 +182,62 @@ std::string ComparableVersionInfo::toString() const { std::string geode::format_as(ComparableVersionInfo const& version) { return version.toString(); } + +bool geode::semverCompare(VersionInfo const& current, VersionInfo const& target) { + if (target.getMajor() != current.getMajor()) { + return false; + } + if (target.getMinor() > current.getMinor()) { + return false; + } + auto ct = current.getTag(); + auto tt = target.getTag(); + if (ct && tt) { + auto currentTag = ct.value(); + auto targetTag = tt.value(); + switch (targetTag.value) { + case VersionTag::Alpha: + if (currentTag.value > VersionTag::Alpha) { + return false; + } + if (currentTag.number && targetTag.number) { + return currentTag.number.value() == targetTag.number.value(); + } + if (currentTag.number) { + return true; + } + if (targetTag.number) { + return false; + } + return true; + case VersionTag::Beta: + if (currentTag.number && targetTag.number) { + return currentTag.number.value() >= targetTag.number.value(); + } + if (currentTag.number) { + return true; + } + if (targetTag.number) { + return false; + } + return true; + default: + return true; + } + } + else if (ct) { + auto currentTag = ct.value(); + if (currentTag.value > VersionTag::Alpha) { + return true; + } + return false; + } + else if (tt) { + auto targetTag = tt.value(); + if (targetTag.value > VersionTag::Alpha) { + return true; + } + return false; + } + return true; +} diff --git a/loader/test/dependency/mod.json b/loader/test/dependency/mod.json index 00ee7276..bfb95961 100644 --- a/loader/test/dependency/mod.json +++ b/loader/test/dependency/mod.json @@ -1,6 +1,6 @@ { "geode": "2.0.0", - "gd": "*", + "gd": "*", "version": "1.0.0", "id": "geode.testdep", "name": "Geode Test Dependency",