From 196d50ee9b0929a70c2fcbef594bcaf85ac1b533 Mon Sep 17 00:00:00 2001
From: HJfod <60038575+HJfod@users.noreply.github.com>
Date: Tue, 7 May 2024 17:32:14 +0300
Subject: [PATCH] mod issues stuff

---
 loader/resources/exclamation-red.png   | Bin 0 -> 3828 bytes
 loader/src/hooks/MenuLayer.cpp         |  17 ++-
 loader/src/internal/FixModIssues.cpp   | 187 +++++++++++++++++++++++++
 loader/src/internal/FixModIssues.hpp   |   6 +
 loader/src/loader/Loader.cpp           |   7 -
 loader/src/loader/Mod.cpp              |  30 ++--
 loader/src/ui/other/FixIssuesPopup.cpp |  28 ----
 loader/src/ui/other/FixIssuesPopup.hpp |  19 ---
 8 files changed, 226 insertions(+), 68 deletions(-)
 create mode 100644 loader/resources/exclamation-red.png
 create mode 100644 loader/src/internal/FixModIssues.cpp
 create mode 100644 loader/src/internal/FixModIssues.hpp
 delete mode 100644 loader/src/ui/other/FixIssuesPopup.cpp
 delete mode 100644 loader/src/ui/other/FixIssuesPopup.hpp

diff --git a/loader/resources/exclamation-red.png b/loader/resources/exclamation-red.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa265a254187825bc41110f76b5d33549369ea2e
GIT binary patch
literal 3828
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

literal 0
HcmV?d00001

diff --git a/loader/src/hooks/MenuLayer.cpp b/loader/src/hooks/MenuLayer.cpp
index ce1819e7..26606bdb 100644
--- a/loader/src/hooks/MenuLayer.cpp
+++ b/loader/src/hooks/MenuLayer.cpp
@@ -14,7 +14,6 @@
 #include <loader/updater.hpp>
 #include <Geode/binding/ButtonSprite.hpp>
 #include <Geode/modify/LevelSelectLayer.hpp>
-#include <ui/other/FixIssuesPopup.hpp>
 
 using namespace geode::prelude;
 
@@ -83,7 +82,20 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
         }
 
         // show if some mods failed to load
-        checkLoadingIssues(this);
+        if (Loader::get()->getProblems().size()) {
+            static bool shownProblemPopup = false;
+            if (!shownProblemPopup) {
+                shownProblemPopup = true;
+                Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
+            }
+
+            auto icon = CCSprite::createWithSpriteFrameName("exclamation-red.png"_spr);
+            icon->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
+            icon->setID("errors-found");
+            icon->setZOrder(99);
+            icon->setScale(.8f);
+            m_fields->m_geodeButton->addChild(icon);
+        }
         
         // show if the user tried to be naughty and load arbitrary DLLs
         static bool shownTriedToLoadDlls = false;
