diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp index 61c33a18d..856036d23 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.cpp +++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp @@ -38,7 +38,7 @@ void XboxUserStep::perform() QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setRawHeader("Accept", "application/json"); - // set contract-verison header (prevent err 400 bad-request?) + // set contract-version header (prevent err 400 bad-request?) // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders request.setRawHeader("x-xbl-contract-version", "1"); diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 61e6a3a98..8bd83d988 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -24,6 +24,7 @@ class CheckUpdateTask : public Task { QString old_hash; QString old_version; QString new_version; + std::optional new_version_type; QString changelog; ModPlatform::ResourceProvider provider; shared_qobject_ptr download; @@ -33,10 +34,18 @@ class CheckUpdateTask : public Task { QString old_h, QString old_v, QString new_v, + std::optional new_v_type, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr t) - : name(name), old_hash(old_h), old_version(old_v), new_version(new_v), changelog(changelog), provider(p), download(t) + : name(name) + , old_hash(old_h) + , old_version(old_v) + , new_version(new_v) + , new_version_type(new_v_type) + , changelog(changelog) + , provider(p) + , download(t) {} }; diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index e8e4a38dd..fc79dff15 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -24,6 +24,40 @@ namespace ModPlatform { +static const QMap s_indexed_version_type_names = { + { "release", IndexedVersionType::VersionType::Release }, + { "beta", IndexedVersionType::VersionType::Beta }, + { "alpha", IndexedVersionType::VersionType::Alpha } +}; + +IndexedVersionType::IndexedVersionType(const QString& type) : IndexedVersionType(enumFromString(type)) {} + +IndexedVersionType::IndexedVersionType(const IndexedVersionType::VersionType& type) +{ + m_type = type; +} + +IndexedVersionType::IndexedVersionType(const IndexedVersionType& other) +{ + m_type = other.m_type; +} + +IndexedVersionType& IndexedVersionType::operator=(const IndexedVersionType& other) +{ + m_type = other.m_type; + return *this; +} + +const QString IndexedVersionType::toString(const IndexedVersionType::VersionType& type) +{ + return s_indexed_version_type_names.key(type, "unknown"); +} + +IndexedVersionType::VersionType IndexedVersionType::enumFromString(const QString& type) +{ + return s_indexed_version_type_names.value(type, IndexedVersionType::VersionType::Unknown); +} + auto ProviderCapabilities::name(ResourceProvider p) -> const char* { switch (p) { diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 7d144176d..4d6759d3b 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -25,6 +25,7 @@ #include #include #include +#include class QIODevice; @@ -58,6 +59,34 @@ struct DonationData { QString url; }; +struct IndexedVersionType { + enum class VersionType { Release = 1, Beta, Alpha, Unknown }; + IndexedVersionType(const QString& type); + IndexedVersionType(const IndexedVersionType::VersionType& type); + IndexedVersionType(const IndexedVersionType& type); + IndexedVersionType() : IndexedVersionType(IndexedVersionType::VersionType::Unknown) {} + static const QString toString(const IndexedVersionType::VersionType& type); + static IndexedVersionType::VersionType enumFromString(const QString& type); + bool isValid() const { return m_type != IndexedVersionType::VersionType::Unknown; } + IndexedVersionType& operator=(const IndexedVersionType& other); + bool operator==(const IndexedVersionType& other) const { return m_type == other.m_type; } + bool operator==(const IndexedVersionType::VersionType& type) const { return m_type == type; } + bool operator!=(const IndexedVersionType& other) const { return m_type != other.m_type; } + bool operator!=(const IndexedVersionType::VersionType& type) const { return m_type != type; } + bool operator<(const IndexedVersionType& other) const { return m_type < other.m_type; } + bool operator<(const IndexedVersionType::VersionType& type) const { return m_type < type; } + bool operator<=(const IndexedVersionType& other) const { return m_type <= other.m_type; } + bool operator<=(const IndexedVersionType::VersionType& type) const { return m_type <= type; } + bool operator>(const IndexedVersionType& other) const { return m_type > other.m_type; } + bool operator>(const IndexedVersionType::VersionType& type) const { return m_type > type; } + bool operator>=(const IndexedVersionType& other) const { return m_type >= other.m_type; } + bool operator>=(const IndexedVersionType::VersionType& type) const { return m_type >= type; } + + QString toString() const { return toString(m_type); } + + IndexedVersionType::VersionType m_type; +}; + struct Dependency { QVariant addonId; DependencyType type; @@ -69,6 +98,7 @@ struct IndexedVersion { QVariant fileId; QString version; QString version_number = {}; + IndexedVersionType version_type; QStringList mcVersion; QString downloadUrl; QString date; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index e99ce3a56..a9697893d 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -133,7 +133,9 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe for (auto file : arr) { auto file_obj = Json::requireObject(file); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); - if (file_tmp.date > ver.date && (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders)) + if (file_tmp.date > ver.date && + (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders) && + file_tmp.version_type <= ver.version_type) ver = file_tmp; } diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 0501dda62..c014863a3 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -173,7 +173,7 @@ void FlameCheckUpdate::executeTask() } auto download_task = makeShared(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, + m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, latest_ver.version_type, api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), ModPlatform::ResourceProvider::FLAME, download_task); } diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 494dc2a7e..2adcd7814 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -96,8 +96,9 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { + bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date; + return a.date > b.date && a_better_release; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; @@ -139,6 +140,22 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); + ModPlatform::IndexedVersionType::VersionType ver_type; + switch (Json::requireInteger(obj, "releaseType")) { + case 1: + ver_type = ModPlatform::IndexedVersionType::VersionType::Release; + break; + case 2: + ver_type = ModPlatform::IndexedVersionType::VersionType::Beta; + break; + case 3: + ver_type = ModPlatform::IndexedVersionType::VersionType::Alpha; + break; + default: + ver_type = ModPlatform::IndexedVersionType::VersionType::Unknown; + } + file.version_type = ModPlatform::IndexedVersionType(ver_type); + auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { auto hash_entry = Json::ensureObject(h); diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index 21835a543..71f1e4a2d 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -89,6 +89,22 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr) // pick the latest version supported file.mcVersion = versionArray[0].toString(); file.version = Json::requireString(version, "displayName"); + + ModPlatform::IndexedVersionType::VersionType ver_type; + switch (Json::requireInteger(version, "releaseType")) { + case 1: + ver_type = ModPlatform::IndexedVersionType::VersionType::Release; + break; + case 2: + ver_type = ModPlatform::IndexedVersionType::VersionType::Beta; + break; + case 3: + ver_type = ModPlatform::IndexedVersionType::VersionType::Alpha; + break; + default: + ver_type = ModPlatform::IndexedVersionType::VersionType::Unknown; + } + file.version_type = ModPlatform::IndexedVersionType(ver_type); file.downloadUrl = Json::ensureString(version, "downloadUrl"); // only add if we have a download URL (third party distribution is enabled) diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index b089b722c..b2a12a67f 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -17,6 +17,7 @@ struct IndexedVersion { int addonId; int fileId; QString version; + ModPlatform::IndexedVersionType version_type; QString mcVersion; QString downloadUrl; }; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index ab07a363c..9b7c53854 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -161,8 +161,8 @@ void ModrinthCheckUpdate::executeTask() auto download_task = makeShared(pack, project_ver, m_mods_folder); - m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.changelog, - ModPlatform::ResourceProvider::MODRINTH, download_task); + m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.version_type, + project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } m_deps.append(std::make_shared(pack, project_ver)); } diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 107b99006..f1e77d465 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -109,8 +109,9 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArra unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { + bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date; + return a.date > b.date && a_better_release; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; @@ -149,6 +150,8 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t } file.version = Json::requireString(obj, "name"); file.version_number = Json::requireString(obj, "version_number"); + file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); + file.changelog = Json::requireString(obj, "changelog"); auto dependencies = Json::ensureArray(obj, "dependencies"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 0d07c6361..a154317fe 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -111,8 +111,9 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc) unsortedVersions.append(file); } auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool { + bool a_better_release = a.version_type <= b.version_type; // dates are in RFC 3339 format - return a.date > b.date; + return a.date > b.date && a_better_release; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); @@ -128,6 +129,7 @@ auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion file.name = Json::requireString(obj, "name"); file.version = Json::requireString(obj, "version_number"); + file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); file.changelog = Json::ensureString(obj, "changelog"); file.id = Json::requireString(obj, "id"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index effa1a84a..8e5306774 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -45,6 +45,8 @@ #include #include +#include "modplatform/ModIndex.h" + class MinecraftInstance; namespace Modrinth { @@ -79,6 +81,7 @@ struct ModpackExtra { struct ModpackVersion { QString name; QString version; + ModPlatform::IndexedVersionType version_type; QString changelog; QString id; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index ee64b392d..1a70ea59a 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -219,8 +219,10 @@ void ModUpdateDialog::checkCandidates() if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); auto download_task = makeShared(dep->pack, dep->version, m_mod_model); - CheckUpdateTask::UpdatableMod updatable = { dep->pack->name, dep->version.hash, "", dep->version.version, - changelog, dep->pack->provider, download_task }; + CheckUpdateTask::UpdatableMod updatable = { + dep->pack->name, dep->version.hash, "", dep->version.version, dep->version.version_type, + changelog, dep->pack->provider, download_task + }; appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString())); m_tasks.insert(updatable.name, updatable.download); @@ -415,6 +417,11 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri auto new_version_item = new QTreeWidgetItem(item_top); new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); + if (info.new_version_type.has_value()) { + auto new_version_type_itme = new QTreeWidgetItem(item_top); + new_version_type_itme->setText(0, tr("New Version Type: %1").arg(info.new_version_type.value().toString())); + } + if (!requiredBy.isEmpty()) { auto requiredByItem = new QTreeWidgetItem(item_top); if (requiredBy.length() == 1) { diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index c40b15469..dc7cfff06 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -167,8 +167,8 @@ void ResourceDownloadDialog::confirm() }); for (auto& task : selected) { confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(), - ProviderCaps.name(task->getProvider()), - getRequiredBy.value(task->getPack()->addonId.toString()) }); + ProviderCaps.name(task->getProvider()), getRequiredBy.value(task->getPack()->addonId.toString()), + task->getVersion().version_type.toString() }); } if (confirm_dialog->exec()) { diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 78c2542fd..aa668f8c2 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -77,6 +77,10 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info) itemTop->insertChildren(childIndx++, { requiredByItem }); } + auto versionTypeItem = new QTreeWidgetItem(itemTop); + versionTypeItem->setText(0, tr("Version Type: %1").arg(info.version_type)); + itemTop->insertChildren(childIndx++, { versionTypeItem }); + ui->modTreeWidget->addTopLevelItem(itemTop); } diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index a520cc2a6..596f39c8e 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -18,6 +18,7 @@ class ReviewMessageBox : public QDialog { QString custom_file_path{}; QString provider; QStringList required_by; + QString version_type; }; void appendResource(ResourceInformation&& info); diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 7eb08f6a2..d6cc1fdcc 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -131,8 +131,10 @@ void ModPage::updateVersionList() } // Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out - if ((valid || m_filter->versions.empty()) && !optedOut(version)) - m_ui->versionSelectionBox->addItem(version.version, QVariant(i)); + if ((valid || m_filter->versions.empty()) && !optedOut(version)) { + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; + m_ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(i)); + } } if (m_ui->versionSelectionBox->count() == 0) { m_ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index fc7d64a48..44a91003d 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -266,6 +266,9 @@ void ResourcePage::updateVersionList() if (optedOut(version)) continue; + auto release_type = current_pack->versions[i].version_type.isValid() + ? QString(" [%1]").arg(current_pack->versions[i].version_type.toString()) + : ""; m_ui->versionSelectionBox->addItem(current_pack->versions[i].version, QVariant(i)); } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 50656f427..584d94ad9 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -176,7 +176,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde } for (auto version : current.versions) { - ui->versionSelectionBox->addItem(version.version, QVariant(version.downloadUrl)); + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; + ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(version.downloadUrl)); } QVariant current_updated; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index f7fa8fd78..8e2b9a902 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -217,12 +217,13 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI qDebug() << *response; qWarning() << "Error while reading modrinth modpack version: " << e.cause(); } - for (auto version : current.versions) { + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; if (!version.name.contains(version.version)) - ui->versionSelectionBox->addItem(QString("%1 — %2").arg(version.name, version.version), QVariant(version.id)); + ui->versionSelectionBox->addItem(QString("%1 — %2%3").arg(version.name, version.version, release_type), + QVariant(version.id)); else - ui->versionSelectionBox->addItem(version.name, QVariant(version.id)); + ui->versionSelectionBox->addItem(QString("%1%2").arg(version.name, release_type), QVariant(version.id)); } QVariant current_updated;