From 315879fe6b3143e3828259284e8e332be163411f Mon Sep 17 00:00:00 2001 From: tooomm Date: Tue, 7 Jul 2026 22:30:20 +0200 Subject: [PATCH 01/10] first version --- .../network/update/client/release_channel.cpp | 133 ++++++++++-------- .../network/update/client/release_channel.h | 4 +- 2 files changed, 76 insertions(+), 61 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 260167bc8..5f438e26d 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -2,6 +2,7 @@ #include "version_string.h" +#include #include #include #include @@ -44,54 +45,84 @@ void ReleaseChannel::checkForUpdates() } // Different release channel checking functions for different operating systems -bool ReleaseChannel::downloadMatchesCurrentOS(const QString &fileName) +std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName) { #if defined(Q_OS_MACOS) - static QRegularExpression version_regex("macOS(\\d+)"); - auto match = version_regex.match(fileName); - if (!match.hasMatch()) { - return false; - } - - auto getSystemVersion = [] { - // QSysInfo does not go through translation layers - // We need to use sysctl to reliably detect the underlying architecture + const bool isIntel = [] { char arch[255]; size_t len = sizeof(arch); if (sysctlbyname("machdep.cpu.brand_string", arch, &len, nullptr, 0) == 0) { - // Intel mac is only supported on macOS 13 versions - if (QString::fromUtf8(arch).contains("Intel")) { - return 13; - } + return QString::fromUtf8(arch).contains("Intel"); } - - return QSysInfo::productVersion().split(".")[0].toInt(); - }; - - // older(smaller) releases are compatible with a newer or the same system version - int sys_maj = getSystemVersion(); - int rel_maj = match.captured(1).toInt(); - return rel_maj == sys_maj; + return false; + }(); + const int systemVersion = QSysInfo::productVersion().split(".")[0].toInt(); + if (isIntel) { + static QRegularExpression regex(R"(macOS(\d+)_Intel)"); + auto match = regex.match(fileName); + if (!match.hasMatch()) { + return std::nullopt; + } + int version = match.captured(1).toInt(); + if (version <= systemVersion) { + return version; + } + return std::nullopt; + } + static QRegularExpression regex(R"(macOS(\d+)(?!_Intel))"); + auto match = regex.match(fileName); + if (!match.hasMatch()) { + return std::nullopt; + } + int version = match.captured(1).toInt(); + if (version <= systemVersion) { + return version; + } + return std::nullopt; #elif defined(Q_OS_WIN) -#if Q_PROCESSOR_WORDSIZE == 4 - return fileName.contains("32bit"); -#elif Q_PROCESSOR_WORDSIZE == 8 - const QString &version = QSysInfo::productVersion(); - if (version.startsWith("7") || version.startsWith("8")) { - return fileName.contains("Win7"); - } else { - return fileName.contains("Win10"); +#if Q_PROCESSOR_WORDSIZE == 8 + const int systemVersion = QSysInfo::productVersion().split(".")[0].toInt(); + static QRegularExpression regex(R"(Windows(\d+))"); + auto match = regex.match(fileName); + if (!match.hasMatch()) { + return std::nullopt; } + int version = match.captured(1).toInt(); + if (version <= systemVersion) { + return version; + } +#endif + return std::nullopt; #else Q_UNUSED(fileName); - return false; + return std::nullopt; #endif +} -#else // If the OS doesn't fit one of the above #defines, then it will never match - Q_UNUSED(fileName); - return false; -#endif +QString ReleaseChannel::findBestDownloadUrl(const QVariantList &assets) +{ + QString bestUrl; + int bestVersion = -1; + for (const auto &rawAsset : assets) { + QVariantMap asset = rawAsset.toMap(); + QString name = asset["name"].toString(); + QString url = asset["browser_download_url"].toString(); + auto version = getTargetVersionForCurrentOS(name); + if (!version) { + continue; + } + if (*version > bestVersion) { + bestVersion = *version; + bestUrl = url; + } + } + if (!bestUrl.isEmpty()) { + qCInfo(ReleaseChannelLog) + << "Selected compatible asset version=" << bestVersion + << "url=" << bestUrl; + } + return bestUrl; } QString StableReleaseChannel::getManualDownloadUrl() const @@ -138,16 +169,9 @@ void StableReleaseChannel::releaseListFinished() lastRelease->setPublishDate(resultMap["published_at"].toDate()); if (resultMap.contains("assets")) { - auto rawAssets = resultMap["assets"].toList(); - for (const auto &rawAsset : rawAssets) { - QVariantMap asset = rawAsset.toMap(); - QString name = asset["name"].toString(); - QString url = asset["browser_download_url"].toString(); - - if (downloadMatchesCurrentOS(name)) { - lastRelease->setDownloadUrl(url); - break; - } + auto url = findBestDownloadUrl(resultMap["assets"].toList()); + if (!url.isEmpty()) { + lastRelease->setDownloadUrl(url); } } @@ -289,21 +313,10 @@ void BetaReleaseChannel::fileListFinished() bool needToUpdate = (QString::compare(shortHash, myHash, Qt::CaseInsensitive) != 0); bool compatibleVersion = false; - QStringList resultUrlList{}; - for (QVariant file : resultList) { - QVariantMap map = file.toMap(); - resultUrlList << map["browser_download_url"].toString(); - } - - resultUrlList.sort(); - // iterate in reverse so the first item is the latest os version - for (auto url = resultUrlList.rbegin(); url < resultUrlList.rend(); ++url) { - if (downloadMatchesCurrentOS(*url)) { - compatibleVersion = true; - lastRelease->setDownloadUrl(*url); - qCInfo(ReleaseChannelLog) << "Found compatible version url=" << *url; - break; - } + QString downloadUrl = findBestDownloadUrl(resultList); + if (!downloadUrl.isEmpty()) { + compatibleVersion = true; + lastRelease->setDownloadUrl(downloadUrl); } emit finishedCheck(needToUpdate, compatibleVersion, lastRelease); diff --git a/cockatrice/src/client/network/update/client/release_channel.h b/cockatrice/src/client/network/update/client/release_channel.h index c56d0cfce..79a23f8cc 100644 --- a/cockatrice/src/client/network/update/client/release_channel.h +++ b/cockatrice/src/client/network/update/client/release_channel.h @@ -7,6 +7,7 @@ #ifndef RELEASECHANNEL_H #define RELEASECHANNEL_H +#include #include #include #include @@ -96,7 +97,8 @@ protected: Release *lastRelease; protected: - static bool downloadMatchesCurrentOS(const QString &fileName); + std::optional getTargetVersionForCurrentOS(const QString &fileName); + QString findBestDownloadUrl(const QVariantList &assets); [[nodiscard]] virtual QString getReleaseChannelUrl() const = 0; public: From 3ad7bae16db442c2eb15a50194b7358bd2325e83 Mon Sep 17 00:00:00 2001 From: tooomm Date: Tue, 7 Jul 2026 23:03:18 +0200 Subject: [PATCH 02/10] combined logic + better regex --- .../network/update/client/release_channel.cpp | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 5f438e26d..42ded9076 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -49,6 +49,8 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f { #if defined(Q_OS_MACOS) const bool isIntel = [] { + // QSysInfo does not go through translation layers + // We need to use sysctl to reliably detect the underlying architecture char arch[255]; size_t len = sizeof(arch); if (sysctlbyname("machdep.cpu.brand_string", arch, &len, nullptr, 0) == 0) { @@ -56,48 +58,32 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f } return false; }(); - const int systemVersion = QSysInfo::productVersion().split(".")[0].toInt(); - if (isIntel) { - static QRegularExpression regex(R"(macOS(\d+)_Intel)"); - auto match = regex.match(fileName); - if (!match.hasMatch()) { - return std::nullopt; - } - int version = match.captured(1).toInt(); - if (version <= systemVersion) { - return version; - } - return std::nullopt; - } - static QRegularExpression regex(R"(macOS(\d+)(?!_Intel))"); - auto match = regex.match(fileName); - if (!match.hasMatch()) { - return std::nullopt; - } - int version = match.captured(1).toInt(); - if (version <= systemVersion) { - return version; - } - return std::nullopt; + static const QRegularExpression macIntelRegex(R"(macOS(\d+)_Intel\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + static const QRegularExpression macArmRegex(R"(macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + const QRegularExpression ®ex = isIntel ? macIntelRegex : macArmRegex; #elif defined(Q_OS_WIN) -#if Q_PROCESSOR_WORDSIZE == 8 - const int systemVersion = QSysInfo::productVersion().split(".")[0].toInt(); - static QRegularExpression regex(R"(Windows(\d+))"); - auto match = regex.match(fileName); - if (!match.hasMatch()) { - return std::nullopt; - } - int version = match.captured(1).toInt(); - if (version <= systemVersion) { - return version; - } -#endif - return std::nullopt; -#else +#if Q_PROCESSOR_WORDSIZE != 8 // non 64-bit host Q_UNUSED(fileName); return std::nullopt; #endif + static const QRegularExpression regex(R"(Win(?:dows)?(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + +#else // if the OS doesn't fit one of the above #defines, then it will never match + Q_UNUSED(fileName); + return std::nullopt; +#endif + + const int systemVersion = QSysInfo::productVersion().split('.').first().toInt(); + auto match = regex.match(fileName); + if (!match.hasMatch()) { + return std::nullopt; + } + const int targetVersion = match.captured(1).toInt(); + if (targetVersion > systemVersion) { + return std::nullopt; + } + return targetVersion; } QString ReleaseChannel::findBestDownloadUrl(const QVariantList &assets) @@ -119,8 +105,7 @@ QString ReleaseChannel::findBestDownloadUrl(const QVariantList &assets) } if (!bestUrl.isEmpty()) { qCInfo(ReleaseChannelLog) - << "Selected compatible asset version=" << bestVersion - << "url=" << bestUrl; + << "Best compatible asset=" << bestUrl; } return bestUrl; } From 1675e318cc068b5567adde03dbf88e17651c47d2 Mon Sep 17 00:00:00 2001 From: tooomm Date: Wed, 8 Jul 2026 15:54:08 +0200 Subject: [PATCH 03/10] Simplify, we only support 64bit, installer prevents wrong installs --- .../src/client/network/update/client/release_channel.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 42ded9076..5ed276765 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -63,10 +63,6 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f const QRegularExpression ®ex = isIntel ? macIntelRegex : macArmRegex; #elif defined(Q_OS_WIN) -#if Q_PROCESSOR_WORDSIZE != 8 // non 64-bit host - Q_UNUSED(fileName); - return std::nullopt; -#endif static const QRegularExpression regex(R"(Win(?:dows)?(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); #else // if the OS doesn't fit one of the above #defines, then it will never match From 42d001ee6ada86d9cf93c6c42f0e8b142c938d4c Mon Sep 17 00:00:00 2001 From: tooomm Date: Wed, 8 Jul 2026 16:21:06 +0200 Subject: [PATCH 04/10] lint + fix Linux versions (missing declaration) --- .../network/update/client/release_channel.cpp | 19 +++++++++++-------- .../network/update/client/release_channel.h | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 5ed276765..beba05c7d 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -2,7 +2,6 @@ #include "version_string.h" -#include #include #include #include @@ -11,6 +10,7 @@ #include #include #include +#include #if defined(Q_OS_MACOS) #include @@ -47,6 +47,8 @@ void ReleaseChannel::checkForUpdates() // Different release channel checking functions for different operating systems std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName) { + const QRegularExpression *regex = nullptr; + #if defined(Q_OS_MACOS) const bool isIntel = [] { // QSysInfo does not go through translation layers @@ -58,12 +60,14 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f } return false; }(); - static const QRegularExpression macIntelRegex(R"(macOS(\d+)_Intel\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + static const QRegularExpression macIntelRegex(R"(macOS(\d+)_Intel\.[^.]+$)", + QRegularExpression::CaseInsensitiveOption); static const QRegularExpression macArmRegex(R"(macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); - const QRegularExpression ®ex = isIntel ? macIntelRegex : macArmRegex; + regex = isIntel ? &macIntelRegex : &macArmRegex; #elif defined(Q_OS_WIN) - static const QRegularExpression regex(R"(Win(?:dows)?(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + static const QRegularExpression winRegex(R"(Win(?:dows)?(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + regex = &winRegex; #else // if the OS doesn't fit one of the above #defines, then it will never match Q_UNUSED(fileName); @@ -71,7 +75,7 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f #endif const int systemVersion = QSysInfo::productVersion().split('.').first().toInt(); - auto match = regex.match(fileName); + auto match = regex->match(fileName); if (!match.hasMatch()) { return std::nullopt; } @@ -100,8 +104,7 @@ QString ReleaseChannel::findBestDownloadUrl(const QVariantList &assets) } } if (!bestUrl.isEmpty()) { - qCInfo(ReleaseChannelLog) - << "Best compatible asset=" << bestUrl; + qCInfo(ReleaseChannelLog) << "Best compatible asset=" << bestUrl; } return bestUrl; } @@ -152,7 +155,7 @@ void StableReleaseChannel::releaseListFinished() if (resultMap.contains("assets")) { auto url = findBestDownloadUrl(resultMap["assets"].toList()); if (!url.isEmpty()) { - lastRelease->setDownloadUrl(url); + lastRelease->setDownloadUrl(url); } } diff --git a/cockatrice/src/client/network/update/client/release_channel.h b/cockatrice/src/client/network/update/client/release_channel.h index 79a23f8cc..c7a4ebc48 100644 --- a/cockatrice/src/client/network/update/client/release_channel.h +++ b/cockatrice/src/client/network/update/client/release_channel.h @@ -7,12 +7,12 @@ #ifndef RELEASECHANNEL_H #define RELEASECHANNEL_H -#include #include #include #include #include #include +#include #include inline Q_LOGGING_CATEGORY(ReleaseChannelLog, "release_channel"); From 8c126cdf7e5cda1937017b2ea3545d09d7978fbc Mon Sep 17 00:00:00 2001 From: tooomm Date: Fri, 17 Jul 2026 17:08:26 +0200 Subject: [PATCH 05/10] update regex --- .../src/client/network/update/client/release_channel.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index beba05c7d..af802a3d2 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -60,13 +60,14 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f } return false; }(); - static const QRegularExpression macIntelRegex(R"(macOS(\d+)_Intel\.[^.]+$)", + static const QRegularExpression macIntelRegex(R"(-macOS(\d+)_Intel\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); - static const QRegularExpression macArmRegex(R"(macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + static const QRegularExpression macArmRegex(R"(-macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); regex = isIntel ? &macIntelRegex : &macArmRegex; #elif defined(Q_OS_WIN) - static const QRegularExpression winRegex(R"(Win(?:dows)?(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + static const QRegularExpression winRegex(R"(-(?:Win|Windows)(\d+)\.[^.]+$)", + QRegularExpression::CaseInsensitiveOption); regex = &winRegex; #else // if the OS doesn't fit one of the above #defines, then it will never match From 66d81e6f14364e854b808045d9cdd81009dd4c07 Mon Sep 17 00:00:00 2001 From: tooomm Date: Fri, 17 Jul 2026 18:08:01 +0200 Subject: [PATCH 06/10] improve/add comments --- .../src/client/network/update/client/release_channel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index af802a3d2..35fe85fff 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -44,7 +44,7 @@ void ReleaseChannel::checkForUpdates() connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished); } -// Different release channel checking functions for different operating systems +// Find compatible assets for host platform (Linux is not supported by in-client updater) std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName) { const QRegularExpression *regex = nullptr; @@ -75,6 +75,7 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f return std::nullopt; #endif + // Any asset targeting an OS version smaller or equal to the current host works const int systemVersion = QSysInfo::productVersion().split('.').first().toInt(); auto match = regex->match(fileName); if (!match.hasMatch()) { @@ -87,6 +88,7 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f return targetVersion; } +// Pick newest targeted version (highest number) amongst all compatible ones QString ReleaseChannel::findBestDownloadUrl(const QVariantList &assets) { QString bestUrl; From cc9cf9ba63bd491ff6a195c63070d53bbe0da044 Mon Sep 17 00:00:00 2001 From: tooomm Date: Fri, 24 Jul 2026 00:26:52 +0200 Subject: [PATCH 07/10] comments --- .../network/update/client/release_channel.cpp | 32 +++++++++++++++---- .../network/update/client/release_channel.h | 4 +-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 35fe85fff..b760c021c 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -44,13 +44,28 @@ void ReleaseChannel::checkForUpdates() connect(response, &QNetworkReply::finished, this, &ReleaseChannel::releaseListFinished); } -// Find compatible assets for host platform (Linux is not supported by in-client updater) +// Find assets compatible with host platform (Linux is not supported by in-client updater) std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName) { const QRegularExpression *regex = nullptr; + static const std::optional systemVersion = [] { + bool ok = false; + const QString versionString = QSysInfo::productVersion(); + const int version = versionString.split('.').first().toInt(&ok); + if (!ok) { + qCWarning(ReleaseChannelLog) << "Unable to determine OS version from" << versionString; + return std::optional(); + } + return std::optional{version}; + }(); + + if (!systemVersion) { + return std::nullopt; + } + #if defined(Q_OS_MACOS) - const bool isIntel = [] { + static const bool isIntel = [] { // QSysInfo does not go through translation layers // We need to use sysctl to reliably detect the underlying architecture char arch[255]; @@ -60,12 +75,16 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f } return false; }(); + + // Apple (ARM) --> Cockatrice-3.0.0-macOS15.dmg + // Apple (x86) --> Cockatrice-3.0.0-macOS15_Intel.dmg static const QRegularExpression macIntelRegex(R"(-macOS(\d+)_Intel\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); - static const QRegularExpression macArmRegex(R"(-macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); - regex = isIntel ? &macIntelRegex : &macArmRegex; + static const QRegularExpression macRegex(R"(-macOS(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); + regex = isIntel ? &macIntelRegex : &macRegex; #elif defined(Q_OS_WIN) + // Windows (x86) --> Cockatrice-3.0.0-Windows10.exe static const QRegularExpression winRegex(R"(-(?:Win|Windows)(\d+)\.[^.]+$)", QRegularExpression::CaseInsensitiveOption); regex = &winRegex; @@ -75,14 +94,13 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f return std::nullopt; #endif - // Any asset targeting an OS version smaller or equal to the current host works - const int systemVersion = QSysInfo::productVersion().split('.').first().toInt(); + // Any asset targeting an OS version up to the current host version works auto match = regex->match(fileName); if (!match.hasMatch()) { return std::nullopt; } const int targetVersion = match.captured(1).toInt(); - if (targetVersion > systemVersion) { + if (targetVersion > *systemVersion) { return std::nullopt; } return targetVersion; diff --git a/cockatrice/src/client/network/update/client/release_channel.h b/cockatrice/src/client/network/update/client/release_channel.h index c7a4ebc48..e038cb2f4 100644 --- a/cockatrice/src/client/network/update/client/release_channel.h +++ b/cockatrice/src/client/network/update/client/release_channel.h @@ -97,8 +97,8 @@ protected: Release *lastRelease; protected: - std::optional getTargetVersionForCurrentOS(const QString &fileName); - QString findBestDownloadUrl(const QVariantList &assets); + static std::optional getTargetVersionForCurrentOS(const QString &fileName); + static QString findBestDownloadUrl(const QVariantList &assets); [[nodiscard]] virtual QString getReleaseChannelUrl() const = 0; public: From fd8ed265558c82e6c24762b0453de8b0962665ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Mon, 21 Sep 2026 11:27:47 +0200 Subject: [PATCH 08/10] ReleaseChannel: parse non-numeric OS versions on Windows Server QSysInfo::productVersion() returns "Server 2022" on Windows Server, so splitting on '.' and calling toInt() made the version unparseable and disabled the in-client updater there. Extract the first numeric token instead; Server 2022 becomes 2022 and matches the Win10 assets. --- .../network/update/client/release_channel.cpp | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index b760c021c..22fd0e0d4 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -27,6 +27,27 @@ #define GIT_SHORT_HASH_LEN 7 +namespace +{ + +// QSysInfo::productVersion() is not guaranteed to start with a number, e.g. it returns +// "Server 2022" on Windows Server. Use the first numeric token as the major host version. +std::optional getProductVersionMajor() +{ + const QString versionString = QSysInfo::productVersion(); + bool ok = false; + static const QRegularExpression firstNumber(R"((\d+))"); + const auto match = firstNumber.match(versionString); + const int version = match.hasMatch() ? match.captured(1).toInt(&ok) : 0; + if (!ok) { + qCWarning(ReleaseChannelLog) << "Unable to determine OS version from" << versionString; + return std::nullopt; + } + return version; +} + +} // namespace + ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr) { } @@ -49,16 +70,7 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f { const QRegularExpression *regex = nullptr; - static const std::optional systemVersion = [] { - bool ok = false; - const QString versionString = QSysInfo::productVersion(); - const int version = versionString.split('.').first().toInt(&ok); - if (!ok) { - qCWarning(ReleaseChannelLog) << "Unable to determine OS version from" << versionString; - return std::optional(); - } - return std::optional{version}; - }(); + static const std::optional systemVersion = getProductVersionMajor(); if (!systemVersion) { return std::nullopt; From 709d145422276f8453a3dab10af12bf8792787cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Mon, 21 Sep 2026 11:30:07 +0200 Subject: [PATCH 09/10] ReleaseChannel: don't offer 64-bit assets on 32-bit Windows The old downloadMatchesCurrentOS() had an explicit 32-bit branch that only accepted "32bit" assets. Restore that guard: 32-bit Windows builds return no match instead of being offered the 64-bit Win10 installer that won't run. --- .../src/client/network/update/client/release_channel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 22fd0e0d4..3087c298a 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -68,6 +68,12 @@ void ReleaseChannel::checkForUpdates() // Find assets compatible with host platform (Linux is not supported by in-client updater) std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &fileName) { +#if defined(Q_OS_WIN) && Q_PROCESSOR_WORDSIZE == 4 + // The published Windows assets are 64-bit only + Q_UNUSED(fileName); + return std::nullopt; +#endif + const QRegularExpression *regex = nullptr; static const std::optional systemVersion = getProductVersionMajor(); From 45662ac4e2a79bece6ef26b12eaad548b192eed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Mon, 21 Sep 2026 11:34:52 +0200 Subject: [PATCH 10/10] ReleaseChannel: only query the OS version on updater platforms The in-client updater only ships assets for macOS and Windows, but the host system version was still being resolved on every platform, including Linux where it is never used. Compute it only under the Q_OS_MACOS / Q_OS_WIN guards. --- .../client/network/update/client/release_channel.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 3087c298a..f401dfd00 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -30,6 +30,7 @@ namespace { +#if defined(Q_OS_MACOS) || defined(Q_OS_WIN) // QSysInfo::productVersion() is not guaranteed to start with a number, e.g. it returns // "Server 2022" on Windows Server. Use the first numeric token as the major host version. std::optional getProductVersionMajor() @@ -45,6 +46,7 @@ std::optional getProductVersionMajor() } return version; } +#endif } // namespace @@ -76,7 +78,13 @@ std::optional ReleaseChannel::getTargetVersionForCurrentOS(const QString &f const QRegularExpression *regex = nullptr; - static const std::optional systemVersion = getProductVersionMajor(); + // Only platforms shipping updater assets (macOS, Windows) need the host version + static const std::optional systemVersion = +#if defined(Q_OS_MACOS) || defined(Q_OS_WIN) + getProductVersionMajor(); +#else + std::nullopt; +#endif if (!systemVersion) { return std::nullopt;