@@ -154,6 +166,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
                     auto updatesFound = result->unwrap();
                     if (updatesFound.size() && !m_fields->m_geodeButton->getChildByID("updates-available")) {
                         log::info("Found updates for mods: {}!", updatesFound);
+
                         auto icon = CCSprite::createWithSpriteFrameName("updates-available.png"_spr);
                         icon->setPosition(
                             m_fields->m_geodeButton->getContentSize() - CCSize { 10.f, 10.f }
diff --git a/loader/src/internal/FixModIssues.cpp b/loader/src/internal/FixModIssues.cpp
new file mode 100644
index 00000000..71cf326a
--- /dev/null
+++ b/loader/src/internal/FixModIssues.cpp
@@ -0,0 +1,187 @@
+#include "FixModIssues.hpp"
+#include <Geode/loader/Loader.hpp>
+#include <server/DownloadManager.hpp>
+#include <ui/mods/sources/ModSource.hpp>
+
+// TODO: UNFINISHED!!!
+// If you want to bring this back, you are free to do so - 
+// I just didn't feel like the engineering effort is worth it.
+// The point of this is to be a mod load issue auto-resolver
+
+using namespace geode::prelude;
+
+class AutoFixStatus final {
+protected:
+    struct Question final {
+        std::string title;
+        std::string content;
+        std::string optionA;
+        std::string optionB;
+        MiniFunction<void(bool)> after;
+    };
+
+    EventListener<server::ModDownloadFilter> m_download;
+    std::vector<std::string> m_unsolved;
+    std::deque<Question> m_questionQueue;
+
+    static ghc::filesystem::path getPath(LoadProblem const& problem) {
+        return std::visit(makeVisitor {
+            [](ghc::filesystem::path const& path) {
+                return path;
+            },
+            [](ModMetadata const& meta) {
+                return meta.getPath();
+            },
+            [](Mod* mod) {
+                return mod->getPackagePath();
+            },
+        }, problem.cause);
+    }
+    static std::string getName(LoadProblem const& problem) {
+        return std::visit(makeVisitor {
+            [](ghc::filesystem::path const& path) {
+                return path.string();
+            },
+            [](ModMetadata const& meta) {
+                return meta.getID();
+            },
+            [](Mod* mod) {
+                return mod->getID();
+            },
+        }, problem.cause);
+    }
+
+    void nextQuestion() {
+        auto& question = m_questionQueue.front();
+        createQuickPopup(
+            question.title.c_str(),
+            question.content,
+            question.optionA.c_str(), question.optionB.c_str(),
+            [this, &question](auto*, bool btn2) {
+                question.after(btn2);
+                m_questionQueue.pop_front();
+                if (!m_questionQueue.empty()) {
+                    this->nextQuestion();
+                }
+            }
+        );
+    }
+    void ask(Question&& question) {
+        m_questionQueue.push_back(question);
+        // If this was the first question in the queue, start asking
+        if (m_questionQueue.size() == 1) {
+            this->nextQuestion();
+        }
+    }
+
+public:
+    void start() {
+        for (auto problem : Loader::get()->getProblems()) {
+            switch (problem.type) {
+                // Errors where the correct solution is to just delete the invalid .geode package
+                case LoadProblem::Type::InvalidFile:
+                // todo: maybe duplicate should prompt which one to delete?
+                // or maybe the user can just figure that one out since that only happens 
+                // on manual install (as server installs delete the old version using the 
+                // real path and not the old index filename trickery)
+                case LoadProblem::Type::Duplicate:
+                case LoadProblem::Type::SetupFailed:
+                case LoadProblem::Type::LoadFailed:
+                case LoadProblem::Type::EnableFailed:
+                case LoadProblem::Type::UnsupportedGeodeVersion:
+                case LoadProblem::Type::NeedsNewerGeodeVersion:
+                case LoadProblem::Type::UnsupportedVersion:
+                {
+                    auto path = getPath(problem);
+                    std::error_code ec;
+                    ghc::filesystem::remove(path, ec);
+                    if (ec) {
+                        m_unsolved.push_back(fmt::format("Failed to delete '{}'", path));
+                    }
+                }
+                break;
+
+                // Missing / bad dependencies
+                case LoadProblem::Type::MissingDependency:
+                case LoadProblem::Type::OutdatedDependency:
+                {
+                    // Parse the damn "{id} {version}" string to get the id
+                    // God I wish this was Rust so the enum variant could just have the ID right there
+                    auto id = problem.message.substr(0, problem.message.find(' '));
+
+                    // If this mod is already installed, see if it can be updated
+                    if (auto mod = Loader::get()->getInstalledMod(id)) {
+                        // todo: after update check, if there are no updates, mark this as unsolved, otherwise start update
+                        ModSource(mod).checkUpdates();
+                    }
+                    // Otherwise try to install the mod
+                    // todo: Check if the mod can be downloaded, and if not mark this as unsolved
+                    else {
+                        server::ModDownloadManager::get()->startDownload(id, std::nullopt);
+                    }
+                }
+                break;
+
+                // Enable the dependency duh
+                case LoadProblem::Type::DisabledDependency:
+                {
+                    auto mod = std::get<Mod*>(problem.cause);
+                    if (!mod->enable()) {
+                        m_unsolved.push_back(fmt::format("Failed to enable '{}'", mod->getID()));
+                    }
+                }
+                break;
+
+                // Incompatabilities; the user should choose which to disable
+                case LoadProblem::Type::PresentIncompatibility:
+                case LoadProblem::Type::OutdatedIncompatibility:
+                case LoadProblem::Type::OutdatedConflict:
+                case LoadProblem::Type::Conflict:
+                {
+                    auto modA = std::get<Mod*>(problem.cause)->getID();
+                    auto modB = problem.message;
+                    this->ask(Question {
+                        .title = "Select Mod",
+                        .content = fmt::format(
+                            "The mods <cy>'{}'</c> and <cp>{}</c> are <cr>incompatible</c>.\n"
+                            "<cj>Please select which one to disable</c>.",
+                            modA, modB
+                        ),
+                        .optionA = modA,
+                        .optionB = modB,
+                        .after = [modA, modB](bool b) {
+
+                        }
+                    });
+                }
+                break;
+
+                // Errors we can't fix, or ones where you should probably just restart the game / PC
+                default:
+                case LoadProblem::Type::UnzipFailed:
+                case LoadProblem::Type::Unknown:
+                {
+                    auto name = getName(problem);
+                    m_unsolved.push_back(fmt::format(
+                        "<cr>Unknown/unsolvable error</c> with <cg>'{}'</c>: {}\n"
+                        "<cy>Maybe try restarting your computer?</c>",
+                        name, problem.message
+                    ));
+                }
+                break;
+            }
+        }
+    }
+};
+
+static std::optional<AutoFixStatus> STATUS = {};
+
+void internal::tryAutoFixModIssues() {
+    if (!STATUS) {
+        STATUS.emplace(AutoFixStatus());
+        STATUS->start();
+    }
+}
+bool internal::hasTriedToFixIssues() {
+    return STATUS.has_value();
+}
diff --git a/loader/src/internal/FixModIssues.hpp b/loader/src/internal/FixModIssues.hpp
new file mode 100644
index 00000000..539fe991
--- /dev/null
+++ b/loader/src/internal/FixModIssues.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace internal {
+    void tryAutoFixModIssues();
+    bool hasTriedToFixIssues();
+}
diff --git a/loader/src/loader/Loader.cpp b/loader/src/loader/Loader.cpp
index ce46c9ad..3f1d59e8 100644
--- a/loader/src/loader/Loader.cpp
+++ b/loader/src/loader/Loader.cpp
@@ -79,13 +79,6 @@ std::vector<LoadProblem> Loader::getProblems() const {
         }
     }
     return result;
-    return ranges::filter(
-        m_impl->getProblems(),
-        [](auto const& problem) {
-            return problem.type != LoadProblem::Type::Recommendation && 
-                problem.type != LoadProblem::Type::Suggestion;
-        }
-    );
 }
 std::vector<LoadProblem> Loader::getRecommendations() const {
     std::vector<LoadProblem> result;
diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp
index 695663c7..e701a9bc 100644
--- a/loader/src/loader/Mod.cpp
+++ b/loader/src/loader/Mod.cpp
@@ -252,22 +252,28 @@ std::vector<LoadProblem> Mod::getAllProblems() const {
     return m_impl->getProblems();
 }
 std::vector<LoadProblem> Mod::getProblems() const {
-    return ranges::filter(
-        this->getAllProblems(),
-        [](auto const& problem) {
-            return problem.type != LoadProblem::Type::Recommendation && 
-                problem.type != LoadProblem::Type::Suggestion;
+    std::vector<LoadProblem> result;
+    for (auto problem : this->getAllProblems()) {
+        if (
+            problem.type != LoadProblem::Type::Recommendation && 
+            problem.type != LoadProblem::Type::Suggestion
+        ) {
+            result.push_back(problem);
         }
-    );
+    }
+    return result;
 }
 std::vector<LoadProblem> Mod::getRecommendations() const {
-    return ranges::filter(
-        this->getAllProblems(),
-        [](auto const& problem) {
-            return problem.type == LoadProblem::Type::Recommendation || 
-                problem.type == LoadProblem::Type::Suggestion;
+    std::vector<LoadProblem> result;
+    for (auto problem : this->getAllProblems()) {
+        if (
+            problem.type == LoadProblem::Type::Recommendation || 
+            problem.type == LoadProblem::Type::Suggestion
+        ) {
+            result.push_back(problem);
         }
-    );
+    }
+    return result;
 }
 bool Mod::shouldLoad() const {
     return m_impl->shouldLoad();
diff --git a/loader/src/ui/other/FixIssuesPopup.cpp b/loader/src/ui/other/FixIssuesPopup.cpp
deleted file mode 100644
index 35e7cb67..00000000
--- a/loader/src/ui/other/FixIssuesPopup.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "FixIssuesPopup.hpp"
-#include <Geode/loader/Loader.hpp>
-
-bool FixIssuesPopup::setup() {
-    m_noElasticity = true;
-
-    this->setTitle("Problems Loading Mods");
-
-    return true;
-}
-
-FixIssuesPopup* FixIssuesPopup::create() {
-    auto ret = new FixIssuesPopup();
-    if (ret && ret->init(350, 280)) {
-        ret->autorelease();
-        return ret;
-    }
-    CC_SAFE_DELETE(ret);
-    return nullptr;
-}
-
-void checkLoadingIssues(CCNode* targetScene) {
-    if (Loader::get()->getProblems().size()) {
-        auto popup = FixIssuesPopup::create();
-        popup->m_scene = targetScene;
-        popup->show();
-    }
-}
diff --git a/loader/src/ui/other/FixIssuesPopup.hpp b/loader/src/ui/other/FixIssuesPopup.hpp
deleted file mode 100644
index 8e0bae3a..00000000
--- a/loader/src/ui/other/FixIssuesPopup.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <Geode/ui/Popup.hpp>
-#include "../mods/GeodeStyle.hpp"
-#include "../mods/list/ModProblemItemList.hpp"
-
-using namespace geode::prelude;
-
-class FixIssuesPopup : public GeodePopup<> {
-protected:
-    ModProblemItemList* m_list;
-
-    bool setup() override;
-
-public:
-    static FixIssuesPopup* create();
-};
-
-void checkLoadingIssues(CCNode* targetScene = nullptr);