This commit is contained in:
BruebachL 2026-11-04 04:17:57 +01:00 • committed by GitHub
commit dfded56931
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 179 additions and 0 deletions

View file

@ -8,6 +8,7 @@ set(UTILITY_SOURCES
libcockatrice/utility/passwordhasher.cpp libcockatrice/utility/passwordhasher.cpp
libcockatrice/utility/report_categories.cpp libcockatrice/utility/report_categories.cpp
libcockatrice/utility/server_rate_limiter.cpp libcockatrice/utility/server_rate_limiter.cpp
libcockatrice/utility/system_memory_querier.cpp
libcockatrice/utility/translation_loader.cpp libcockatrice/utility/translation_loader.cpp
libcockatrice/utility/user_data_backup.cpp libcockatrice/utility/user_data_backup.cpp
libcockatrice/utility/warning_categories.cpp libcockatrice/utility/warning_categories.cpp
@ -34,6 +35,7 @@ set(UTILITY_HEADERS
libcockatrice/utility/zone_names.h libcockatrice/utility/zone_names.h
libcockatrice/utility/days_years_between.h libcockatrice/utility/days_years_between.h
libcockatrice/utility/server_rate_limiter.h libcockatrice/utility/server_rate_limiter.h
libcockatrice/utility/system_memory_querier.h
libcockatrice/utility/translation_loader.h libcockatrice/utility/translation_loader.h
libcockatrice/utility/user_data_backup.h libcockatrice/utility/user_data_backup.h
libcockatrice/utility/warning_categories.h libcockatrice/utility/warning_categories.h

View file

@ -0,0 +1,124 @@
#include "system_memory_querier.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#ifdef Q_OS_LINUX
#include <QFile>
#include <QRegularExpression>
#include <QTextStream>
#endif
#ifdef Q_OS_MACOS
#include <mach/mach.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#endif
qulonglong SystemMemoryQuerier::totalMemoryBytes()
{
#if defined(Q_OS_WIN)
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (GlobalMemoryStatusEx(&statex)) {
return statex.ullTotalPhys;
}
return 0;
#elif defined(Q_OS_LINUX)
QFile file("/proc/meminfo");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return 0;
}
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
if (line.startsWith("MemTotal:")) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QStringList parts = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
#else
QStringList parts = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
#endif
if (parts.size() >= 2) {
return parts[1].toULongLong() * 1024; // kB → bytes
}
}
}
return 0;
#elif defined(Q_OS_MACOS)
int mib[2] = {CTL_HW, HW_MEMSIZE};
qulonglong memsize = 0;
size_t len = sizeof(memsize);
if (sysctl(mib, 2, &memsize, &len, nullptr, 0) == 0) {
return memsize;
}
return 0;
#else
return 0;
#endif
}
qulonglong SystemMemoryQuerier::availableMemoryBytes()
{
#if defined(Q_OS_WIN)
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (GlobalMemoryStatusEx(&statex)) {
return statex.ullAvailPhys;
}
return 0;
#elif defined(Q_OS_LINUX)
QFile file("/proc/meminfo");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return 0;
}
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
if (line.startsWith("MemAvailable:")) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QStringList parts = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
#else
QStringList parts = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
#endif
if (parts.size() >= 2) {
return parts[1].toULongLong() * 1024;
}
}
}
return 0;
#elif defined(Q_OS_MACOS)
vm_size_t pageSize;
host_page_size(mach_host_self(), &pageSize);
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
vm_statistics64_data_t vmstat;
if (host_statistics64(mach_host_self(), HOST_VM_INFO, (host_info64_t)&vmstat, &count) != KERN_SUCCESS) {
return 0;
}
qulonglong freeBytes = (qulonglong)vmstat.free_count * (qulonglong)pageSize;
return freeBytes;
#else
return 0;
#endif
}

View file

@ -0,0 +1,25 @@
#ifndef COCKATRICE_SYSTEM_MEMORY_QUERIER_H
#define COCKATRICE_SYSTEM_MEMORY_QUERIER_H
#include <QtGlobal>
class SystemMemoryQuerier
{
public:
static qulonglong totalMemoryBytes();
static qulonglong availableMemoryBytes();
static bool hasAtLeastGiB(int gib)
{
const qulonglong total = totalMemoryBytes();
// 0 means the amount could not be determined (unsupported platform or failed query).
// Treat it as unknown and skip the check instead of misreading it as low memory.
if (total == 0) {
return true;
}
const qulonglong GiB = 1024ull * 1024ull * 1024ull;
return total >= (qulonglong)gib * GiB;
}
};
#endif // COCKATRICE_SYSTEM_MEMORY_QUERIER_H

View file

@ -1,6 +1,7 @@
#include "pages.h" #include "pages.h"
#include "client/settings/cache_settings.h" #include "client/settings/cache_settings.h"
#include "libcockatrice/utility/system_memory_querier.h"
#include "main.h" #include "main.h"
#include "oracleimporter.h" #include "oracleimporter.h"
#include "oraclewizard.h" #include "oraclewizard.h"
@ -70,6 +71,7 @@ class QWidget;
#define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/" #define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/"
#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json" #define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json"
#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json" #define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json"
#define MTGXML_URL "https://github.com/ebbit1q/mtgxml/releases/latest/download/mtg.xml.xz"
#ifdef HAS_LZMA #ifdef HAS_LZMA
#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz" #define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz"
@ -317,6 +319,32 @@ void LoadSetsPage::initializePage()
{ {
urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString()); urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString());
// Memory check because Oracle parsing fails on systems with less than 4GiB for MTGJsons allPrintings.json
// The fallback URL is xz-compressed, so only offer it in builds that can read it.
// During background (automatic) updates there is no user to answer, so switch silently.
#ifdef HAS_LZMA
if (!SystemMemoryQuerier::hasAtLeastGiB(4) && urlLineEdit->text() == ALLSETS_URL) {
if (wizard()->backgroundMode) {
urlLineEdit->setText(MTGXML_URL);
} else {
// Ask user whether to switch URL
QMessageBox msgBox(
QMessageBox::Question, tr("Low Memory Detected"),
tr("Your system has less than 4 GiB of memory.\n"
"Using the default AllPrintings URL may cause high memory usage and is known to fail as a "
"result.\n\n"
"Would you like to switch to the direct-download pre-parsed URL instead? (Updated daily)"),
QMessageBox::Yes | QMessageBox::No, this);
msgBox.setDefaultButton(QMessageBox::Yes);
if (msgBox.exec() == QMessageBox::Yes) {
urlLineEdit->setText(MTGXML_URL);
}
}
}
#endif
progressLabel->hide(); progressLabel->hide();
progressBar->hide(); progressBar->hide();