diff --git a/launcher/modplatform/ResourceAPI.cpp b/launcher/modplatform/ResourceAPI.cpp index cda90b677..bd683ed21 100644 --- a/launcher/modplatform/ResourceAPI.cpp +++ b/launcher/modplatform/ResourceAPI.cpp @@ -148,9 +148,9 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback&& callbacks) const +Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback&& callbacks, bool askRetry) const { - auto [job, response] = getProject(args.pack->addonId.toString()); + auto [job, response] = getProject(args.pack->addonId.toString(), askRetry); QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] { auto pack = args.pack; @@ -284,7 +284,7 @@ QString ResourceAPI::mapMCVersionToModrinth(Version v) const return verStr; } -std::pair ResourceAPI::getProject(QString addonId) const +std::pair ResourceAPI::getProject(QString addonId, bool askRetry) const { auto project_url_optional = getInfoURL(addonId); if (!project_url_optional.has_value()) @@ -293,6 +293,7 @@ std::pair ResourceAPI::getProject(QString addonId) const auto project_url = project_url_optional.value(); auto netJob = makeShared(QString("%1::GetProject").arg(addonId), APPLICATION->network()); + netJob->setAskRetry(askRetry); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(project_url)); netJob->addNetAction(action); diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 0ad55775c..51b6d4b50 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -115,10 +115,10 @@ class ResourceAPI { public slots: virtual Task::Ptr searchProjects(SearchArgs&&, Callback>&&) const; - virtual std::pair getProject(QString addonId) const; + virtual std::pair getProject(QString addonId, bool askRetry = true) const; virtual std::pair getProjects(QStringList addonIds) const = 0; - virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback&&) const; + virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback&&, bool askRetry = true) const; Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback>&& callbacks) const; virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback&&) const; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index e90eafbf2..a3a043ba9 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -139,15 +139,18 @@ void ResourceModel::search() if (hasActiveSearchJob()) return; - if (m_search_term.startsWith("#")) { + if (m_search_state != SearchState::ResetRequested && m_search_term.startsWith("#")) { auto projectId = m_search_term.mid(1); if (!projectId.isEmpty()) { ResourceAPI::Callback callbacks; - callbacks.on_fail = [this](QString reason, int) { + callbacks.on_fail = [this](QString reason, int network_error_code) { if (!s_running_models.constFind(this).value()) return; - searchRequestFailed(reason, -1); + if (network_error_code == 404) { + m_search_state = SearchState::ResetRequested; + } + searchRequestFailed(reason, network_error_code); }; callbacks.on_abort = [this] { if (!s_running_models.constFind(this).value()) @@ -162,7 +165,7 @@ void ResourceModel::search() }; auto project = std::make_shared(); project->addonId = projectId; - if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks)); job) + if (auto job = m_api->getProjectInfo({ project }, std::move(callbacks), false); job) runSearchJob(job); return; } @@ -407,6 +410,9 @@ void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int net // Network error QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load mods.")); break; + case 404: + // 404 Not Found, some APIs return this when nothing is found, no need to bother the user + break; case 409: // 409 Gone, notify user to update QMessageBox::critical(nullptr, tr("Error"), @@ -414,7 +420,14 @@ void ResourceModel::searchRequestFailed([[maybe_unused]] QString reason, int net break; } - m_search_state = SearchState::Finished; + if (m_search_state == SearchState::ResetRequested) { + clearData(); + + m_next_search_offset = 0; + search(); + } else { + m_search_state = SearchState::Finished; + } } void ResourceModel::searchRequestAborted() diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 5d968d65a..861dd9f22 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -165,12 +165,20 @@ void ListModel::fetchMore(const QModelIndex& parent) void ListModel::performPaginatedSearch() { static const FlameAPI api; - if (m_currentSearchTerm.startsWith("#")) { + + // activate search by id only for numerical values because all CurseForge ids are numerical + static const QRegularExpression s_projectIdExpr("^\\#[0-9]+$"); + if (m_searchState != ResetRequested && s_projectIdExpr.match(m_currentSearchTerm).hasMatch()) { auto projectId = m_currentSearchTerm.mid(1); if (!projectId.isEmpty()) { ResourceAPI::Callback callbacks; - callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); }; + callbacks.on_fail = [this](QString reason, int network_error_code) { + if (network_error_code == 404) { + m_searchState = ResetRequested; + } + searchRequestFailed(reason); + }; callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); }; callbacks.on_abort = [this] { qCritical() << "Search task aborted by an unknown reason!"; @@ -178,7 +186,7 @@ void ListModel::performPaginatedSearch() }; auto project = std::make_shared(); project->addonId = projectId; - if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) { + if (auto job = api.getProjectInfo({ project }, std::move(callbacks), false); job) { m_jobPtr = job; m_jobPtr->start(); } diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 05cd2970c..03518c3af 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -135,20 +135,26 @@ void ModpackListModel::performPaginatedSearch() return; static const ModrinthAPI api; - if (m_currentSearchTerm.startsWith("#")) { + // Modrinth ids are not limited to numbers and can be any length + if (m_searchState != ResetRequested && m_currentSearchTerm.startsWith("#")) { auto projectId = m_currentSearchTerm.mid(1); if (!projectId.isEmpty()) { ResourceAPI::Callback callbacks; - callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); }; + callbacks.on_fail = [this](QString reason, int network_error_code) { + if (network_error_code == 404) { + m_searchState = ResetRequested; + } + searchRequestFailed(reason, network_error_code); + }; callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); }; callbacks.on_abort = [this] { qCritical() << "Search task aborted by an unknown reason!"; - searchRequestFailed("Aborted"); + searchRequestFailed("Aborted", 0); }; auto project = std::make_shared(); project->addonId = projectId; - if (auto job = api.getProjectInfo({ project }, std::move(callbacks)); job) { + if (auto job = api.getProjectInfo({ project }, std::move(callbacks), false); job) { m_jobPtr = job; m_jobPtr->start(); } @@ -161,10 +167,10 @@ void ModpackListModel::performPaginatedSearch() ResourceAPI::Callback> callbacks{}; callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); }; - callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); }; + callbacks.on_fail = [this](QString reason, int network_error_code) { searchRequestFailed(reason, network_error_code); }; callbacks.on_abort = [this] { qCritical() << "Search task aborted by an unknown reason!"; - searchRequestFailed("Aborted"); + searchRequestFailed("Aborted", 0); }; auto netJob = api.searchProjects({ ModPlatform::ResourceType::Modpack, m_nextSearchOffset, m_currentSearchTerm, sort, m_filter->loaders, @@ -316,13 +322,12 @@ void ModpackListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Pt endInsertRows(); } -void ModpackListModel::searchRequestFailed(QString) +void ModpackListModel::searchRequestFailed(QString reason, int network_error_code) { - auto failed_action = dynamic_cast(m_jobPtr.get())->getFailedActions().at(0); - if (failed_action->replyStatusCode() == -1) { - // Network error + if (network_error_code == -1) { + // Unknown error in network stack QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks.")); - } else if (failed_action->replyStatusCode() == 409) { + } else if (network_error_code == 409) { // 409 Gone, notify user to update QMessageBox::critical(nullptr, tr("Error"), //: %1 refers to the launcher itself diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h index 96f6fd128..3e0fc1686 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.h @@ -85,7 +85,7 @@ class ModpackListModel : public QAbstractListModel { public slots: void searchRequestFinished(QList& doc_all); - void searchRequestFailed(QString reason); + void searchRequestFailed(QString reason, int network_error_code); void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr); protected slots: