mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-14 11:05:08 -05:00
show outdated mods in the UI + make outdated Geode ver count for that too
This commit is contained in:
parent
f5f336532f
commit
09fa872781
12 changed files with 134 additions and 66 deletions
|
@ -46,6 +46,21 @@ namespace geode {
|
|||
Type type;
|
||||
std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
|
||||
std::string message;
|
||||
|
||||
bool isSuggestion() const {
|
||||
return
|
||||
type == LoadProblem::Type::Recommendation ||
|
||||
type == LoadProblem::Type::Suggestion;
|
||||
}
|
||||
bool isOutdated() const {
|
||||
return
|
||||
type == LoadProblem::Type::UnsupportedVersion ||
|
||||
type == LoadProblem::Type::NeedsNewerGeodeVersion ||
|
||||
type == LoadProblem::Type::UnsupportedGeodeVersion;
|
||||
}
|
||||
bool isProblem() const {
|
||||
return !isSuggestion() && !isOutdated();
|
||||
}
|
||||
};
|
||||
|
||||
class LoaderImpl;
|
||||
|
|
|
@ -446,7 +446,16 @@ namespace geode {
|
|||
bool isLoggingEnabled() const;
|
||||
void setLoggingEnabled(bool enabled);
|
||||
|
||||
bool targetsOutdatedGDVersion() const;
|
||||
/**
|
||||
* If this mod is built for an outdated GD or Geode version, returns the
|
||||
* `LoadProblem` describing the situation. Otherwise `nullopt` if the
|
||||
* mod is made for the correct version of the game and Geode
|
||||
*/
|
||||
std::optional<LoadProblem> targetsOutdatedVersion() const;
|
||||
/**
|
||||
* @note Make sure to also call `targetsOutdatedVersion` if you want to
|
||||
* make sure the mod is actually loadable
|
||||
*/
|
||||
bool hasLoadProblems() const;
|
||||
std::vector<LoadProblem> getAllProblems() const;
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
|
|
|
@ -46,7 +46,7 @@ void crashlog::printMods(std::stringstream& stream) {
|
|||
mod->isCurrentlyLoading() ? "o"sv :
|
||||
mod->isEnabled() ? "x"sv :
|
||||
mod->hasLoadProblems() ? "!"sv : // thank you for this bug report
|
||||
mod->targetsOutdatedGDVersion() ? "*"sv : // thank you very much for this bug report
|
||||
mod->targetsOutdatedVersion() ? "*"sv : // thank you very much for this bug report
|
||||
mod->shouldLoad() ? "~"sv :
|
||||
" "sv,
|
||||
mod->getVersion().toVString(), mod->getID()
|
||||
|
|
|
@ -71,11 +71,7 @@ std::vector<LoadProblem> Loader::getAllProblems() const {
|
|||
std::vector<LoadProblem> Loader::getLoadProblems() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type != LoadProblem::Type::Recommendation &&
|
||||
problem.type != LoadProblem::Type::Suggestion &&
|
||||
problem.type != LoadProblem::Type::UnsupportedVersion
|
||||
) {
|
||||
if (problem.isProblem()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +80,7 @@ std::vector<LoadProblem> Loader::getLoadProblems() const {
|
|||
std::vector<LoadProblem> Loader::getOutdated() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (problem.type == LoadProblem::Type::UnsupportedVersion) {
|
||||
if (problem.isOutdated()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -93,10 +89,7 @@ std::vector<LoadProblem> Loader::getOutdated() const {
|
|||
std::vector<LoadProblem> Loader::getRecommendations() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type == LoadProblem::Type::Recommendation ||
|
||||
problem.type == LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isSuggestion()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -384,6 +384,37 @@ void Loader::Impl::buildModGraph() {
|
|||
}
|
||||
|
||||
void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||
// Check version first, as it's not worth trying to load a mod with an
|
||||
// invalid target version
|
||||
// Also this makes it so that when GD updates, outdated mods get shown as
|
||||
// "Outdated" in the UI instead of "Missing Dependencies"
|
||||
auto res = node->getMetadata().checkGameVersion();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnsupportedVersion,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("{}", res.unwrapErr());
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
|
||||
this->addProblem({
|
||||
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
|
||||
node,
|
||||
fmt::format(
|
||||
"Geode version {}\nis required to run this mod\n(installed: {})",
|
||||
node->getMetadata().getGeodeVersion().toVString(),
|
||||
this->getVersion().toVString()
|
||||
)
|
||||
});
|
||||
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->hasUnresolvedDependencies()) {
|
||||
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
|
||||
return;
|
||||
|
@ -444,35 +475,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = node->getMetadata().checkGameVersion();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnsupportedVersion,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("{}", res.unwrapErr());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
|
||||
this->addProblem({
|
||||
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
|
||||
node,
|
||||
fmt::format(
|
||||
"Geode version {}\nis required to run this mod\n(installed: {})",
|
||||
node->getMetadata().getGeodeVersion().toVString(),
|
||||
this->getVersion().toVString()
|
||||
)
|
||||
});
|
||||
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (early) {
|
||||
|
@ -524,6 +526,10 @@ void Loader::Impl::findProblems() {
|
|||
log::debug("{} is not enabled", id);
|
||||
continue;
|
||||
}
|
||||
if (mod->targetsOutdatedVersion()) {
|
||||
log::debug("{} is outdated", id);
|
||||
continue;
|
||||
}
|
||||
log::debug("{}", id);
|
||||
log::pushNest();
|
||||
|
||||
|
|
|
@ -255,13 +255,13 @@ bool Mod::hasSavedValue(std::string_view key) {
|
|||
bool Mod::hasLoadProblems() const {
|
||||
return m_impl->hasLoadProblems();
|
||||
}
|
||||
bool Mod::targetsOutdatedGDVersion() const {
|
||||
std::optional<LoadProblem> Mod::targetsOutdatedVersion() const {
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (problem.type == LoadProblem::Type::UnsupportedVersion) {
|
||||
return true;
|
||||
if (problem.isOutdated()) {
|
||||
return problem;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<LoadProblem> Mod::getAllProblems() const {
|
||||
return m_impl->getProblems();
|
||||
|
@ -269,11 +269,7 @@ std::vector<LoadProblem> Mod::getAllProblems() const {
|
|||
std::vector<LoadProblem> Mod::getProblems() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type != LoadProblem::Type::Recommendation &&
|
||||
problem.type != LoadProblem::Type::Suggestion &&
|
||||
problem.type != LoadProblem::Type::UnsupportedVersion
|
||||
) {
|
||||
if (problem.isProblem()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -282,10 +278,7 @@ std::vector<LoadProblem> Mod::getProblems() const {
|
|||
std::vector<LoadProblem> Mod::getRecommendations() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type == LoadProblem::Type::Recommendation ||
|
||||
problem.type == LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isSuggestion()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -706,11 +706,7 @@ bool Mod::Impl::isCurrentlyLoading() const {
|
|||
|
||||
bool Mod::Impl::hasLoadProblems() const {
|
||||
for (auto const& problem : m_problems) {
|
||||
if (
|
||||
problem.type != LoadProblem::Type::Recommendation &&
|
||||
problem.type != LoadProblem::Type::Suggestion &&
|
||||
problem.type != LoadProblem::Type::UnsupportedVersion
|
||||
) {
|
||||
if (problem.isProblem()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ $on_mod(Loaded) {
|
|||
ColorProvider::get()->define("mod-list-version-label-updates-available"_spr, ccc3(88, 202, 255));
|
||||
ColorProvider::get()->define("mod-list-restart-required-label"_spr, ccc3(153, 245, 245));
|
||||
ColorProvider::get()->define("mod-list-restart-required-label-bg"_spr, ccc3(123, 156, 163));
|
||||
ColorProvider::get()->define("mod-list-outdated-label"_spr, ccc3(245, 153, 245));
|
||||
ColorProvider::get()->define("mod-list-outdated-label-bg"_spr, ccc3(156, 123, 163));
|
||||
ColorProvider::get()->define("mod-list-search-bg"_spr, { 83, 65, 109, 255 });
|
||||
ColorProvider::get()->define("mod-list-updates-available-bg"_spr, { 139, 89, 173, 255 });
|
||||
ColorProvider::get()->define("mod-list-updates-available-bg-2"_spr, { 45, 110, 222, 255 });
|
||||
|
|
|
@ -95,6 +95,17 @@ bool ModItem::init(ModSource&& source) {
|
|||
m_restartRequiredLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
||||
m_infoContainer->addChild(m_restartRequiredLabel);
|
||||
|
||||
m_outdatedLabel = createTagLabel(
|
||||
fmt::format("Outdated (GD {})", m_source.getMetadata().getGameVersion().value_or("*")),
|
||||
{
|
||||
to3B(ColorProvider::get()->color("mod-list-outdated-label"_spr)),
|
||||
to3B(ColorProvider::get()->color("mod-list-outdated-label-bg"_spr))
|
||||
}
|
||||
);
|
||||
m_outdatedLabel->setID("outdated-label");
|
||||
m_outdatedLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
|
||||
m_infoContainer->addChild(m_outdatedLabel);
|
||||
|
||||
m_downloadBarContainer = CCNode::create();
|
||||
m_downloadBarContainer->setID("download-bar-container");
|
||||
m_downloadBarContainer->setContentSize({ 320, 30 });
|
||||
|
@ -185,7 +196,7 @@ bool ModItem::init(ModSource&& source) {
|
|||
m_viewMenu->addChild(m_enableToggle);
|
||||
m_viewMenu->updateLayout();
|
||||
}
|
||||
if (mod->hasLoadProblems() || mod->targetsOutdatedGDVersion()) {
|
||||
if (mod->hasLoadProblems() || mod->targetsOutdatedVersion()) {
|
||||
auto viewErrorSpr = createGeodeCircleButton(
|
||||
CCSprite::createWithSpriteFrameName("exclamation.png"_spr), 1.f,
|
||||
CircleBaseSize::Small
|
||||
|
@ -342,7 +353,6 @@ void ModItem::updateState() {
|
|||
m_downloadBarContainer->setVisible(false);
|
||||
m_downloadWaiting->setVisible(false);
|
||||
}
|
||||
m_infoContainer->updateLayout();
|
||||
|
||||
// Set default colors based on source to start off with
|
||||
// (possibly overriding later based on state)
|
||||
|
@ -410,16 +420,22 @@ void ModItem::updateState() {
|
|||
m_titleContainer->updateLayout();
|
||||
|
||||
// If there were problems, tint the BG red
|
||||
m_outdatedLabel->setVisible(false);
|
||||
if (m_source.asMod()) {
|
||||
if (m_source.asMod()->hasLoadProblems()) {
|
||||
m_bg->setColor("mod-list-errors-found"_cc3b);
|
||||
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
||||
}
|
||||
if (m_source.asMod()->targetsOutdatedGDVersion()) {
|
||||
m_bg->setOpacity(isGeodeTheme() ? 0 : 0);
|
||||
if (m_source.asMod()->targetsOutdatedVersion()) {
|
||||
m_bg->setColor("mod-list-outdated-label"_cc3b);
|
||||
m_bg->setOpacity(isGeodeTheme() ? 25 : 90);
|
||||
m_outdatedLabel->setVisible(true);
|
||||
m_developers->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_infoContainer->updateLayout();
|
||||
|
||||
// Highlight item via BG if it wants to restart for extra UI attention
|
||||
if (wantsRestart) {
|
||||
m_bg->setColor("mod-list-restart-required-label"_cc3b);
|
||||
|
@ -541,7 +557,38 @@ void ModItem::onView(CCObject*) {
|
|||
}
|
||||
void ModItem::onViewError(CCObject*) {
|
||||
if (auto mod = m_source.asMod()) {
|
||||
ModErrorPopup::create(mod)->show();
|
||||
if (auto problem = mod->targetsOutdatedVersion()) {
|
||||
std::string issue;
|
||||
std::string howToFix;
|
||||
switch (problem->type) {
|
||||
default:
|
||||
case LoadProblem::Type::UnsupportedVersion: {
|
||||
issue = fmt::format("<cy>{}</c>", problem->message);
|
||||
howToFix = "wait for the developer to <cj>release an update to "
|
||||
"the mod</c> that supports the newer version.";
|
||||
} break;
|
||||
|
||||
case LoadProblem::Type::UnsupportedGeodeVersion: {
|
||||
issue = "This mod is made for a <cp>newer version of Geode</c>.";
|
||||
howToFix = "<cp>update Geode</c> by enabling <co>Automatic Updates</c> "
|
||||
"or redownloading it from the Geode website.";
|
||||
} break;
|
||||
|
||||
case LoadProblem::Type::NeedsNewerGeodeVersion: {
|
||||
issue = "This mod is made for an <cy>older version of Geode</c>.";
|
||||
howToFix = "wait for the developer to <cj>release an update to "
|
||||
"the mod</c> that supports the newer version.";
|
||||
} break;
|
||||
}
|
||||
FLAlertLayer::create(
|
||||
"Outdated",
|
||||
fmt::format("{} Please {}", issue, howToFix),
|
||||
"OK"
|
||||
)->show();
|
||||
}
|
||||
else {
|
||||
ModErrorPopup::create(mod)->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
void ModItem::onEnable(CCObject*) {
|
||||
|
|
|
@ -25,6 +25,7 @@ protected:
|
|||
CCNode* m_recommendedBy;
|
||||
CCLabelBMFont* m_developerLabel;
|
||||
ButtonSprite* m_restartRequiredLabel;
|
||||
ButtonSprite* m_outdatedLabel;
|
||||
CCNode* m_downloadWaiting;
|
||||
CCNode* m_downloadBarContainer;
|
||||
Slider* m_downloadBar;
|
||||
|
|
|
@ -13,7 +13,7 @@ bool InstalledModsQuery::preCheck(ModSource const& src) const {
|
|||
}
|
||||
// If only errors requested, only show mods with errors (duh)
|
||||
if (type == InstalledModListType::OnlyOutdated) {
|
||||
return src.asMod()->targetsOutdatedGDVersion();
|
||||
return src.asMod()->targetsOutdatedVersion().has_value();
|
||||
}
|
||||
if (type == InstalledModListType::OnlyErrors) {
|
||||
return src.asMod()->hasLoadProblems();
|
||||
|
|
|
@ -230,7 +230,13 @@ void filterModsWithLocalQuery(ModListSource::ProvidedMods& mods, Query const& qu
|
|||
if (a.second != b.second) {
|
||||
return a.second > b.second;
|
||||
}
|
||||
// Sort secondarily alphabetically
|
||||
// Make sure outdated mods are always last by default
|
||||
auto aIsOutdated = a.first.getMetadata().checkGameVersion().isErr();
|
||||
auto bIsOutdated = b.first.getMetadata().checkGameVersion().isErr();
|
||||
if (aIsOutdated != bIsOutdated) {
|
||||
return !aIsOutdated;
|
||||
}
|
||||
// Fallback sort alphabetically
|
||||
return utils::string::caseInsensitiveCompare(
|
||||
a.first.getMetadata().getName(),
|
||||
b.first.getMetadata().getName()
|
||||
|
|
Loading…
Reference in a new issue