From f95856d06285a65caf0da7377f42f45c33d9603f Mon Sep 17 00:00:00 2001 From: timoreo Date: Thu, 30 Jun 2022 18:07:08 +0200 Subject: [PATCH 001/755] Add the base of java downloading Signed-off-by: timoreo --- launcher/CMakeLists.txt | 2 + launcher/JavaDownloader.cpp | 165 ++++++++++++++++++++++++++++++++++++ launcher/JavaDownloader.h | 8 ++ 3 files changed, 175 insertions(+) create mode 100644 launcher/JavaDownloader.cpp create mode 100644 launcher/JavaDownloader.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 79ac49c76..4e5508579 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -881,6 +881,8 @@ SET(LAUNCHER_SOURCES ui/instanceview/InstanceDelegate.h ui/instanceview/VisualGroup.cpp ui/instanceview/VisualGroup.h + JavaDownloader.cpp + JavaDownloader.h ) if(WIN32) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp new file mode 100644 index 000000000..43da2df01 --- /dev/null +++ b/launcher/JavaDownloader.cpp @@ -0,0 +1,165 @@ +#include "JavaDownloader.h" +#include "net/NetJob.h" +#include "Application.h" +#include "FileSystem.h" +#include "quazip.h" +#include "MMCZip.h" +#include "net/ChecksumValidator.h" + +//Quick & dirty struct to store files +struct File{ + QString path; + QString url; + QByteArray hash; +}; + +void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) { + //Query the adoptium API to get a good version + auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); + auto response = new QByteArray(); + netJob->addNetAction(Net::Download::makeByteArray(QUrl("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); + QObject::connect(netJob, &NetJob::finished,[netJob, response]{ + netJob->deleteLater(); + delete response; + }); + QObject::connect(netJob, &NetJob::succeeded,[response, &OS, isLegacy] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return; + } + auto versionArray = doc.object()[OS].toObject()[isLegacy ? "jre-legacy" : "java-runtime-gamma"].toArray(); + if(!versionArray.empty()){ + auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); + auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); + auto files = new QByteArray(); + + download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); + + QObject::connect(download, &NetJob::finished,[download, files]{ + download->deleteLater(); + delete files; + }); + QObject::connect(download, &NetJob::succeeded,[files, isLegacy] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *files; + return; + } + + //valid json doc, begin making jre spot + auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java/") + (isLegacy ? "java-legacy" : "java-current")); + FS::ensureFolderPathExists(output); + std::vector toDownload; + auto list = doc.object()["files"].toObject(); + for(auto element : list){ + auto obj = element.toObject(); + for(const auto& paths : obj.keys()){ + auto file = FS::PathCombine(output,paths); + + auto type = obj[paths].toObject()["type"].toString(); + if(type == "directory"){ + FS::ensureFolderPathExists(file); + }else if(type == "link"){ + //this is linux only ! + auto target = FS::PathCombine(file,"../"+obj[paths].toObject()["target"].toString()); + QFile(target).link(file); + }else if(type == "file"){ + //TODO download compressed version if it exists ? + auto raw = obj[paths].toObject()["downloads"].toObject()["raw"].toObject(); + auto f = File{file,raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1())}; + toDownload.push_back(f); + } + } + } + auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); + for(const auto& file : toDownload){ + auto dl = Net::Download::makeFile(file.url,file.path); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); + elementDownload->addNetAction(dl); + } + QObject::connect(elementDownload, &NetJob::finished,[elementDownload]{ + elementDownload->deleteLater(); + }); + elementDownload->start(); + }); + download->start(); + }else{ + //mojang does not have a JRE for us, let's get azul zulu + QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); + QString azulOS; + QString arch; + QString bitness; + + if(OS == "mac-os-arm64"){ + //macos arm64 + azulOS = "macos"; + arch = "arm"; + bitness = "64"; + }else if(OS == "linux-aarch64"){ + //linux aarch64 + azulOS = "linux"; + arch = "arm"; + bitness = "64"; + } + auto metaResponse = new QByteArray(); + auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); + downloadJob->addNetAction(Net::Download::makeByteArray(QString( + "https://api.azul.com/zulu/download/community/v1.0/bundles/?" + "java_version=%1" + "&os=%2" + "&arch=%3" + "&hw_bitness=%4" + "&ext=zip" //as a zip for all os, even linux + "&bundle_type=jre" //jre only + "&latest=true" //only get the one latest entry + ).arg(javaVersion,azulOS,arch,bitness), metaResponse)); + QObject::connect(downloadJob, &NetJob::finished,[downloadJob, metaResponse]{ + downloadJob->deleteLater(); + delete metaResponse; + }); + + QObject::connect(downloadJob, &NetJob::succeeded,[metaResponse, isLegacy] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *metaResponse; + return; + } + auto array = doc.array(); + if(!array.empty()){ + //JRE found ! download the zip + auto downloadURL = QUrl(array[0].toObject()["url"].toString()); + auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); + const QString path = downloadURL.host() + '/' + downloadURL.path(); + auto entry = APPLICATION->metacache()->resolveEntry("general", path); + entry->setStale(true); + download->addNetAction(Net::Download::makeCached(downloadURL,entry)); + auto zippath = entry->getFullPath(); + QObject::connect(download, &NetJob::finished,[download]{ + download->deleteLater(); + + }); + QObject::connect(download, &NetJob::succeeded,[isLegacy, zippath]{ + auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"),isLegacy ? "java-legacy" : "java-current"); + //This should do all of the extracting and creating folders + MMCZip::extractDir(zippath, output); + }); + }else{ + qWarning() << "No suitable JRE found !!"; + } + }); + } + }); + + netJob->start(); + +} \ No newline at end of file diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h new file mode 100644 index 000000000..cc073b54c --- /dev/null +++ b/launcher/JavaDownloader.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace JavaDownloader { + /*Downloads the java to the runtimes folder*/ + void downloadJava(bool isLegacy, const QString& OS); +} From 3433c102b70c1c74c882dd2fa19242725676ffe5 Mon Sep 17 00:00:00 2001 From: timoreo Date: Thu, 30 Jun 2022 18:13:52 +0200 Subject: [PATCH 002/755] Remove old comment and change to piston-meta Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 43da2df01..f1a24a68c 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -14,10 +14,9 @@ struct File{ }; void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) { - //Query the adoptium API to get a good version auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); auto response = new QByteArray(); - netJob->addNetAction(Net::Download::makeByteArray(QUrl("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); + netJob->addNetAction(Net::Download::makeByteArray(QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); QObject::connect(netJob, &NetJob::finished,[netJob, response]{ netJob->deleteLater(); delete response; From 9a4a92de4f90334a9dcf1b7b7712f628b232d480 Mon Sep 17 00:00:00 2001 From: timoreo Date: Fri, 1 Jul 2022 08:52:56 +0200 Subject: [PATCH 003/755] Clang format Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 124 +++++++++++++++++------------------- launcher/JavaDownloader.h | 6 +- 2 files changed, 63 insertions(+), 67 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index f1a24a68c..5c3d04a6d 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -1,158 +1,155 @@ #include "JavaDownloader.h" -#include "net/NetJob.h" #include "Application.h" #include "FileSystem.h" -#include "quazip.h" #include "MMCZip.h" #include "net/ChecksumValidator.h" +#include "net/NetJob.h" +#include "quazip.h" -//Quick & dirty struct to store files -struct File{ +// Quick & dirty struct to store files +struct File { QString path; QString url; QByteArray hash; }; -void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) { +void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) +{ auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); auto response = new QByteArray(); - netJob->addNetAction(Net::Download::makeByteArray(QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); - QObject::connect(netJob, &NetJob::finished,[netJob, response]{ + netJob->addNetAction(Net::Download::makeByteArray( + QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); + QObject::connect(netJob, &NetJob::finished, [netJob, response] { netJob->deleteLater(); delete response; }); - QObject::connect(netJob, &NetJob::succeeded,[response, &OS, isLegacy] { + QObject::connect(netJob, &NetJob::succeeded, [response, &OS, isLegacy] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset - << " reason: " << parse_error.errorString(); + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << *response; return; } auto versionArray = doc.object()[OS].toObject()[isLegacy ? "jre-legacy" : "java-runtime-gamma"].toArray(); - if(!versionArray.empty()){ + if (!versionArray.empty()) { auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); auto files = new QByteArray(); download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); - QObject::connect(download, &NetJob::finished,[download, files]{ + QObject::connect(download, &NetJob::finished, [download, files] { download->deleteLater(); delete files; }); - QObject::connect(download, &NetJob::succeeded,[files, isLegacy] { + QObject::connect(download, &NetJob::succeeded, [files, isLegacy] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset - << " reason: " << parse_error.errorString(); + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << *files; return; } - //valid json doc, begin making jre spot - auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java/") + (isLegacy ? "java-legacy" : "java-current")); + // valid json doc, begin making jre spot + auto output = + FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java/") + (isLegacy ? "java-legacy" : "java-current")); FS::ensureFolderPathExists(output); std::vector toDownload; auto list = doc.object()["files"].toObject(); - for(auto element : list){ + for (auto element : list) { auto obj = element.toObject(); - for(const auto& paths : obj.keys()){ - auto file = FS::PathCombine(output,paths); + for (const auto& paths : obj.keys()) { + auto file = FS::PathCombine(output, paths); auto type = obj[paths].toObject()["type"].toString(); - if(type == "directory"){ + if (type == "directory") { FS::ensureFolderPathExists(file); - }else if(type == "link"){ - //this is linux only ! - auto target = FS::PathCombine(file,"../"+obj[paths].toObject()["target"].toString()); + } else if (type == "link") { + // this is linux only ! + auto target = FS::PathCombine(file, "../" + obj[paths].toObject()["target"].toString()); QFile(target).link(file); - }else if(type == "file"){ - //TODO download compressed version if it exists ? + } else if (type == "file") { + // TODO download compressed version if it exists ? auto raw = obj[paths].toObject()["downloads"].toObject()["raw"].toObject(); - auto f = File{file,raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1())}; + auto f = File{ file, raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1()) }; toDownload.push_back(f); } } } auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); - for(const auto& file : toDownload){ - auto dl = Net::Download::makeFile(file.url,file.path); + for (const auto& file : toDownload) { + auto dl = Net::Download::makeFile(file.url, file.path); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); elementDownload->addNetAction(dl); } - QObject::connect(elementDownload, &NetJob::finished,[elementDownload]{ - elementDownload->deleteLater(); - }); + QObject::connect(elementDownload, &NetJob::finished, [elementDownload] { elementDownload->deleteLater(); }); elementDownload->start(); }); download->start(); - }else{ - //mojang does not have a JRE for us, let's get azul zulu + } else { + // mojang does not have a JRE for us, let's get azul zulu QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); QString azulOS; QString arch; QString bitness; - if(OS == "mac-os-arm64"){ - //macos arm64 + if (OS == "mac-os-arm64") { + // macos arm64 azulOS = "macos"; arch = "arm"; bitness = "64"; - }else if(OS == "linux-aarch64"){ - //linux aarch64 + } else if (OS == "linux-aarch64") { + // linux aarch64 azulOS = "linux"; arch = "arm"; bitness = "64"; } auto metaResponse = new QByteArray(); auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); - downloadJob->addNetAction(Net::Download::makeByteArray(QString( - "https://api.azul.com/zulu/download/community/v1.0/bundles/?" - "java_version=%1" - "&os=%2" - "&arch=%3" - "&hw_bitness=%4" - "&ext=zip" //as a zip for all os, even linux - "&bundle_type=jre" //jre only - "&latest=true" //only get the one latest entry - ).arg(javaVersion,azulOS,arch,bitness), metaResponse)); - QObject::connect(downloadJob, &NetJob::finished,[downloadJob, metaResponse]{ + downloadJob->addNetAction(Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" + "java_version=%1" + "&os=%2" + "&arch=%3" + "&hw_bitness=%4" + "&ext=zip" // as a zip for all os, even linux + "&bundle_type=jre" // jre only + "&latest=true" // only get the one latest entry + ) + .arg(javaVersion, azulOS, arch, bitness), + metaResponse)); + QObject::connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse] { downloadJob->deleteLater(); delete metaResponse; }); - QObject::connect(downloadJob, &NetJob::succeeded,[metaResponse, isLegacy] { + QObject::connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset - << " reason: " << parse_error.errorString(); + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << *metaResponse; return; } auto array = doc.array(); - if(!array.empty()){ - //JRE found ! download the zip + if (!array.empty()) { + // JRE found ! download the zip auto downloadURL = QUrl(array[0].toObject()["url"].toString()); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); const QString path = downloadURL.host() + '/' + downloadURL.path(); auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); - download->addNetAction(Net::Download::makeCached(downloadURL,entry)); + download->addNetAction(Net::Download::makeCached(downloadURL, entry)); auto zippath = entry->getFullPath(); - QObject::connect(download, &NetJob::finished,[download]{ - download->deleteLater(); - - }); - QObject::connect(download, &NetJob::succeeded,[isLegacy, zippath]{ - auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"),isLegacy ? "java-legacy" : "java-current"); - //This should do all of the extracting and creating folders + QObject::connect(download, &NetJob::finished, [download] { download->deleteLater(); }); + QObject::connect(download, &NetJob::succeeded, [isLegacy, zippath] { + auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"), + isLegacy ? "java-legacy" : "java-current"); + // This should do all of the extracting and creating folders MMCZip::extractDir(zippath, output); }); - }else{ + } else { qWarning() << "No suitable JRE found !!"; } }); @@ -160,5 +157,4 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) { }); netJob->start(); - } \ No newline at end of file diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h index cc073b54c..3dd57eebe 100644 --- a/launcher/JavaDownloader.h +++ b/launcher/JavaDownloader.h @@ -3,6 +3,6 @@ #include namespace JavaDownloader { - /*Downloads the java to the runtimes folder*/ - void downloadJava(bool isLegacy, const QString& OS); -} +/*Downloads the java to the runtimes folder*/ +void downloadJava(bool isLegacy, const QString& OS); +} // namespace JavaDownloader From a97387b692456cae3b13b48ea9e5901712334f4c Mon Sep 17 00:00:00 2001 From: timoreo Date: Fri, 1 Jul 2022 09:37:23 +0200 Subject: [PATCH 004/755] Cherry-pick SysInfo from #680 Signed-off-by: timoreo --- launcher/CMakeLists.txt | 2 + launcher/SysInfo.cpp | 155 ++++++++++++++++++++++++++++++++++++++++ launcher/SysInfo.h | 13 ++++ 3 files changed, 170 insertions(+) create mode 100644 launcher/SysInfo.cpp create mode 100644 launcher/SysInfo.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 4e5508579..c4d9cb74f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -580,6 +580,8 @@ SET(LAUNCHER_SOURCES UpdateController.h ApplicationMessage.h ApplicationMessage.cpp + SysInfo.h + SysInfo.cpp # GUI - general utilities DesktopServices.h diff --git a/launcher/SysInfo.cpp b/launcher/SysInfo.cpp new file mode 100644 index 000000000..4abf39301 --- /dev/null +++ b/launcher/SysInfo.cpp @@ -0,0 +1,155 @@ +#include +#include +#include "settings/SettingsObject.h" +#ifdef Q_OS_MACOS +#include +#endif +#include +#include +#include +#include "MessageLevel.h" +#include +#include +#include +#include "java/JavaUtils.h" +#include "FileSystem.h" +#include "Commandline.h" +#include "Application.h" + +#ifdef Q_OS_MACOS +bool rosettaDetect() { + int ret = 0; + size_t size = sizeof(ret); + if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) + { + return false; + } + if(ret == 0) + { + return false; + } + if(ret == 1) + { + return true; + } + return false; +} +#endif + +namespace SysInfo { +QString currentSystem() { +#if defined(Q_OS_LINUX) + return "linux"; +#elif defined(Q_OS_MACOS) + return "osx"; +#elif defined(Q_OS_WINDOWS) + return "windows"; +#elif defined(Q_OS_FREEBSD) + return "freebsd"; +#elif defined(Q_OS_OPENBSD) + return "openbsd"; +#else + return "unknown"; +#endif +} + +QString useQTForArch(){ + auto qtArch = QSysInfo::currentCpuArchitecture(); +#if defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM) + if(rosettaDetect()) + { + return "arm64"; + } + else + { + return "x86_64"; + } +#endif + return qtArch; +} + +QString runCheckerForArch(const SettingsObjectPtr& settingsObj){ + QString checkerJar = FS::PathCombine(APPLICATION->getJarsPath(), "JavaCheck.jar"); + + QStringList args; + + QProcessPtr process = new QProcess(); + args.append({"-jar", checkerJar}); + process->setArguments(args); + process->setProgram(settingsObj->get("JavaPath").toString()); + process->setProcessChannelMode(QProcess::SeparateChannels); + process->setProcessEnvironment(CleanEnviroment()); + qDebug() << "Running java checker: " + settingsObj->get("JavaPath").toString() + args.join(" ");; + + process->start(); + if(!process->waitForFinished(15000)){ + // we've been waiting for 15 seconds! wtf! OR... it already finished. But HOW WOULD THAT HAPPEN? + process->kill(); // die. BUUURNNNN + // fallback to using polymc arch + return useQTForArch(); + } else { + // yay we can use the java arch + QString stdout_javaChecker; + QString stderr_javaChecker; + + // process stdout + QByteArray data = process->readAllStandardOutput(); + QString added = QString::fromLocal8Bit(data); + added.remove('\r'); + stdout_javaChecker += added; + + // process stderr + data = process->readAllStandardError(); + added = QString::fromLocal8Bit(data); + added.remove('\r'); + stderr_javaChecker += added; + + QMap results; + QStringList lines = stdout_javaChecker.split("\n", QString::SkipEmptyParts); + for(QString line : lines) + { + line = line.trimmed(); + // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux + if (line.contains("/bedrock/strata")) { + continue; + } + + auto parts = line.split('=', QString::SkipEmptyParts); + if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) + { + continue; + } + else + { + results.insert(parts[0], parts[1]); + } + } + + if(!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor")) + { + // wtf man why + // fallback to using polymc arch + return useQTForArch(); + } + + return results["os.arch"]; + } +} + +QString currentArch(const SettingsObjectPtr& settingsObj) { + auto realJavaArchitecture = settingsObj->get("JavaRealArchitecture").toString(); + if(realJavaArchitecture == ""){ + //BRO WHY NOW I HAVE TO USE A JAVA CHECKER >:( + qDebug() << "SysInfo: BRO I HAVE TO USE A JAVA CHECKER WHY IS JAVA ARCH BORKED"; + settingsObj->set("JavaRealArchitecture", runCheckerForArch(settingsObj)); + realJavaArchitecture = settingsObj->get("JavaRealArchitecture").toString(); + } + //qDebug() << "SysInfo: realJavaArch = " << realJavaArchitecture; + if(realJavaArchitecture == "aarch64"){ + return "arm64"; + } else { + return realJavaArchitecture; + } +} +} + diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h new file mode 100644 index 000000000..a4e41e674 --- /dev/null +++ b/launcher/SysInfo.h @@ -0,0 +1,13 @@ +#include +#include "settings/SettingsObject.h" +#ifdef Q_OS_MACOS +#include +#endif + +namespace SysInfo { +QString currentSystem(); +QString currentArch(const SettingsObjectPtr& settingsObj); +QString runCheckerForArch(const SettingsObjectPtr& settingsObj); +QString useQTForArch(); +} + From 98a82cd4847160f41e728403efee51ebc4d2b60a Mon Sep 17 00:00:00 2001 From: timoreo Date: Fri, 1 Jul 2022 14:03:45 +0200 Subject: [PATCH 005/755] Fix MMCZip bugs Signed-off-by: timoreo --- launcher/MMCZip.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 9f4e968f7..3b5c44425 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -300,6 +300,11 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su name.remove(0, subdir.size()); auto original_name = name; + // Fix subdirs/files ending with a / getting transformed into absolute paths + if(name.startsWith('/')){ + name = name.mid(1); + } + // Fix weird "folders with a single file get squashed" thing QString path; if(name.contains('/') && !name.endsWith('/')){ @@ -319,6 +324,11 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su absFilePath = directory.absoluteFilePath(path + name); } + //Block potential file traversal issues + if(!absFilePath.startsWith(directory.absolutePath())){ + qWarning() << "Potential file traversal issue, for path " << absFilePath << " with base name as " << directory.absolutePath(); + continue; + } if (!JlCompress::extractFile(zip, "", absFilePath)) { qWarning() << "Failed to extract file" << original_name << "to" << absFilePath; From 89ce80b27960e66efccfaee20b3baf1596a8ba53 Mon Sep 17 00:00:00 2001 From: timoreo Date: Fri, 1 Jul 2022 14:05:14 +0200 Subject: [PATCH 006/755] Fix Java downloader bugs + Add a test button in JavaPage Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 63 ++++++++++++++++----------- launcher/ui/pages/global/JavaPage.cpp | 41 +++++++++++++++++ launcher/ui/pages/global/JavaPage.h | 1 + launcher/ui/pages/global/JavaPage.ui | 11 ++++- 4 files changed, 88 insertions(+), 28 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 5c3d04a6d..3af69f025 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -1,6 +1,7 @@ #include "JavaDownloader.h" #include "Application.h" #include "FileSystem.h" +#include "Json.h" #include "MMCZip.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" @@ -11,6 +12,7 @@ struct File { QString path; QString url; QByteArray hash; + bool isExec; }; void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) @@ -23,7 +25,7 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) netJob->deleteLater(); delete response; }); - QObject::connect(netJob, &NetJob::succeeded, [response, &OS, isLegacy] { + QObject::connect(netJob, &NetJob::succeeded, [response, OS, isLegacy] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -31,7 +33,7 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) qWarning() << *response; return; } - auto versionArray = doc.object()[OS].toObject()[isLegacy ? "jre-legacy" : "java-runtime-gamma"].toArray(); + auto versionArray = Json::ensureArray(Json::ensureObject(doc.object(), OS), isLegacy ? "jre-legacy" : "java-runtime-gamma"); if (!versionArray.empty()) { auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); @@ -53,35 +55,37 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) } // valid json doc, begin making jre spot - auto output = - FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java/") + (isLegacy ? "java-legacy" : "java-current")); + auto output = FS::PathCombine(QString("java"), (isLegacy ? "java-legacy" : "java-current")); FS::ensureFolderPathExists(output); std::vector toDownload; auto list = doc.object()["files"].toObject(); - for (auto element : list) { - auto obj = element.toObject(); - for (const auto& paths : obj.keys()) { - auto file = FS::PathCombine(output, paths); + for (const auto& paths : list.keys()) { + auto file = FS::PathCombine(output, paths); - auto type = obj[paths].toObject()["type"].toString(); - if (type == "directory") { - FS::ensureFolderPathExists(file); - } else if (type == "link") { - // this is linux only ! - auto target = FS::PathCombine(file, "../" + obj[paths].toObject()["target"].toString()); - QFile(target).link(file); - } else if (type == "file") { - // TODO download compressed version if it exists ? - auto raw = obj[paths].toObject()["downloads"].toObject()["raw"].toObject(); - auto f = File{ file, raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1()) }; - toDownload.push_back(f); - } + auto type = list[paths].toObject()["type"].toString(); + if (type == "directory") { + FS::ensureFolderPathExists(file); + } else if (type == "link") { + // this is linux only ! + auto target = FS::PathCombine(file, "../" + list[paths].toObject()["target"].toString()); + QFile(target).link(file); + } else if (type == "file") { + // TODO download compressed version if it exists ? + auto raw = list[paths].toObject()["downloads"].toObject()["raw"].toObject(); + auto isExec = list[paths].toObject()["executable"].toBool(); + auto f = File{ file, raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec }; + toDownload.push_back(f); } } auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); for (const auto& file : toDownload) { auto dl = Net::Download::makeFile(file.url, file.path); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); + if (file.isExec) { + QObject::connect(dl.get(), &Net::Download::succeeded, [file] { + QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); + }); + } elementDownload->addNetAction(dl); } QObject::connect(elementDownload, &NetJob::finished, [elementDownload] { elementDownload->deleteLater(); }); @@ -90,7 +94,7 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) download->start(); } else { // mojang does not have a JRE for us, let's get azul zulu - QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); + QString javaVersion = isLegacy ? QString("8.0") : QString("18.0"); QString azulOS; QString arch; QString bitness; @@ -100,11 +104,16 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) azulOS = "macos"; arch = "arm"; bitness = "64"; - } else if (OS == "linux-aarch64") { - // linux aarch64 + } else if (OS == "linux-arm64") { + // linux arm64 azulOS = "linux"; arch = "arm"; bitness = "64"; + } else if (OS == "linux-arm") { + // linux arm (32) + azulOS = "linux"; + arch = "arm"; + bitness = "32"; } auto metaResponse = new QByteArray(); auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); @@ -143,16 +152,18 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) download->addNetAction(Net::Download::makeCached(downloadURL, entry)); auto zippath = entry->getFullPath(); QObject::connect(download, &NetJob::finished, [download] { download->deleteLater(); }); - QObject::connect(download, &NetJob::succeeded, [isLegacy, zippath] { + QObject::connect(download, &NetJob::succeeded, [isLegacy, zippath, downloadURL] { auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"), isLegacy ? "java-legacy" : "java-current"); // This should do all of the extracting and creating folders - MMCZip::extractDir(zippath, output); + MMCZip::extractDir(zippath, downloadURL.fileName().chopped(4), output); }); + download->start(); } else { qWarning() << "No suitable JRE found !!"; } }); + downloadJob->start(); } }); diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 2cee15bf1..a57a8ee06 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -52,6 +52,9 @@ #include #include "Application.h" #include +#include "SysInfo.h" +#include "JavaDownloader.h" + JavaPage::JavaPage(QWidget *parent) : QWidget(parent), ui(new Ui::JavaPage) { @@ -177,6 +180,44 @@ void JavaPage::on_javaTestBtn_clicked() checker->run(); } +void JavaPage::on_javaDownloadBtn_clicked(){ + QString sys = SysInfo::currentSystem(); + if(sys == "osx"){ + sys = "mac-os"; + } + QString arch = SysInfo::useQTForArch(); + QString version; + if(sys == "windows"){ + if(arch == "x86_64"){ + version = "windows-x64"; + }else if(arch == "i386"){ + version = "windows-x86"; + }else{ + //Unknown, maybe arm, appending arch for downloader + version = "windows-"+arch; + } + }else if(sys == "mac-os"){ + if(arch == "arm64"){ + version = "mac-os-arm64"; + }else{ + version = "mac-os"; + } + }else if(sys == "linux"){ + if(arch == "x86_64"){ + version = "linux"; + }else { + // will work for i386, and arm(64) + version = "linux-" + arch; + } + }else{ + // ? ? ? ? ? unknown os, at least it won't have a java version on mojang or azul, display warning + QMessageBox::warning(this, tr("Unknown OS"), tr("The OS you are running is not supported by Mojang or Azul. Please install Java manually.")); + return; + } + //TODO display a selection for java 8 or 18 + JavaDownloader::downloadJava(false, version); +} + void JavaPage::checkerFinished() { checker.reset(); diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index 64d4098e5..aa06dc0be 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -85,6 +85,7 @@ slots: void on_javaDetectBtn_clicked(); void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); + void on_javaDownloadBtn_clicked(); void checkerFinished(); private: diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 6ccffed4d..c109efbfc 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -6,8 +6,8 @@ 0 0 - 545 - 580 + 559 + 659 @@ -279,6 +279,13 @@ + + + + Download Java + + + From 54ad91c3b79dbd3379e6f0e1c7bc76f149fc6794 Mon Sep 17 00:00:00 2001 From: timoreo Date: Fri, 1 Jul 2022 14:18:34 +0200 Subject: [PATCH 007/755] Anti-scrumped Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 3af69f025..09aeed5de 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -168,4 +168,4 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) }); netJob->start(); -} \ No newline at end of file +} From 53ddba80778e7c232cd778c1eabc0bc36c3f6414 Mon Sep 17 00:00:00 2001 From: timoreo Date: Sun, 10 Jul 2022 19:56:52 +0200 Subject: [PATCH 008/755] Made JavaDownloader a task and added some quick UI Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 51 +++++++++++++++++---------- launcher/JavaDownloader.h | 17 ++++++--- launcher/ui/pages/global/JavaPage.cpp | 28 +++++++++++---- 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 09aeed5de..d72f66f36 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -15,17 +15,21 @@ struct File { bool isExec; }; -void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) +void JavaDownloader::executeTask() { + auto OS = m_OS; + auto isLegacy = m_isLegacy; auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); auto response = new QByteArray(); + setStatus(tr("Querying mojang meta")); netJob->addNetAction(Net::Download::makeByteArray( QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); QObject::connect(netJob, &NetJob::finished, [netJob, response] { netJob->deleteLater(); delete response; }); - QObject::connect(netJob, &NetJob::succeeded, [response, OS, isLegacy] { + QObject::connect(netJob, &NetJob::progress, this, &JavaDownloader::progress); + QObject::connect(netJob, &NetJob::succeeded, [response, OS, isLegacy, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -35,6 +39,7 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) } auto versionArray = Json::ensureArray(Json::ensureObject(doc.object(), OS), isLegacy ? "jre-legacy" : "java-runtime-gamma"); if (!versionArray.empty()) { + setStatus(tr("Downloading java from Mojang")); auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); auto files = new QByteArray(); @@ -45,7 +50,8 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) download->deleteLater(); delete files; }); - QObject::connect(download, &NetJob::succeeded, [files, isLegacy] { + QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); + QObject::connect(download, &NetJob::succeeded, [files, isLegacy, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -89,12 +95,14 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) elementDownload->addNetAction(dl); } QObject::connect(elementDownload, &NetJob::finished, [elementDownload] { elementDownload->deleteLater(); }); + QObject::connect(elementDownload, &NetJob::succeeded, [this]{emitSucceeded();}); elementDownload->start(); }); download->start(); } else { // mojang does not have a JRE for us, let's get azul zulu - QString javaVersion = isLegacy ? QString("8.0") : QString("18.0"); + setStatus(tr("Querying Azul meta")); + QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); QString azulOS; QString arch; QString bitness; @@ -117,23 +125,24 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) } auto metaResponse = new QByteArray(); auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); - downloadJob->addNetAction(Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" - "java_version=%1" - "&os=%2" - "&arch=%3" - "&hw_bitness=%4" - "&ext=zip" // as a zip for all os, even linux - "&bundle_type=jre" // jre only - "&latest=true" // only get the one latest entry - ) - .arg(javaVersion, azulOS, arch, bitness), - metaResponse)); + downloadJob->addNetAction( + Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" + "java_version=%1" + "&os=%2" + "&arch=%3" + "&hw_bitness=%4" + "&ext=zip" // as a zip for all os, even linux NOTE !! Linux ARM is .deb only !! + "&bundle_type=jre" // jre only + "&latest=true" // only get the one latest entry + ) + .arg(javaVersion, azulOS, arch, bitness), + metaResponse)); QObject::connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse] { downloadJob->deleteLater(); delete metaResponse; }); - - QObject::connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy] { + QObject::connect(downloadJob, &NetJob::progress, this, &JavaDownloader::progress); + QObject::connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -144,6 +153,7 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) auto array = doc.array(); if (!array.empty()) { // JRE found ! download the zip + setStatus(tr("Downloading java from Azul")); auto downloadURL = QUrl(array[0].toObject()["url"].toString()); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); const QString path = downloadURL.host() + '/' + downloadURL.path(); @@ -152,15 +162,18 @@ void JavaDownloader::downloadJava(bool isLegacy, const QString& OS) download->addNetAction(Net::Download::makeCached(downloadURL, entry)); auto zippath = entry->getFullPath(); QObject::connect(download, &NetJob::finished, [download] { download->deleteLater(); }); - QObject::connect(download, &NetJob::succeeded, [isLegacy, zippath, downloadURL] { + QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); + QObject::connect(download, &NetJob::succeeded, [isLegacy, zippath, downloadURL, this] { + setStatus(tr("Extracting java")); auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"), isLegacy ? "java-legacy" : "java-current"); // This should do all of the extracting and creating folders MMCZip::extractDir(zippath, downloadURL.fileName().chopped(4), output); + emitSucceeded(); }); download->start(); } else { - qWarning() << "No suitable JRE found !!"; + emitFailed(tr("No suitable JRE found")); } }); downloadJob->start(); diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h index 3dd57eebe..3b7a7c425 100644 --- a/launcher/JavaDownloader.h +++ b/launcher/JavaDownloader.h @@ -1,8 +1,17 @@ #pragma once #include +#include "tasks/Task.h" -namespace JavaDownloader { -/*Downloads the java to the runtimes folder*/ -void downloadJava(bool isLegacy, const QString& OS); -} // namespace JavaDownloader +class JavaDownloader : public Task { + Q_OBJECT + public: + /*Downloads the java to the runtimes folder*/ + explicit JavaDownloader(bool isLegacy, const QString& OS) : m_isLegacy(isLegacy), m_OS(OS) {} + + void executeTask() override; + + private: + bool m_isLegacy; + const QString& m_OS; +}; diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index a57a8ee06..2cc627b7d 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -48,13 +48,14 @@ #include "java/JavaUtils.h" #include "java/JavaInstallList.h" -#include "settings/SettingsObject.h" #include -#include "Application.h" #include -#include "SysInfo.h" +#include "Application.h" #include "JavaDownloader.h" - +#include "SysInfo.h" +#include "settings/SettingsObject.h" +#include "ui/dialogs/ProgressDialog.h" +#include JavaPage::JavaPage(QWidget *parent) : QWidget(parent), ui(new Ui::JavaPage) { @@ -214,8 +215,23 @@ void JavaPage::on_javaDownloadBtn_clicked(){ QMessageBox::warning(this, tr("Unknown OS"), tr("The OS you are running is not supported by Mojang or Azul. Please install Java manually.")); return; } - //TODO display a selection for java 8 or 18 - JavaDownloader::downloadJava(false, version); + //Selection using QMessageBox for java 8 or 17 + QMessageBox box(QMessageBox::Icon::Question, tr("Java version"), tr("Do you want to download Java version 8 or 17?\n Java 8 is recommended for minecraft versions below 1.17\n Java 17 is recommended for minecraft versions above or equal to 1.17"), + QMessageBox::NoButton, this); + box.addButton("Java 17", QMessageBox::YesRole); + auto no = box.addButton("Java 8", QMessageBox::NoRole); + auto cancel = box.addButton(tr("Download both"), QMessageBox::AcceptRole); + box.exec(); + bool isLegacy = box.clickedButton() == no; + + auto down = new JavaDownloader(isLegacy, version); + ProgressDialog dialog(this); + dialog.execWithTask(down); + if(box.clickedButton() == cancel) { + auto dwn = new JavaDownloader(false, version); + ProgressDialog dg(this); + dg.execWithTask(dwn); + } } void JavaPage::checkerFinished() From f946964490c181700cbc0b1b479fd6089f4ba02e Mon Sep 17 00:00:00 2001 From: timoreo Date: Sun, 10 Jul 2022 20:21:52 +0200 Subject: [PATCH 009/755] Regrab a few changes on SysInfo Signed-off-by: timoreo --- launcher/CMakeLists.txt | 2 ++ launcher/SysInfo.cpp | 32 ++++++++++++++++------- launcher/SysInfo.h | 9 +++---- launcher/minecraft/LaunchContext.cpp | 39 ++++++++++++++++++++++++++++ launcher/minecraft/LaunchContext.h | 34 ++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 launcher/minecraft/LaunchContext.cpp create mode 100644 launcher/minecraft/LaunchContext.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index c4d9cb74f..c467957a8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -271,6 +271,8 @@ set(MINECRAFT_SOURCES minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.h + minecraft/LaunchContext.cpp + minecraft/LaunchContext.h minecraft/LaunchProfile.cpp minecraft/LaunchProfile.h minecraft/Component.cpp diff --git a/launcher/SysInfo.cpp b/launcher/SysInfo.cpp index 4abf39301..010eb84c8 100644 --- a/launcher/SysInfo.cpp +++ b/launcher/SysInfo.cpp @@ -1,6 +1,6 @@ #include #include -#include "settings/SettingsObject.h" +#include "minecraft/LaunchContext.h" #ifdef Q_OS_MACOS #include #endif @@ -68,18 +68,24 @@ QString useQTForArch(){ return qtArch; } -QString runCheckerForArch(const SettingsObjectPtr& settingsObj){ - QString checkerJar = FS::PathCombine(APPLICATION->getJarsPath(), "JavaCheck.jar"); +QString runCheckerForArch(LaunchContext launchContext){ + QString checkerJar = JavaUtils::getJavaCheckPath(); + + if (checkerJar.isEmpty()) + { + qDebug() << "Java checker library could not be found. Please check your installation."; + return useQTForArch(); + } QStringList args; QProcessPtr process = new QProcess(); args.append({"-jar", checkerJar}); process->setArguments(args); - process->setProgram(settingsObj->get("JavaPath").toString()); + process->setProgram(launchContext.getJavaPath().toString()); process->setProcessChannelMode(QProcess::SeparateChannels); process->setProcessEnvironment(CleanEnviroment()); - qDebug() << "Running java checker: " + settingsObj->get("JavaPath").toString() + args.join(" ");; + qDebug() << "Running java checker: " + launchContext.getJavaPath().toString() + args.join(" ");; process->start(); if(!process->waitForFinished(15000)){ @@ -105,7 +111,11 @@ QString runCheckerForArch(const SettingsObjectPtr& settingsObj){ stderr_javaChecker += added; QMap results; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList lines = stdout_javaChecker.split("\n", Qt::SkipEmptyParts); +#else QStringList lines = stdout_javaChecker.split("\n", QString::SkipEmptyParts); +#endif for(QString line : lines) { line = line.trimmed(); @@ -114,7 +124,11 @@ QString runCheckerForArch(const SettingsObjectPtr& settingsObj){ continue; } +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto parts = line.split('=', Qt::SkipEmptyParts); +#else auto parts = line.split('=', QString::SkipEmptyParts); +#endif if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) { continue; @@ -136,13 +150,13 @@ QString runCheckerForArch(const SettingsObjectPtr& settingsObj){ } } -QString currentArch(const SettingsObjectPtr& settingsObj) { - auto realJavaArchitecture = settingsObj->get("JavaRealArchitecture").toString(); +QString currentArch(LaunchContext launchContext) { + auto realJavaArchitecture = launchContext.getRealJavaArch().toString(); if(realJavaArchitecture == ""){ //BRO WHY NOW I HAVE TO USE A JAVA CHECKER >:( qDebug() << "SysInfo: BRO I HAVE TO USE A JAVA CHECKER WHY IS JAVA ARCH BORKED"; - settingsObj->set("JavaRealArchitecture", runCheckerForArch(settingsObj)); - realJavaArchitecture = settingsObj->get("JavaRealArchitecture").toString(); + launchContext.setRealJavaArch(runCheckerForArch(launchContext)); + realJavaArchitecture = launchContext.getRealJavaArch().toString(); } //qDebug() << "SysInfo: realJavaArch = " << realJavaArchitecture; if(realJavaArchitecture == "aarch64"){ diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h index a4e41e674..280757674 100644 --- a/launcher/SysInfo.h +++ b/launcher/SysInfo.h @@ -1,13 +1,12 @@ #include -#include "settings/SettingsObject.h" +#include "minecraft/LaunchContext.h" #ifdef Q_OS_MACOS #include #endif namespace SysInfo { QString currentSystem(); -QString currentArch(const SettingsObjectPtr& settingsObj); -QString runCheckerForArch(const SettingsObjectPtr& settingsObj); +QString currentArch(LaunchContext launchContext); +QString runCheckerForArch(LaunchContext launchContext); QString useQTForArch(); -} - +} \ No newline at end of file diff --git a/launcher/minecraft/LaunchContext.cpp b/launcher/minecraft/LaunchContext.cpp new file mode 100644 index 000000000..b445d9ae1 --- /dev/null +++ b/launcher/minecraft/LaunchContext.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Toshit Chawda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "LaunchContext.h" + +LaunchContext::LaunchContext(SettingsObjectPtr instanceSettings){ + m_instanceSettings = instanceSettings; +} + +void LaunchContext::setRealJavaArch(QVariant realJavaArch) +{ + m_instanceSettings->set("JavaRealArchitecture", realJavaArch); +} + +QVariant LaunchContext::getRealJavaArch() +{ + return m_instanceSettings->get("JavaRealArchitecture"); +} + +QVariant LaunchContext::getJavaPath() +{ + return m_instanceSettings->get("JavaPath"); +} \ No newline at end of file diff --git a/launcher/minecraft/LaunchContext.h b/launcher/minecraft/LaunchContext.h new file mode 100644 index 000000000..0d322a2ea --- /dev/null +++ b/launcher/minecraft/LaunchContext.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * PolyMC - Minecraft Launcher + * Copyright (C) 2022 Toshit Chawda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include "settings/SettingsObject.h" + +#pragma once + +class LaunchContext +{ + public: + LaunchContext(SettingsObjectPtr instanceSettings); + void setRealJavaArch(QVariant realJavaArch); + QVariant getRealJavaArch(); + QVariant getJavaPath(); + private: + SettingsObjectPtr m_instanceSettings; +}; \ No newline at end of file From 68446c2a5442d568a75229d63d6addc502237b4a Mon Sep 17 00:00:00 2001 From: timoreo Date: Sun, 10 Jul 2022 20:34:29 +0200 Subject: [PATCH 010/755] Obligatory formatting fix commit Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 2 +- launcher/SysInfo.h | 2 +- launcher/minecraft/LaunchContext.cpp | 2 +- launcher/minecraft/LaunchContext.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index d72f66f36..2cca83a9b 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -95,7 +95,7 @@ void JavaDownloader::executeTask() elementDownload->addNetAction(dl); } QObject::connect(elementDownload, &NetJob::finished, [elementDownload] { elementDownload->deleteLater(); }); - QObject::connect(elementDownload, &NetJob::succeeded, [this]{emitSucceeded();}); + QObject::connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); elementDownload->start(); }); download->start(); diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h index 280757674..9107cacb8 100644 --- a/launcher/SysInfo.h +++ b/launcher/SysInfo.h @@ -9,4 +9,4 @@ QString currentSystem(); QString currentArch(LaunchContext launchContext); QString runCheckerForArch(LaunchContext launchContext); QString useQTForArch(); -} \ No newline at end of file +} diff --git a/launcher/minecraft/LaunchContext.cpp b/launcher/minecraft/LaunchContext.cpp index b445d9ae1..ea6a549f0 100644 --- a/launcher/minecraft/LaunchContext.cpp +++ b/launcher/minecraft/LaunchContext.cpp @@ -36,4 +36,4 @@ QVariant LaunchContext::getRealJavaArch() QVariant LaunchContext::getJavaPath() { return m_instanceSettings->get("JavaPath"); -} \ No newline at end of file +} diff --git a/launcher/minecraft/LaunchContext.h b/launcher/minecraft/LaunchContext.h index 0d322a2ea..2c6e1307f 100644 --- a/launcher/minecraft/LaunchContext.h +++ b/launcher/minecraft/LaunchContext.h @@ -31,4 +31,4 @@ class LaunchContext QVariant getJavaPath(); private: SettingsObjectPtr m_instanceSettings; -}; \ No newline at end of file +}; From 24dc154856cc45b78404b47fa085f8ea0a1adeb0 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 11 Jul 2022 15:11:08 +0200 Subject: [PATCH 011/755] Add download button to more pages + UI fixes Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 79 +++++++++++++++++++ launcher/JavaDownloader.h | 1 + launcher/ui/pages/global/JavaPage.cpp | 57 +------------ .../pages/instance/InstanceSettingsPage.cpp | 6 ++ .../ui/pages/instance/InstanceSettingsPage.h | 1 + .../ui/pages/instance/InstanceSettingsPage.ui | 7 ++ launcher/ui/widgets/JavaSettingsWidget.cpp | 11 +++ launcher/ui/widgets/JavaSettingsWidget.h | 3 + 8 files changed, 111 insertions(+), 54 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 2cca83a9b..cb8fd1116 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -1,11 +1,15 @@ #include "JavaDownloader.h" +#include +#include #include "Application.h" #include "FileSystem.h" #include "Json.h" #include "MMCZip.h" +#include "SysInfo.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" #include "quazip.h" +#include "ui/dialogs/ProgressDialog.h" // Quick & dirty struct to store files struct File { @@ -95,6 +99,7 @@ void JavaDownloader::executeTask() elementDownload->addNetAction(dl); } QObject::connect(elementDownload, &NetJob::finished, [elementDownload] { elementDownload->deleteLater(); }); + QObject::connect(elementDownload, &NetJob::progress, this, &JavaDownloader::progress); QObject::connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); elementDownload->start(); }); @@ -182,3 +187,77 @@ void JavaDownloader::executeTask() netJob->start(); } +void JavaDownloader::showPrompts(QWidget* parent) +{ + QString sys = SysInfo::currentSystem(); + if (sys == "osx") { + sys = "mac-os"; + } + QString arch = SysInfo::useQTForArch(); + QString version; + if (sys == "windows") { + if (arch == "x86_64") { + version = "windows-x64"; + } else if (arch == "i386") { + version = "windows-x86"; + } else { + // Unknown, maybe arm, appending arch for downloader + version = "windows-" + arch; + } + } else if (sys == "mac-os") { + if (arch == "arm64") { + version = "mac-os-arm64"; + } else { + version = "mac-os"; + } + } else if (sys == "linux") { + if (arch == "x86_64") { + version = "linux"; + } else { + // will work for i386, and arm(64) + version = "linux-" + arch; + } + } else { + // ? ? ? ? ? unknown os, at least it won't have a java version on mojang or azul, display warning + QMessageBox::warning(parent, tr("Unknown OS"), + tr("The OS you are running is not supported by Mojang or Azul. Please install Java manually.")); + return; + } + // Selection using QMessageBox for java 8 or 17 + QMessageBox box(QMessageBox::Icon::Question, tr("Java version"), + tr("Do you want to download Java version 8 or 17?\n Java 8 is recommended for minecraft versions below 1.17\n Java 17 " + "is recommended for minecraft versions above or equal to 1.17"), + QMessageBox::NoButton, parent); + auto yes = box.addButton("Java 17", QMessageBox::AcceptRole); + auto no = box.addButton("Java 8", QMessageBox::AcceptRole); + auto both = box.addButton(tr("Download both"), QMessageBox::AcceptRole); + auto cancel = box.addButton(QMessageBox::Cancel); + + if (QFileInfo::exists(FS::PathCombine(QString("java"), "java-legacy"))) { + no->setEnabled(false); + } + if (QFileInfo::exists(FS::PathCombine(QString("java"), "java-current"))) { + yes->setEnabled(false); + } + if (!yes->isEnabled() || !no->isEnabled()) { + both->setEnabled(false); + } + if (!yes->isEnabled() && !no->isEnabled()) { + QMessageBox::warning(parent, tr("Already installed !"), tr("Both versions of java are already installed !")); + return; + } + box.exec(); + if (box.clickedButton() == nullptr || box.clickedButton() == cancel) { + return; + } + bool isLegacy = box.clickedButton() == no; + + auto down = new JavaDownloader(isLegacy, version); + ProgressDialog dialog(parent); + dialog.execWithTask(down); + if (box.clickedButton() == both) { + auto dwn = new JavaDownloader(false, version); + ProgressDialog dg(parent); + dg.execWithTask(dwn); + } +} diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h index 3b7a7c425..274a95e49 100644 --- a/launcher/JavaDownloader.h +++ b/launcher/JavaDownloader.h @@ -10,6 +10,7 @@ class JavaDownloader : public Task { explicit JavaDownloader(bool isLegacy, const QString& OS) : m_isLegacy(isLegacy), m_OS(OS) {} void executeTask() override; + static void showPrompts(QWidget* parent = nullptr); private: bool m_isLegacy; diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 2cc627b7d..d7f48f552 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -52,10 +52,7 @@ #include #include "Application.h" #include "JavaDownloader.h" -#include "SysInfo.h" #include "settings/SettingsObject.h" -#include "ui/dialogs/ProgressDialog.h" -#include JavaPage::JavaPage(QWidget *parent) : QWidget(parent), ui(new Ui::JavaPage) { @@ -181,57 +178,9 @@ void JavaPage::on_javaTestBtn_clicked() checker->run(); } -void JavaPage::on_javaDownloadBtn_clicked(){ - QString sys = SysInfo::currentSystem(); - if(sys == "osx"){ - sys = "mac-os"; - } - QString arch = SysInfo::useQTForArch(); - QString version; - if(sys == "windows"){ - if(arch == "x86_64"){ - version = "windows-x64"; - }else if(arch == "i386"){ - version = "windows-x86"; - }else{ - //Unknown, maybe arm, appending arch for downloader - version = "windows-"+arch; - } - }else if(sys == "mac-os"){ - if(arch == "arm64"){ - version = "mac-os-arm64"; - }else{ - version = "mac-os"; - } - }else if(sys == "linux"){ - if(arch == "x86_64"){ - version = "linux"; - }else { - // will work for i386, and arm(64) - version = "linux-" + arch; - } - }else{ - // ? ? ? ? ? unknown os, at least it won't have a java version on mojang or azul, display warning - QMessageBox::warning(this, tr("Unknown OS"), tr("The OS you are running is not supported by Mojang or Azul. Please install Java manually.")); - return; - } - //Selection using QMessageBox for java 8 or 17 - QMessageBox box(QMessageBox::Icon::Question, tr("Java version"), tr("Do you want to download Java version 8 or 17?\n Java 8 is recommended for minecraft versions below 1.17\n Java 17 is recommended for minecraft versions above or equal to 1.17"), - QMessageBox::NoButton, this); - box.addButton("Java 17", QMessageBox::YesRole); - auto no = box.addButton("Java 8", QMessageBox::NoRole); - auto cancel = box.addButton(tr("Download both"), QMessageBox::AcceptRole); - box.exec(); - bool isLegacy = box.clickedButton() == no; - - auto down = new JavaDownloader(isLegacy, version); - ProgressDialog dialog(this); - dialog.execWithTask(down); - if(box.clickedButton() == cancel) { - auto dwn = new JavaDownloader(false, version); - ProgressDialog dg(this); - dg.execWithTask(dwn); - } +void JavaPage::on_javaDownloadBtn_clicked() +{ + JavaDownloader::showPrompts(this); } void JavaPage::checkerFinished() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 5da7f19f5..62e780ce8 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -49,6 +49,7 @@ #include "JavaCommon.h" #include "Application.h" +#include "JavaDownloader.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "FileSystem.h" @@ -374,6 +375,11 @@ void InstanceSettingsPage::loadSettings() ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); } +void InstanceSettingsPage::on_javaDownloadBtn_clicked() +{ + JavaDownloader::showPrompts(this); +} + void InstanceSettingsPage::on_javaDetectBtn_clicked() { if (JavaUtils::getJavaCheckPath().isEmpty()) { diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 97d1296fe..ffb226781 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -81,6 +81,7 @@ private slots: void on_javaDetectBtn_clicked(); void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); + void on_javaDownloadBtn_clicked(); void applySettings(); void loadSettings(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 8b3c33702..4ce59a0ae 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -95,6 +95,13 @@ + + + + Download Java... + + + diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 314a126e4..e62cdd1f0 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -14,6 +14,7 @@ #include "JavaCommon.h" #include "java/JavaInstall.h" #include "java/JavaUtils.h" +#include "JavaDownloader.h" #include "FileSystem.h" #include "ui/dialogs/CustomMessageBox.h" @@ -38,6 +39,8 @@ JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent) connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaBrowseBtn_clicked); connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited); connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked); + connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaDownloadBtn_clicked); + } void JavaSettingsWidget::setupUi() @@ -115,6 +118,10 @@ void JavaSettingsWidget::setupUi() m_verticalLayout->addWidget(m_memoryGroupBox); + m_javaDownloadBtn = new QPushButton("Download Java",this); + + m_verticalLayout->addWidget(m_javaDownloadBtn); + retranslate(); } @@ -276,7 +283,11 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() m_javaPathTextBox->setText(cooked_path); checkJavaPath(cooked_path); } +void JavaSettingsWidget::on_javaDownloadBtn_clicked() +{ + JavaDownloader::showPrompts(this); +} void JavaSettingsWidget::on_javaStatusBtn_clicked() { QString text; diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 0d280daf3..fe6ee2f58 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -63,6 +63,7 @@ protected slots: void javaVersionSelected(BaseVersionPtr version); void on_javaBrowseBtn_clicked(); void on_javaStatusBtn_clicked(); + void on_javaDownloadBtn_clicked(); void checkFinished(JavaCheckResult result); protected: /* methods */ @@ -88,6 +89,8 @@ private: /* data */ QSpinBox *m_minMemSpinBox = nullptr; QLabel *m_labelPermGen = nullptr; QSpinBox *m_permGenSpinBox = nullptr; + + QPushButton *m_javaDownloadBtn = nullptr; QIcon goodIcon; QIcon yellowIcon; QIcon badIcon; From a902f29ccff347f1c51ab95330748cb6e71d1539 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 12 Sep 2022 08:19:42 +0200 Subject: [PATCH 012/755] Changed to a temporary file Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 21 ++++++++++++++------- launcher/ui/widgets/JavaSettingsWidget.cpp | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index cb8fd1116..e56a87d0c 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -1,6 +1,7 @@ #include "JavaDownloader.h" #include #include +#include #include "Application.h" #include "FileSystem.h" #include "Json.h" @@ -127,6 +128,11 @@ void JavaDownloader::executeTask() azulOS = "linux"; arch = "arm"; bitness = "32"; + } else if (OS == "linux"){ + // linux x86 64 (used for debugging, should never reach here) + azulOS = "linux"; + arch = "x86"; + bitness = "64"; } auto metaResponse = new QByteArray(); auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); @@ -161,19 +167,20 @@ void JavaDownloader::executeTask() setStatus(tr("Downloading java from Azul")); auto downloadURL = QUrl(array[0].toObject()["url"].toString()); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); - const QString path = downloadURL.host() + '/' + downloadURL.path(); - auto entry = APPLICATION->metacache()->resolveEntry("general", path); - entry->setStale(true); - download->addNetAction(Net::Download::makeCached(downloadURL, entry)); - auto zippath = entry->getFullPath(); + auto temp = std::make_unique(FS::PathCombine(APPLICATION->root(), "temp", "XXXXXX.zip")); + FS::ensureFolderPathExists(FS::PathCombine(APPLICATION->root(),"temp")); + // Have to open at least once to generate path + temp->open(); + temp->close(); + download->addNetAction(Net::Download::makeFile(downloadURL, temp->fileName())); QObject::connect(download, &NetJob::finished, [download] { download->deleteLater(); }); QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(download, &NetJob::succeeded, [isLegacy, zippath, downloadURL, this] { + QObject::connect(download, &NetJob::succeeded, [isLegacy, file = std::move(temp), downloadURL, this] { setStatus(tr("Extracting java")); auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"), isLegacy ? "java-legacy" : "java-current"); // This should do all of the extracting and creating folders - MMCZip::extractDir(zippath, downloadURL.fileName().chopped(4), output); + MMCZip::extractDir(file->fileName(), downloadURL.fileName().chopped(4), output); emitSucceeded(); }); download->start(); diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index e62cdd1f0..6494b79fd 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -118,7 +118,7 @@ void JavaSettingsWidget::setupUi() m_verticalLayout->addWidget(m_memoryGroupBox); - m_javaDownloadBtn = new QPushButton("Download Java",this); + m_javaDownloadBtn = new QPushButton(tr("Download Java"), this); m_verticalLayout->addWidget(m_javaDownloadBtn); From dc84a6199932fed417796ec1183598669b4dde2f Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 12 Sep 2022 14:17:41 +0200 Subject: [PATCH 013/755] Added failed and aborted handlers Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 61 +++++++++++++++++++++++++++++++------ launcher/JavaDownloader.h | 3 ++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index e56a87d0c..b0cde7d98 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -24,17 +24,29 @@ void JavaDownloader::executeTask() { auto OS = m_OS; auto isLegacy = m_isLegacy; + auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); auto response = new QByteArray(); setStatus(tr("Querying mojang meta")); netJob->addNetAction(Net::Download::makeByteArray( QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); - QObject::connect(netJob, &NetJob::finished, [netJob, response] { + + QObject::connect(this, &Task::aborted, [isLegacy]{ + QDir(FS::PathCombine("java",(isLegacy ? "java-legacy" : "java-current"))).removeRecursively(); + }); + + QObject::connect(netJob, &NetJob::finished, [netJob, response, this] { + //delete so that it's not called on a deleted job + QObject::disconnect(this, &Task::aborted, netJob, &NetJob::abort); netJob->deleteLater(); delete response; }); QObject::connect(netJob, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(netJob, &NetJob::succeeded, [response, OS, isLegacy, this] { + QObject::connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); + + QObject::connect(this, &Task::aborted, netJob, &NetJob::abort); + + QObject::connect(netJob, &NetJob::succeeded, [response, OS, isLegacy, this, netJob] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -51,12 +63,16 @@ void JavaDownloader::executeTask() download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); - QObject::connect(download, &NetJob::finished, [download, files] { + QObject::connect(download, &NetJob::finished, [download, files, this] { + QObject::disconnect(this, &Task::aborted, download, &NetJob::abort); download->deleteLater(); delete files; }); QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(download, &NetJob::succeeded, [files, isLegacy, this] { + QObject::connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); + QObject::connect(this, &Task::aborted, download, &NetJob::abort); + + QObject::connect(download, &NetJob::succeeded, [download, files, isLegacy, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -99,8 +115,15 @@ void JavaDownloader::executeTask() } elementDownload->addNetAction(dl); } - QObject::connect(elementDownload, &NetJob::finished, [elementDownload] { elementDownload->deleteLater(); }); + QObject::connect(elementDownload, &NetJob::finished, [elementDownload, this] { + QObject::disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); + elementDownload->deleteLater(); + }); QObject::connect(elementDownload, &NetJob::progress, this, &JavaDownloader::progress); + QObject::connect(elementDownload, &NetJob::failed, this, &JavaDownloader::emitFailed); + + + QObject::connect(this, &Task::aborted, elementDownload, &NetJob::abort); QObject::connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); elementDownload->start(); }); @@ -148,12 +171,15 @@ void JavaDownloader::executeTask() ) .arg(javaVersion, azulOS, arch, bitness), metaResponse)); - QObject::connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse] { + QObject::connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse, this] { + QObject::disconnect(this, &Task::aborted, downloadJob, &NetJob::abort); downloadJob->deleteLater(); delete metaResponse; }); + QObject::connect(this, &Task::aborted, downloadJob, &NetJob::abort); + QObject::connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); QObject::connect(downloadJob, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy, this] { + QObject::connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy, this, downloadJob] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -173,8 +199,13 @@ void JavaDownloader::executeTask() temp->open(); temp->close(); download->addNetAction(Net::Download::makeFile(downloadURL, temp->fileName())); - QObject::connect(download, &NetJob::finished, [download] { download->deleteLater(); }); + QObject::connect(download, &NetJob::finished, [download, this] { + QObject::disconnect(this, &Task::aborted, download, &NetJob::abort); + download->deleteLater(); + }); QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); + QObject::connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); + QObject::connect(this, &Task::aborted, download, &NetJob::abort); QObject::connect(download, &NetJob::succeeded, [isLegacy, file = std::move(temp), downloadURL, this] { setStatus(tr("Extracting java")); auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"), @@ -194,6 +225,14 @@ void JavaDownloader::executeTask() netJob->start(); } +void JavaDownloader::abortNetJob(NetJob* elementDownload) +{ + if(elementDownload->isRunning()){ + elementDownload->abort(); + }else{ + emit elementDownload->aborted(); + } +} void JavaDownloader::showPrompts(QWidget* parent) { QString sys = SysInfo::currentSystem(); @@ -261,10 +300,12 @@ void JavaDownloader::showPrompts(QWidget* parent) auto down = new JavaDownloader(isLegacy, version); ProgressDialog dialog(parent); - dialog.execWithTask(down); - if (box.clickedButton() == both) { + dialog.setSkipButton(true, tr("Abort")); + + if (dialog.execWithTask(down) && box.clickedButton() == both) { auto dwn = new JavaDownloader(false, version); ProgressDialog dg(parent); + dg.setSkipButton(true, tr("Abort")); dg.execWithTask(dwn); } } diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h index 274a95e49..00ccdf2bc 100644 --- a/launcher/JavaDownloader.h +++ b/launcher/JavaDownloader.h @@ -1,6 +1,7 @@ #pragma once #include +#include "net/NetJob.h" #include "tasks/Task.h" class JavaDownloader : public Task { @@ -10,9 +11,11 @@ class JavaDownloader : public Task { explicit JavaDownloader(bool isLegacy, const QString& OS) : m_isLegacy(isLegacy), m_OS(OS) {} void executeTask() override; + [[nodiscard]] bool canAbort() const override { return true; } static void showPrompts(QWidget* parent = nullptr); private: bool m_isLegacy; const QString& m_OS; + static void abortNetJob(NetJob* elementDownload); }; From 89f1b60538068ac033f7ca1899b4d310eb43f8c2 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 12 Sep 2022 15:00:31 +0200 Subject: [PATCH 014/755] Delete out non-needed functions from SysInfo Delete LaunchContext Signed-off-by: timoreo --- launcher/CMakeLists.txt | 4 +- launcher/SysInfo.cpp | 103 --------------------------- launcher/SysInfo.h | 6 -- launcher/minecraft/LaunchContext.cpp | 39 ---------- launcher/minecraft/LaunchContext.h | 34 --------- 5 files changed, 1 insertion(+), 185 deletions(-) delete mode 100644 launcher/minecraft/LaunchContext.cpp delete mode 100644 launcher/minecraft/LaunchContext.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index c467957a8..4bb686106 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -271,9 +271,7 @@ set(MINECRAFT_SOURCES minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.h - minecraft/LaunchContext.cpp - minecraft/LaunchContext.h - minecraft/LaunchProfile.cpp + minecraft/LaunchProfile.cpp minecraft/LaunchProfile.h minecraft/Component.cpp minecraft/Component.h diff --git a/launcher/SysInfo.cpp b/launcher/SysInfo.cpp index 010eb84c8..f3ff200fc 100644 --- a/launcher/SysInfo.cpp +++ b/launcher/SysInfo.cpp @@ -1,18 +1,13 @@ #include #include -#include "minecraft/LaunchContext.h" #ifdef Q_OS_MACOS #include #endif -#include #include -#include -#include "MessageLevel.h" #include #include #include #include "java/JavaUtils.h" -#include "FileSystem.h" #include "Commandline.h" #include "Application.h" @@ -67,103 +62,5 @@ QString useQTForArch(){ #endif return qtArch; } - -QString runCheckerForArch(LaunchContext launchContext){ - QString checkerJar = JavaUtils::getJavaCheckPath(); - - if (checkerJar.isEmpty()) - { - qDebug() << "Java checker library could not be found. Please check your installation."; - return useQTForArch(); - } - - QStringList args; - - QProcessPtr process = new QProcess(); - args.append({"-jar", checkerJar}); - process->setArguments(args); - process->setProgram(launchContext.getJavaPath().toString()); - process->setProcessChannelMode(QProcess::SeparateChannels); - process->setProcessEnvironment(CleanEnviroment()); - qDebug() << "Running java checker: " + launchContext.getJavaPath().toString() + args.join(" ");; - - process->start(); - if(!process->waitForFinished(15000)){ - // we've been waiting for 15 seconds! wtf! OR... it already finished. But HOW WOULD THAT HAPPEN? - process->kill(); // die. BUUURNNNN - // fallback to using polymc arch - return useQTForArch(); - } else { - // yay we can use the java arch - QString stdout_javaChecker; - QString stderr_javaChecker; - - // process stdout - QByteArray data = process->readAllStandardOutput(); - QString added = QString::fromLocal8Bit(data); - added.remove('\r'); - stdout_javaChecker += added; - - // process stderr - data = process->readAllStandardError(); - added = QString::fromLocal8Bit(data); - added.remove('\r'); - stderr_javaChecker += added; - - QMap results; -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - QStringList lines = stdout_javaChecker.split("\n", Qt::SkipEmptyParts); -#else - QStringList lines = stdout_javaChecker.split("\n", QString::SkipEmptyParts); -#endif - for(QString line : lines) - { - line = line.trimmed(); - // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux - if (line.contains("/bedrock/strata")) { - continue; - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - auto parts = line.split('=', Qt::SkipEmptyParts); -#else - auto parts = line.split('=', QString::SkipEmptyParts); -#endif - if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) - { - continue; - } - else - { - results.insert(parts[0], parts[1]); - } - } - - if(!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor")) - { - // wtf man why - // fallback to using polymc arch - return useQTForArch(); - } - - return results["os.arch"]; - } -} - -QString currentArch(LaunchContext launchContext) { - auto realJavaArchitecture = launchContext.getRealJavaArch().toString(); - if(realJavaArchitecture == ""){ - //BRO WHY NOW I HAVE TO USE A JAVA CHECKER >:( - qDebug() << "SysInfo: BRO I HAVE TO USE A JAVA CHECKER WHY IS JAVA ARCH BORKED"; - launchContext.setRealJavaArch(runCheckerForArch(launchContext)); - realJavaArchitecture = launchContext.getRealJavaArch().toString(); - } - //qDebug() << "SysInfo: realJavaArch = " << realJavaArchitecture; - if(realJavaArchitecture == "aarch64"){ - return "arm64"; - } else { - return realJavaArchitecture; - } -} } diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h index 9107cacb8..3cb1c8de6 100644 --- a/launcher/SysInfo.h +++ b/launcher/SysInfo.h @@ -1,12 +1,6 @@ #include -#include "minecraft/LaunchContext.h" -#ifdef Q_OS_MACOS -#include -#endif namespace SysInfo { QString currentSystem(); -QString currentArch(LaunchContext launchContext); -QString runCheckerForArch(LaunchContext launchContext); QString useQTForArch(); } diff --git a/launcher/minecraft/LaunchContext.cpp b/launcher/minecraft/LaunchContext.cpp deleted file mode 100644 index ea6a549f0..000000000 --- a/launcher/minecraft/LaunchContext.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Toshit Chawda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include "LaunchContext.h" - -LaunchContext::LaunchContext(SettingsObjectPtr instanceSettings){ - m_instanceSettings = instanceSettings; -} - -void LaunchContext::setRealJavaArch(QVariant realJavaArch) -{ - m_instanceSettings->set("JavaRealArchitecture", realJavaArch); -} - -QVariant LaunchContext::getRealJavaArch() -{ - return m_instanceSettings->get("JavaRealArchitecture"); -} - -QVariant LaunchContext::getJavaPath() -{ - return m_instanceSettings->get("JavaPath"); -} diff --git a/launcher/minecraft/LaunchContext.h b/launcher/minecraft/LaunchContext.h deleted file mode 100644 index 2c6e1307f..000000000 --- a/launcher/minecraft/LaunchContext.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * PolyMC - Minecraft Launcher - * Copyright (C) 2022 Toshit Chawda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include -#include "settings/SettingsObject.h" - -#pragma once - -class LaunchContext -{ - public: - LaunchContext(SettingsObjectPtr instanceSettings); - void setRealJavaArch(QVariant realJavaArch); - QVariant getRealJavaArch(); - QVariant getJavaPath(); - private: - SettingsObjectPtr m_instanceSettings; -}; From 86e3518430917483f35777a25a76159274e72d38 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 12 Sep 2022 16:34:37 +0200 Subject: [PATCH 015/755] Apply most of the suggestions from @flowln Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 366 ++++++++++++++++++------------------ launcher/JavaDownloader.h | 10 +- 2 files changed, 197 insertions(+), 179 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index b0cde7d98..425a7d470 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -9,7 +9,6 @@ #include "SysInfo.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" -#include "quazip.h" #include "ui/dialogs/ProgressDialog.h" // Quick & dirty struct to store files @@ -25,28 +24,31 @@ void JavaDownloader::executeTask() auto OS = m_OS; auto isLegacy = m_isLegacy; + downloadMojangJavaList(OS, isLegacy); +} +void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) +{ auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); auto response = new QByteArray(); setStatus(tr("Querying mojang meta")); netJob->addNetAction(Net::Download::makeByteArray( QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); - QObject::connect(this, &Task::aborted, [isLegacy]{ - QDir(FS::PathCombine("java",(isLegacy ? "java-legacy" : "java-current"))).removeRecursively(); - }); + connect(this, &Task::aborted, + [isLegacy] { QDir(FS::PathCombine("java", (isLegacy ? "java-legacy" : "java-current"))).removeRecursively(); }); - QObject::connect(netJob, &NetJob::finished, [netJob, response, this] { - //delete so that it's not called on a deleted job - QObject::disconnect(this, &Task::aborted, netJob, &NetJob::abort); + connect(netJob, &NetJob::finished, [netJob, response, this] { + // delete so that it's not called on a deleted job + disconnect(this, &Task::aborted, netJob, &NetJob::abort); netJob->deleteLater(); delete response; }); - QObject::connect(netJob, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(netJob, &NetJob::progress, this, &JavaDownloader::progress); + connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); - QObject::connect(this, &Task::aborted, netJob, &NetJob::abort); + connect(this, &Task::aborted, netJob, &NetJob::abort); - QObject::connect(netJob, &NetJob::succeeded, [response, OS, isLegacy, this, netJob] { + connect(netJob, &NetJob::succeeded, [response, OS, isLegacy, this, netJob] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -56,182 +58,190 @@ void JavaDownloader::executeTask() } auto versionArray = Json::ensureArray(Json::ensureObject(doc.object(), OS), isLegacy ? "jre-legacy" : "java-runtime-gamma"); if (!versionArray.empty()) { - setStatus(tr("Downloading java from Mojang")); - auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); - auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); - auto files = new QByteArray(); + parseMojangManifest(isLegacy, versionArray); - download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); - - QObject::connect(download, &NetJob::finished, [download, files, this] { - QObject::disconnect(this, &Task::aborted, download, &NetJob::abort); - download->deleteLater(); - delete files; - }); - QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); - QObject::connect(this, &Task::aborted, download, &NetJob::abort); - - QObject::connect(download, &NetJob::succeeded, [download, files, isLegacy, this] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *files; - return; - } - - // valid json doc, begin making jre spot - auto output = FS::PathCombine(QString("java"), (isLegacy ? "java-legacy" : "java-current")); - FS::ensureFolderPathExists(output); - std::vector toDownload; - auto list = doc.object()["files"].toObject(); - for (const auto& paths : list.keys()) { - auto file = FS::PathCombine(output, paths); - - auto type = list[paths].toObject()["type"].toString(); - if (type == "directory") { - FS::ensureFolderPathExists(file); - } else if (type == "link") { - // this is linux only ! - auto target = FS::PathCombine(file, "../" + list[paths].toObject()["target"].toString()); - QFile(target).link(file); - } else if (type == "file") { - // TODO download compressed version if it exists ? - auto raw = list[paths].toObject()["downloads"].toObject()["raw"].toObject(); - auto isExec = list[paths].toObject()["executable"].toBool(); - auto f = File{ file, raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec }; - toDownload.push_back(f); - } - } - auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); - for (const auto& file : toDownload) { - auto dl = Net::Download::makeFile(file.url, file.path); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); - if (file.isExec) { - QObject::connect(dl.get(), &Net::Download::succeeded, [file] { - QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); - }); - } - elementDownload->addNetAction(dl); - } - QObject::connect(elementDownload, &NetJob::finished, [elementDownload, this] { - QObject::disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); - elementDownload->deleteLater(); - }); - QObject::connect(elementDownload, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(elementDownload, &NetJob::failed, this, &JavaDownloader::emitFailed); - - - QObject::connect(this, &Task::aborted, elementDownload, &NetJob::abort); - QObject::connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); - elementDownload->start(); - }); - download->start(); } else { // mojang does not have a JRE for us, let's get azul zulu - setStatus(tr("Querying Azul meta")); - QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); - QString azulOS; - QString arch; - QString bitness; - - if (OS == "mac-os-arm64") { - // macos arm64 - azulOS = "macos"; - arch = "arm"; - bitness = "64"; - } else if (OS == "linux-arm64") { - // linux arm64 - azulOS = "linux"; - arch = "arm"; - bitness = "64"; - } else if (OS == "linux-arm") { - // linux arm (32) - azulOS = "linux"; - arch = "arm"; - bitness = "32"; - } else if (OS == "linux"){ - // linux x86 64 (used for debugging, should never reach here) - azulOS = "linux"; - arch = "x86"; - bitness = "64"; - } - auto metaResponse = new QByteArray(); - auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); - downloadJob->addNetAction( - Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" - "java_version=%1" - "&os=%2" - "&arch=%3" - "&hw_bitness=%4" - "&ext=zip" // as a zip for all os, even linux NOTE !! Linux ARM is .deb only !! - "&bundle_type=jre" // jre only - "&latest=true" // only get the one latest entry - ) - .arg(javaVersion, azulOS, arch, bitness), - metaResponse)); - QObject::connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse, this] { - QObject::disconnect(this, &Task::aborted, downloadJob, &NetJob::abort); - downloadJob->deleteLater(); - delete metaResponse; - }); - QObject::connect(this, &Task::aborted, downloadJob, &NetJob::abort); - QObject::connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); - QObject::connect(downloadJob, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy, this, downloadJob] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *metaResponse; - return; - } - auto array = doc.array(); - if (!array.empty()) { - // JRE found ! download the zip - setStatus(tr("Downloading java from Azul")); - auto downloadURL = QUrl(array[0].toObject()["url"].toString()); - auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); - auto temp = std::make_unique(FS::PathCombine(APPLICATION->root(), "temp", "XXXXXX.zip")); - FS::ensureFolderPathExists(FS::PathCombine(APPLICATION->root(),"temp")); - // Have to open at least once to generate path - temp->open(); - temp->close(); - download->addNetAction(Net::Download::makeFile(downloadURL, temp->fileName())); - QObject::connect(download, &NetJob::finished, [download, this] { - QObject::disconnect(this, &Task::aborted, download, &NetJob::abort); - download->deleteLater(); - }); - QObject::connect(download, &NetJob::progress, this, &JavaDownloader::progress); - QObject::connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); - QObject::connect(this, &Task::aborted, download, &NetJob::abort); - QObject::connect(download, &NetJob::succeeded, [isLegacy, file = std::move(temp), downloadURL, this] { - setStatus(tr("Extracting java")); - auto output = FS::PathCombine(FS::PathCombine(QCoreApplication::applicationDirPath(), "java"), - isLegacy ? "java-legacy" : "java-current"); - // This should do all of the extracting and creating folders - MMCZip::extractDir(file->fileName(), downloadURL.fileName().chopped(4), output); - emitSucceeded(); - }); - download->start(); - } else { - emitFailed(tr("No suitable JRE found")); - } - }); - downloadJob->start(); + downloadAzulMeta(OS, isLegacy, netJob); } }); netJob->start(); } -void JavaDownloader::abortNetJob(NetJob* elementDownload) +void JavaDownloader::parseMojangManifest(bool isLegacy, const QJsonArray& versionArray) { - if(elementDownload->isRunning()){ - elementDownload->abort(); - }else{ - emit elementDownload->aborted(); + setStatus(tr("Downloading java from Mojang")); + auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); + auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); + auto files = new QByteArray(); + + download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); + + connect(download, &NetJob::finished, [download, files, this] { + disconnect(this, &Task::aborted, download, &NetJob::abort); + download->deleteLater(); + delete files; + }); + connect(download, &NetJob::progress, this, &JavaDownloader::progress); + connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(this, &Task::aborted, download, &NetJob::abort); + + connect(download, &NetJob::succeeded, [files, isLegacy, this] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << *files; + return; + } + downloadMojangJava(isLegacy, doc); + }); + download->start(); +} +void JavaDownloader::downloadMojangJava(bool isLegacy, const QJsonDocument& doc) +{ // valid json doc, begin making jre spot + auto output = FS::PathCombine(QString("java"), (isLegacy ? "java-legacy" : "java-current")); + FS::ensureFolderPathExists(output); + std::vector toDownload; + auto list = doc.object()["files"].toObject(); + for (const auto& paths : list.keys()) { + auto file = FS::PathCombine(output, paths); + + auto type = list[paths].toObject()["type"].toString(); + if (type == "directory") { + FS::ensureFolderPathExists(file); + } else if (type == "link") { + // this is linux only ! + auto target = FS::PathCombine(file, "../" + list[paths].toObject()["target"].toString()); + QFile(target).link(file); + } else if (type == "file") { + // TODO download compressed version if it exists ? + auto raw = list[paths].toObject()["downloads"].toObject()["raw"].toObject(); + auto isExec = list[paths].toObject()["executable"].toBool(); + auto f = File{ file, raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec }; + toDownload.push_back(f); + } } + auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); + for (const auto& file : toDownload) { + auto dl = Net::Download::makeFile(file.url, file.path); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); + if (file.isExec) { + connect(dl.get(), &Net::Download::succeeded, + [file] { QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); }); + } + elementDownload->addNetAction(dl); + } + connect(elementDownload, &NetJob::finished, [elementDownload, this] { + disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); + elementDownload->deleteLater(); + }); + connect(elementDownload, &NetJob::progress, this, &JavaDownloader::progress); + connect(elementDownload, &NetJob::failed, this, &JavaDownloader::emitFailed); + + connect(this, &Task::aborted, elementDownload, &NetJob::abort); + connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); + elementDownload->start(); +} +void JavaDownloader::downloadAzulMeta(const QString& OS, bool isLegacy, const NetJob* netJob) +{ + setStatus(tr("Querying Azul meta")); + QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); + + QString azulOS; + QString arch; + QString bitness; + + mojangOStoAzul(OS, azulOS, arch, bitness); + auto metaResponse = new QByteArray(); + auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); + downloadJob->addNetAction( + Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" + "java_version=%1" + "&os=%2" + "&arch=%3" + "&hw_bitness=%4" + "&ext=zip" // as a zip for all os, even linux NOTE !! Linux ARM is .deb only !! + "&bundle_type=jre" // jre only + "&latest=true" // only get the one latest entry + ) + .arg(javaVersion, azulOS, arch, bitness), + metaResponse)); + connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse, this] { + disconnect(this, &Task::aborted, downloadJob, &NetJob::abort); + downloadJob->deleteLater(); + delete metaResponse; + }); + connect(this, &Task::aborted, downloadJob, &NetJob::abort); + connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(downloadJob, &NetJob::progress, this, &JavaDownloader::progress); + connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy, this] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << *metaResponse; + return; + } + auto array = doc.array(); + if (!array.empty()) { + downloadAzulJava(isLegacy, array); + } else { + emitFailed(tr("No suitable JRE found")); + } + }); + downloadJob->start(); +} +void JavaDownloader::mojangOStoAzul(const QString& OS, QString& azulOS, QString& arch, QString& bitness) +{ + if (OS == "mac-os-arm64") { + // macos arm64 + azulOS = "macos"; + arch = "arm"; + bitness = "64"; + } else if (OS == "linux-arm64") { + // linux arm64 + azulOS = "linux"; + arch = "arm"; + bitness = "64"; + } else if (OS == "linux-arm") { + // linux arm (32) + azulOS = "linux"; + arch = "arm"; + bitness = "32"; + } else if (OS == "linux") { + // linux x86 64 (used for debugging, should never reach here) + azulOS = "linux"; + arch = "x86"; + bitness = "64"; + } +} +void JavaDownloader::downloadAzulJava(bool isLegacy, const QJsonArray& array) +{ // JRE found ! download the zip + setStatus(tr("Downloading java from Azul")); + auto downloadURL = QUrl(array[0].toObject()["url"].toString()); + auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); + auto temp = std::make_unique(FS::PathCombine(APPLICATION->root(), "temp", "XXXXXX.zip")); + FS::ensureFolderPathExists(FS::PathCombine(APPLICATION->root(), "temp")); + // Have to open at least once to generate path + temp->open(); + temp->close(); + download->addNetAction(Net::Download::makeFile(downloadURL, temp->fileName())); + connect(download, &NetJob::finished, [download, this] { + disconnect(this, &Task::aborted, download, &NetJob::abort); + download->deleteLater(); + }); + connect(download, &NetJob::progress, this, &JavaDownloader::progress); + connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(this, &Task::aborted, download, &NetJob::abort); + connect(download, &NetJob::succeeded, [isLegacy, file = std::move(temp), downloadURL, this] { + setStatus(tr("Extracting java")); + auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), "java", isLegacy ? "java-legacy" : "java-current"); + // This should do all of the extracting and creating folders + MMCZip::extractDir(file->fileName(), downloadURL.fileName().chopped(4), output); + emitSucceeded(); + }); + download->start(); } void JavaDownloader::showPrompts(QWidget* parent) { @@ -289,7 +299,7 @@ void JavaDownloader::showPrompts(QWidget* parent) both->setEnabled(false); } if (!yes->isEnabled() && !no->isEnabled()) { - QMessageBox::warning(parent, tr("Already installed !"), tr("Both versions of java are already installed !")); + QMessageBox::warning(parent, tr("Already installed!"), tr("Both versions of java are already installed!")); return; } box.exec(); diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h index 00ccdf2bc..6ed67263a 100644 --- a/launcher/JavaDownloader.h +++ b/launcher/JavaDownloader.h @@ -17,5 +17,13 @@ class JavaDownloader : public Task { private: bool m_isLegacy; const QString& m_OS; - static void abortNetJob(NetJob* elementDownload); + + void downloadMojangJavaList(const QString& OS, bool isLegacy); + void parseMojangManifest(bool isLegacy, const QJsonArray& versionArray); + void downloadMojangJava(bool isLegacy, const QJsonDocument& doc); + + static void mojangOStoAzul(const QString& OS, QString& azulOS, QString& arch, QString& bitness) ; + void downloadAzulMeta(const QString& OS, bool isLegacy, const NetJob* netJob); + void downloadAzulJava(bool isLegacy, const QJsonArray& array); + }; From a65f20a78920efdb974b78f0c72df95d401ad337 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 12 Sep 2022 17:21:55 +0200 Subject: [PATCH 016/755] Fix a tiny formatting issue Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Signed-off-by: timoreo --- launcher/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 4bb686106..c4d9cb74f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -271,7 +271,7 @@ set(MINECRAFT_SOURCES minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.h - minecraft/LaunchProfile.cpp + minecraft/LaunchProfile.cpp minecraft/LaunchProfile.h minecraft/Component.cpp minecraft/Component.h From dcbd6cc1afe3495908511fb9179dd27fe8a70c98 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 12 Sep 2022 19:31:07 +0200 Subject: [PATCH 017/755] More Json:: everywhere ! Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 425a7d470..d0b8c5240 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -71,7 +71,7 @@ void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) void JavaDownloader::parseMojangManifest(bool isLegacy, const QJsonArray& versionArray) { setStatus(tr("Downloading java from Mojang")); - auto url = versionArray[0].toObject()["manifest"].toObject()["url"].toString(); + auto url = Json::ensureString(Json::ensureObject(Json::ensureObject(versionArray[0]), "manifest"), "url"); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); auto files = new QByteArray(); @@ -103,22 +103,22 @@ void JavaDownloader::downloadMojangJava(bool isLegacy, const QJsonDocument& doc) auto output = FS::PathCombine(QString("java"), (isLegacy ? "java-legacy" : "java-current")); FS::ensureFolderPathExists(output); std::vector toDownload; - auto list = doc.object()["files"].toObject(); + auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files"); for (const auto& paths : list.keys()) { auto file = FS::PathCombine(output, paths); - auto type = list[paths].toObject()["type"].toString(); + auto type = Json::requireString(Json::requireObject(list, paths), "type"); if (type == "directory") { FS::ensureFolderPathExists(file); } else if (type == "link") { // this is linux only ! - auto target = FS::PathCombine(file, "../" + list[paths].toObject()["target"].toString()); + auto target = FS::PathCombine(file, "../" + Json::requireString(Json::requireObject(list, paths), "target")); QFile(target).link(file); } else if (type == "file") { // TODO download compressed version if it exists ? - auto raw = list[paths].toObject()["downloads"].toObject()["raw"].toObject(); - auto isExec = list[paths].toObject()["executable"].toBool(); - auto f = File{ file, raw["url"].toString(), QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec }; + auto raw = Json::requireObject(Json::requireObject(Json::requireObject(list, paths), "downloads"), "raw"); + auto isExec = Json::ensureBoolean(Json::requireObject(list, paths), "executable", false); + auto f = File{ file, Json::requireString(raw, "url"), QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec }; toDownload.push_back(f); } } @@ -183,7 +183,7 @@ void JavaDownloader::downloadAzulMeta(const QString& OS, bool isLegacy, const Ne qWarning() << *metaResponse; return; } - auto array = doc.array(); + auto array = Json::ensureArray(doc.array()); if (!array.empty()) { downloadAzulJava(isLegacy, array); } else { From 6a5db12c6a3350b74eb9cd75a526881bc5b68572 Mon Sep 17 00:00:00 2001 From: timoreo Date: Mon, 24 Oct 2022 14:55:51 +0200 Subject: [PATCH 018/755] Apply some more suggestions from @flowln Co-authored-by: flow Signed-off-by: timoreo --- launcher/JavaDownloader.cpp | 82 +++++++++++-------- .../ui/pages/instance/InstanceSettingsPage.ui | 38 ++++----- 2 files changed, 68 insertions(+), 52 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index d0b8c5240..165573599 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -2,8 +2,10 @@ #include #include #include +#include #include "Application.h" #include "FileSystem.h" +#include "InstanceList.h" #include "Json.h" #include "MMCZip.h" #include "SysInfo.h" @@ -34,8 +36,10 @@ void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) netJob->addNetAction(Net::Download::makeByteArray( QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); - connect(this, &Task::aborted, - [isLegacy] { QDir(FS::PathCombine("java", (isLegacy ? "java-legacy" : "java-current"))).removeRecursively(); }); + connect(this, &Task::aborted, [isLegacy] { + QDir(FS::PathCombine(QCoreApplication::applicationDirPath(), "java", (isLegacy ? "java-legacy" : "java-current"))) + .removeRecursively(); + }); connect(netJob, &NetJob::finished, [netJob, response, this] { // delete so that it's not called on a deleted job @@ -70,7 +74,7 @@ void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) } void JavaDownloader::parseMojangManifest(bool isLegacy, const QJsonArray& versionArray) { - setStatus(tr("Downloading java from Mojang")); + setStatus(tr("Downloading Java from Mojang")); auto url = Json::ensureString(Json::ensureObject(Json::ensureObject(versionArray[0]), "manifest"), "url"); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); auto files = new QByteArray(); @@ -100,32 +104,41 @@ void JavaDownloader::parseMojangManifest(bool isLegacy, const QJsonArray& versio } void JavaDownloader::downloadMojangJava(bool isLegacy, const QJsonDocument& doc) { // valid json doc, begin making jre spot - auto output = FS::PathCombine(QString("java"), (isLegacy ? "java-legacy" : "java-current")); + auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java"), (isLegacy ? "java-legacy" : "java-current")); FS::ensureFolderPathExists(output); std::vector toDownload; auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files"); for (const auto& paths : list.keys()) { auto file = FS::PathCombine(output, paths); - auto type = Json::requireString(Json::requireObject(list, paths), "type"); + const QJsonObject& meta = Json::ensureObject(list, paths); + auto type = Json::ensureString(meta, "type"); if (type == "directory") { FS::ensureFolderPathExists(file); } else if (type == "link") { // this is linux only ! - auto target = FS::PathCombine(file, "../" + Json::requireString(Json::requireObject(list, paths), "target")); - QFile(target).link(file); + auto path = Json::ensureString(meta, "target"); + if (!path.isEmpty()) { + auto target = FS::PathCombine(file, "../" + path); + QFile(target).link(file); + } } else if (type == "file") { // TODO download compressed version if it exists ? - auto raw = Json::requireObject(Json::requireObject(Json::requireObject(list, paths), "downloads"), "raw"); - auto isExec = Json::ensureBoolean(Json::requireObject(list, paths), "executable", false); - auto f = File{ file, Json::requireString(raw, "url"), QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec }; - toDownload.push_back(f); + auto raw = Json::ensureObject(Json::ensureObject(meta, "downloads"), "raw"); + auto isExec = Json::ensureBoolean(meta, "executable", false); + auto url = Json::ensureString(raw, "url"); + if (!url.isEmpty() && QUrl(url).isValid()) { + auto f = File{ file, url, QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec }; + toDownload.push_back(f); + } } } auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); for (const auto& file : toDownload) { auto dl = Net::Download::makeFile(file.url, file.path); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); + if (!file.hash.isEmpty()) { + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); + } if (file.isExec) { connect(dl.get(), &Net::Download::succeeded, [file] { QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); }); @@ -161,7 +174,7 @@ void JavaDownloader::downloadAzulMeta(const QString& OS, bool isLegacy, const Ne "&os=%2" "&arch=%3" "&hw_bitness=%4" - "&ext=zip" // as a zip for all os, even linux NOTE !! Linux ARM is .deb only !! + "&ext=zip" // as a zip for all os, even linux NOTE !! Linux ARM is .deb or .tar.gz only !! "&bundle_type=jre" // jre only "&latest=true" // only get the one latest entry ) @@ -218,27 +231,30 @@ void JavaDownloader::mojangOStoAzul(const QString& OS, QString& azulOS, QString& } void JavaDownloader::downloadAzulJava(bool isLegacy, const QJsonArray& array) { // JRE found ! download the zip - setStatus(tr("Downloading java from Azul")); + setStatus(tr("Downloading Java from Azul")); auto downloadURL = QUrl(array[0].toObject()["url"].toString()); auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); - auto temp = std::make_unique(FS::PathCombine(APPLICATION->root(), "temp", "XXXXXX.zip")); - FS::ensureFolderPathExists(FS::PathCombine(APPLICATION->root(), "temp")); - // Have to open at least once to generate path - temp->open(); - temp->close(); - download->addNetAction(Net::Download::makeFile(downloadURL, temp->fileName())); + auto path = APPLICATION->instances()->getStagedInstancePath(); + auto temp = FS::PathCombine(path, "azulJRE.zip"); + + download->addNetAction(Net::Download::makeFile(downloadURL, temp)); connect(download, &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download, &NetJob::abort); download->deleteLater(); }); + connect(download, &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); connect(download, &NetJob::progress, this, &JavaDownloader::progress); - connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(download, &NetJob::failed, this, [this, path](QString reason) { + APPLICATION->instances()->destroyStagingPath(path); + emitFailed(std::move(reason)); + }); connect(this, &Task::aborted, download, &NetJob::abort); - connect(download, &NetJob::succeeded, [isLegacy, file = std::move(temp), downloadURL, this] { + connect(download, &NetJob::succeeded, [isLegacy, temp, downloadURL, path, this] { setStatus(tr("Extracting java")); auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), "java", isLegacy ? "java-legacy" : "java-current"); // This should do all of the extracting and creating folders - MMCZip::extractDir(file->fileName(), downloadURL.fileName().chopped(4), output); + MMCZip::extractDir(temp, downloadURL.fileName().chopped(4), output); + APPLICATION->instances()->destroyStagingPath(path); emitSucceeded(); }); download->start(); @@ -280,26 +296,27 @@ void JavaDownloader::showPrompts(QWidget* parent) return; } // Selection using QMessageBox for java 8 or 17 - QMessageBox box(QMessageBox::Icon::Question, tr("Java version"), - tr("Do you want to download Java version 8 or 17?\n Java 8 is recommended for minecraft versions below 1.17\n Java 17 " - "is recommended for minecraft versions above or equal to 1.17"), - QMessageBox::NoButton, parent); + QMessageBox box( + QMessageBox::Icon::Question, tr("Java version"), + tr("Do you want to download Java version 8 or 17?\n Java 8 is recommended for older Minecraft versions, below 1.17\n Java 17 " + "is recommended for newer Minecraft versions, starting from 1.17"), + QMessageBox::NoButton, parent); auto yes = box.addButton("Java 17", QMessageBox::AcceptRole); auto no = box.addButton("Java 8", QMessageBox::AcceptRole); auto both = box.addButton(tr("Download both"), QMessageBox::AcceptRole); auto cancel = box.addButton(QMessageBox::Cancel); - if (QFileInfo::exists(FS::PathCombine(QString("java"), "java-legacy"))) { + if (QFileInfo::exists(FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java"), "java-legacy"))) { no->setEnabled(false); } - if (QFileInfo::exists(FS::PathCombine(QString("java"), "java-current"))) { + if (QFileInfo::exists(FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java"), "java-current"))) { yes->setEnabled(false); } if (!yes->isEnabled() || !no->isEnabled()) { both->setEnabled(false); } if (!yes->isEnabled() && !no->isEnabled()) { - QMessageBox::warning(parent, tr("Already installed!"), tr("Both versions of java are already installed!")); + QMessageBox::information(parent, tr("Already installed!"), tr("Both versions of Java are already installed!")); return; } box.exec(); @@ -311,8 +328,9 @@ void JavaDownloader::showPrompts(QWidget* parent) auto down = new JavaDownloader(isLegacy, version); ProgressDialog dialog(parent); dialog.setSkipButton(true, tr("Abort")); - - if (dialog.execWithTask(down) && box.clickedButton() == both) { + bool finished_successfully = dialog.execWithTask(down); + // Run another download task for the other option as well! + if (finished_successfully && box.clickedButton() == both) { auto dwn = new JavaDownloader(false, version); ProgressDialog dg(parent); dg.setSkipButton(true, tr("Abort")); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 4ce59a0ae..a28332d5a 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -61,9 +61,6 @@ false - - - @@ -71,20 +68,6 @@ - - - - Browse... - - - - - - - Test - - - @@ -95,7 +78,24 @@ - + + + + + + + Browse... + + + + + + + Test + + + + Download Java... @@ -635,8 +635,6 @@ javaSettingsGroupBox javaPathTextBox javaDetectBtn - javaBrowseBtn - javaTestBtn memoryGroupBox minMemSpinBox maxMemSpinBox From 7f4665e3cca891bdd42d67a5a82a8c6561d1c86e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 14 Jul 2023 21:07:34 +0300 Subject: [PATCH 019/755] format Signed-off-by: Trial97 --- .../modplatform/flame/FileResolvingTask.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index ce7a60551..f84002385 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -128,12 +128,13 @@ void Flame::FileResolvingTask::netJobFinished() m_checkJob->start(); } -void Flame::FileResolvingTask::modrinthCheckFinished() { +void Flame::FileResolvingTask::modrinthCheckFinished() +{ setProgress(2, 3); qDebug() << "Finished with blocked mods : " << blockedProjects.size(); for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) { - auto &out = *it; + auto& out = *it; auto bytes = blockedProjects[out]; if (!out->resolved) { continue; @@ -153,15 +154,13 @@ void Flame::FileResolvingTask::modrinthCheckFinished() { out->resolved = false; } } - //copy to an output list and filter out projects found on modrinth + // copy to an output list and filter out projects found on modrinth auto block = std::make_shared>(); auto it = blockedProjects.keys(); - std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) { - return !f->resolved; - }); - //Display not found mods early + std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; }); + // Display not found mods early if (!block->empty()) { - //blocked mods found, we need the slug for displaying.... we need another job :D ! + // blocked mods found, we need the slug for displaying.... we need another job :D ! m_slugJob.reset(new NetJob("Slug Job", m_network)); int index = 0; for (auto mod : *block) { @@ -173,8 +172,8 @@ void Flame::FileResolvingTask::modrinthCheckFinished() { QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done auto json = QJsonDocument::fromJson(*output); - auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"), - "websiteUrl"); + auto base = + Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl"); auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId)); mod->websiteUrl = link; }); From d3dc00dcab31ecf4a8ae5886db310ff0bedc9018 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 14 Jul 2023 21:07:53 +0300 Subject: [PATCH 020/755] made concurent task fail Signed-off-by: Trial97 --- .../modplatform/flame/FileResolvingTask.cpp | 1 - launcher/tasks/ConcurrentTask.cpp | 20 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index f84002385..7695dfcdf 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -112,7 +112,6 @@ void Flame::FileResolvingTask::netJobFinished() connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) { step_progress->state = TaskStepState::Failed; stepProgress(*step_progress); - emitFailed(reason); }); connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress); connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 9aada5e69..7c7cfaf72 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -118,8 +118,11 @@ void ConcurrentTask::startNext() if (m_aborted || m_doing.count() > m_total_max_size) return; - if (m_queue.isEmpty() && m_doing.isEmpty() && !wasSuccessful()) { - emitSucceeded(); + if (m_queue.isEmpty() && m_doing.isEmpty() && isRunning()) { + if (m_failed.isEmpty()) + emitSucceeded(); + else + emitFailed(tr("One or more subtasks failed")); return; } @@ -194,7 +197,7 @@ void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg) auto task_progress = m_task_progress.value(task->getUid()); task_progress->status = msg; task_progress->state = TaskStepState::Running; - + emit stepProgress(*task_progress); if (totalSize() == 1) { @@ -207,7 +210,7 @@ void ConcurrentTask::subTaskDetails(Task::Ptr task, const QString& msg) auto task_progress = m_task_progress.value(task->getUid()); task_progress->details = msg; task_progress->state = TaskStepState::Running; - + emit stepProgress(*task_progress); if (totalSize() == 1) { @@ -220,7 +223,7 @@ void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 tota auto task_progress = m_task_progress.value(task->getUid()); task_progress->update(current, total); - + emit stepProgress(*task_progress); updateStepProgress(*task_progress, Operation::CHANGED); updateState(); @@ -233,7 +236,7 @@ void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 tota void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgress const& task_progress) { Operation op = Operation::ADDED; - + if (!m_task_progress.contains(task_progress.uid)) { m_task_progress.insert(task_progress.uid, std::make_shared(task_progress)); op = Operation::ADDED; @@ -254,12 +257,10 @@ void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgress const& emit stepProgress(*tp.get()); updateStepProgress(*tp.get(), op); } - } void ConcurrentTask::updateStepProgress(TaskStepProgress const& changed_progress, Operation op) { - switch (op) { case Operation::ADDED: m_stepProgress += changed_progress.current; @@ -274,9 +275,8 @@ void ConcurrentTask::updateStepProgress(TaskStepProgress const& changed_progress m_stepTotalProgress -= changed_progress.old_total; m_stepProgress += changed_progress.current; m_stepTotalProgress += changed_progress.total; - break; + break; } - } void ConcurrentTask::updateState() From 68b5fdf7489b440b61ca375452b0150bd94675e9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 15 Jul 2023 00:03:11 +0300 Subject: [PATCH 021/755] Ask user to retry if netjob fails Signed-off-by: Trial97 --- launcher/net/NetJob.cpp | 29 +++++++++++++++++++++++++++-- launcher/net/NetJob.h | 1 + 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index 3869316e3..7b5a87788 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -36,6 +36,8 @@ */ #include "NetJob.h" +#include "tasks/ConcurrentTask.h" +#include "ui/dialogs/CustomMessageBox.h" auto NetJob::addNetAction(NetAction::Ptr action) -> bool { @@ -52,8 +54,11 @@ void NetJob::startNext() // We're finished, check for failures and retry if we can (up to 3 times) if (!m_failed.isEmpty() && m_try < 3) { m_try += 1; - while (!m_failed.isEmpty()) - m_queue.enqueue(m_failed.take(*m_failed.keyBegin())); + while (!m_failed.isEmpty()) { + auto task = m_failed.take(*m_failed.keyBegin()); + m_done.remove(task.get()); + m_queue.enqueue(task); + } } } @@ -127,3 +132,23 @@ void NetJob::updateState() setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)") .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize()))); } + +void NetJob::emitFailed(QString reason) +{ + auto response = CustomMessageBox::selectable(nullptr, "Confirm retry", + "The tasks failed\n" + "Failed urls\n" + + getFailedFiles().join("\n\t") + + "\n" + "If this continues to happen please check the logs of the application" + "Do you want to retry?", + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) { + m_try = 0; + startNext(); + return; + } + ConcurrentTask::emitFailed(reason); +} \ No newline at end of file diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index 764cec18b..406ddda63 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -68,6 +68,7 @@ class NetJob : public ConcurrentTask { public slots: // Qt can't handle auto at the start for some reason? bool abort() override; + void emitFailed(QString reason) override; protected: void updateState() override; From 0880d4d37b654ffe68c4497a78883e73933dfe98 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 15 Jul 2023 09:16:37 +0300 Subject: [PATCH 022/755] made sure abort signal is catched in cocurent task Signed-off-by: Trial97 --- launcher/tasks/ConcurrentTask.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 7c7cfaf72..7bb02e57d 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -88,6 +88,7 @@ bool ConcurrentTask::abort() QMutableHashIterator doing_iter(m_doing); while (doing_iter.hasNext()) { auto task = doing_iter.next(); + disconnect(task.value().get(), &Task::aborted, this, 0); suceedeed &= (task.value())->abort(); } @@ -133,6 +134,9 @@ void ConcurrentTask::startNext() connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); }); connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); + // this should never happen but if it does better to fail the task that being stuck + // most + connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); }); connect(next.get(), &Task::details, this, [this, next](QString msg) { subTaskDetails(next, msg); }); From a7e0c9db9694a153a94cf0c924296576b0e9b767 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 15 Jul 2023 12:01:07 +0300 Subject: [PATCH 023/755] connected failed event for some tasks Signed-off-by: Trial97 --- launcher/modplatform/EnsureMetadataTask.cpp | 2 ++ .../modplatform/flame/FlameCheckUpdate.cpp | 7 ++--- launcher/modplatform/flame/FlameCheckUpdate.h | 8 +++++- .../flame/FlameInstanceCreationTask.cpp | 1 + .../modplatform/flame/FlamePackExportTask.cpp | 1 + .../modrinth/ModrinthCheckUpdate.cpp | 14 ++++------ launcher/ui/dialogs/ModUpdateDialog.cpp | 8 +++++- .../ui/pages/modplatform/flame/FlamePage.cpp | 12 ++++----- .../modplatform/modrinth/ModrinthPage.cpp | 5 ++++ .../pages/modplatform/technic/TechnicPage.cpp | 10 ++++--- .../ui/widgets/VariableSizedImageObject.cpp | 26 +++++++++++++------ 11 files changed, 63 insertions(+), 31 deletions(-) diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index c3eadd06d..22581e43e 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -148,6 +148,7 @@ void EnsureMetadataTask::executeTask() if (m_current_task) m_current_task.reset(); }); + connect(project_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed); m_current_task = project_task; project_task->start(); @@ -158,6 +159,7 @@ void EnsureMetadataTask::executeTask() if (m_current_task) m_current_task.reset(); }); + connect(version_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed); if (m_mods.size() > 1) setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider))); diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index a2628e34c..092133f1a 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -23,7 +23,7 @@ bool FlameCheckUpdate::abort() return true; } -ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) +ModPlatform::IndexedPack FlameCheckUpdate::getProjectInfo(ModPlatform::IndexedVersion& ver_info) { ModPlatform::IndexedPack pack; @@ -56,6 +56,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) } }); + connect(get_project_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed); QObject::connect(get_project_job, &NetJob::finished, [&loop, get_project_job] { get_project_job->deleteLater(); loop.quit(); @@ -67,7 +68,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) return pack; } -ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) +ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileId) { ModPlatform::IndexedVersion ver; @@ -99,7 +100,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) qDebug() << doc; } }); - + connect(get_file_info_job, &NetJob::failed, this, &FlameCheckUpdate::emitFailed); QObject::connect(get_file_info_job, &NetJob::finished, [&loop, get_file_info_job] { get_file_info_job->deleteLater(); loop.quit(); diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index 4a98d684f..0dbd199e7 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -8,7 +8,10 @@ class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - FlameCheckUpdate(QList& mods, std::list& mcVersions, std::optional loaders, std::shared_ptr mods_folder) + FlameCheckUpdate(QList& mods, + std::list& mcVersions, + std::optional loaders, + std::shared_ptr mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) {} @@ -19,6 +22,9 @@ class FlameCheckUpdate : public CheckUpdateTask { void executeTask() override; private: + ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info); + ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId); + NetJob* m_net_job = nullptr; bool m_was_aborted = false; diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index b57db288a..fa5057e5c 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -229,6 +229,7 @@ bool FlameCreationTask::updateInstance() m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path)); } }); + connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files: " << reason; }); connect(job.get(), &Task::finished, &loop, &QEventLoop::quit); m_process_update_file_info_job = job; diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index ac0da2142..69f88127e 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -327,6 +327,7 @@ void FlamePackExportTask::getProjectsInfo() } buildZip(); }); + connect(projTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed); task.reset(projTask); task->start(); } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index a7c22832a..c83ef2fd4 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -73,8 +73,6 @@ void ModrinthCheckUpdate::executeTask() auto response = std::make_shared(); auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); - QEventLoop lock; - connect(job.get(), &Task::succeeded, this, [this, response, &mappings, best_hash_type, job] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); @@ -83,7 +81,7 @@ void ModrinthCheckUpdate::executeTask() << " reason: " << parse_error.errorString(); qWarning() << *response; - failed(parse_error.errorString()); + emitFailed(parse_error.errorString()); return; } @@ -167,19 +165,17 @@ void ModrinthCheckUpdate::executeTask() } } } catch (Json::JsonException& e) { - failed(e.cause() + " : " + e.what()); + emitFailed(e.cause() + " : " + e.what()); + return; } + emitSucceeded(); }); - connect(job.get(), &Task::finished, &lock, &QEventLoop::quit); + connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::emitFailed); setStatus(tr("Waiting for the API response from Modrinth...")); setProgress(1, 3); m_net_job = qSharedPointerObjectCast(job); job->start(); - - lock.exec(); - - emitSucceeded(); } diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 8618b9240..df45cbbcf 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -271,6 +271,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); }); + connect(modrinth_task.get(), &EnsureMetadataTask::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); if (modrinth_task->getHashingTask()) seq.addTask(modrinth_task->getHashingTask()); @@ -284,6 +286,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); }); + connect(flame_task.get(), &EnsureMetadataTask::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); if (flame_task->getHashingTask()) seq.addTask(flame_task->getHashingTask()); @@ -337,12 +341,14 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R auto task = makeShared(mod, index_dir, next(first_choice)); connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); + connect(task.get(), &EnsureMetadataTask::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); m_second_try_metadata->addTask(task); } else { QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.append({mod, reason}); + m_failed_metadata.append({ mod, reason }); } } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index cef26bb6b..133a3981a 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -34,6 +34,7 @@ */ #include "FlamePage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui_FlamePage.h" #include @@ -42,9 +43,9 @@ #include "FlameModel.h" #include "InstanceImportTask.h" #include "Json.h" +#include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" -#include "modplatform/flame/FlameAPI.h" static FlameAPI api; @@ -171,6 +172,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) suggestCurrent(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); }); + connect(netJob, &NetJob::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); netJob->start(); } else { for (auto version : current.versions) { @@ -252,10 +255,8 @@ void FlamePage::updateUi() text += "
" + tr(" by ") + authorStrs.join(", "); } - if(current.extraInfoLoaded) { - if (!current.extra.issuesUrl.isEmpty() - || !current.extra.sourceUrl.isEmpty() - || !current.extra.wikiUrl.isEmpty()) { + if (current.extraInfoLoaded) { + if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty()) { text += "

" + tr("External links:") + "
"; } @@ -267,7 +268,6 @@ void FlamePage::updateUi() text += "- " + tr("Source code: %1").arg(current.extra.sourceUrl) + "
"; } - text += "
"; text += api.getModDescription(current.addonId).toUtf8(); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index c71dd9038..68ce8fd63 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -35,6 +35,7 @@ */ #include "ModrinthPage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui_ModrinthPage.h" #include "ModrinthModel.h" @@ -163,6 +164,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) suggestCurrent(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); }); + connect(netJob, &NetJob::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); netJob->start(); } else updateUI(); @@ -215,6 +218,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) suggestCurrent(); }); QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); }); + connect(netJob, &NetJob::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); netJob->start(); } else { diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index fc678fa20..c7f74d5ac 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -34,6 +34,7 @@ */ #include "TechnicPage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui_TechnicPage.h" #include @@ -41,16 +42,15 @@ #include "ui/dialogs/NewInstanceDialog.h" #include "BuildConfig.h" +#include "Json.h" #include "TechnicModel.h" #include "modplatform/technic/SingleZipPackInstallTask.h" #include "modplatform/technic/SolderPackInstallTask.h" -#include "Json.h" #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" -TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent) - : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) +TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) { ui->setupUi(this); connect(ui->searchButton, &QPushButton::clicked, this, &TechnicPage::triggerSearch); @@ -201,6 +201,8 @@ void TechnicPage::suggestCurrent() metadataLoaded(); }); + connect(jobPtr.get(), &NetJob::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); jobPtr = netJob; jobPtr->start(); @@ -252,6 +254,8 @@ void TechnicPage::metadataLoaded() netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); + connect(jobPtr.get(), &NetJob::failed, + [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); jobPtr = netJob; jobPtr->start(); diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index 991b4a047..a787f61e6 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -101,14 +101,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, auto full_entry_path = entry->getFullPath(); auto source_url = source; - connect(job, &NetJob::succeeded, this, [this, doc, full_entry_path, source_url, posInDocument] { - qDebug() << "Loaded resource at" << full_entry_path; - - // If we flushed, don't proceed. - if (!m_fetching_images.contains(source_url)) - return; - - QImage image(full_entry_path); + auto loadImage = [this, doc, full_entry_path, source_url, posInDocument](const QImage& image) { doc->addResource(QTextDocument::ImageResource, source_url, image); parseImage(doc, image, posInDocument); @@ -120,6 +113,23 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, doc->setPageSize(size); m_fetching_images.remove(source_url); + }; + connect(job, &NetJob::succeeded, this, [this, full_entry_path, source_url, loadImage] { + qDebug() << "Loaded resource at:" << full_entry_path; + // If we flushed, don't proceed. + if (!m_fetching_images.contains(source_url)) + return; + + QImage image(full_entry_path); + loadImage(image); + }); + connect(job, &NetJob::failed, this, [this, full_entry_path, source_url, loadImage](QString reason) { + qWarning() << "Failed resource at:" << full_entry_path << " because:" << reason; + // If we flushed, don't proceed. + if (!m_fetching_images.contains(source_url)) + return; + + loadImage(QImage()); }); connect(job, &NetJob::finished, job, &NetJob::deleteLater); From 6cf7c0a647e513978e692f02babd0140d909c3af Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 15 Jul 2023 20:17:16 +0300 Subject: [PATCH 024/755] Connected last fail signals Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 4 +++- launcher/modplatform/ResourceAPI.h | 3 +++ .../modplatform/helpers/NetworkResourceAPI.cpp | 15 ++++++++++++++- launcher/ui/pages/modplatform/ResourceModel.cpp | 8 ++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index f8ecdb33e..09fc66f59 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -180,7 +180,9 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType }; ResourceAPI::DependencySearchCallbacks callbacks; - + callbacks.on_fail = [](QString reason, int) { + qCritical() << tr("A network error occurred. Could not load project dependenies:%1").arg(reason); + }; callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) { try { QJsonArray arr; diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index d3277761e..6f92fa108 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -99,6 +99,7 @@ class ResourceAPI { }; struct VersionSearchCallbacks { std::function on_succeed; + std::function on_fail; }; struct ProjectInfoArgs { @@ -109,6 +110,7 @@ class ResourceAPI { }; struct ProjectInfoCallbacks { std::function on_succeed; + std::function on_fail; }; struct DependencySearchArgs { @@ -119,6 +121,7 @@ class ResourceAPI { struct DependencySearchCallbacks { std::function on_succeed; + std::function on_fail; }; public: diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index c278f800d..771486005 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -70,7 +70,7 @@ Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfo callbacks.on_succeed(doc, args.pack); }); - + QObject::connect(job.get(), &NetJob::failed, [callbacks](QString reason) { callbacks.on_fail(reason); }); return job; } @@ -99,6 +99,13 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi callbacks.on_succeed(doc, args.pack); }); + QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) { + int network_error_code = -1; + if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) + network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + callbacks.on_fail(reason, network_error_code); + }); return netJob; } @@ -143,6 +150,12 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, callbacks.on_succeed(doc, args.dependency); }); + QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) { + int network_error_code = -1; + if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) + network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + callbacks.on_fail(reason, network_error_code); + }); return netJob; }; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 49405a02b..170b016ad 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -178,6 +178,10 @@ void ResourceModel::loadEntry(QModelIndex& entry) return; versionRequestSucceeded(doc, pack, entry); }; + if (!callbacks.on_fail) + callbacks.on_fail = [](QString reason, int) { + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project versions:%1").arg(reason)); + }; if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) runInfoJob(job); @@ -194,6 +198,10 @@ void ResourceModel::loadEntry(QModelIndex& entry) return; infoRequestSucceeded(doc, pack, entry); }; + if (!callbacks.on_fail) + callbacks.on_fail = [](QString reason) { + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason)); + }; if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) runInfoJob(job); From f42514a1a3848f750241661138b89fdead1a3370 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 16 Jul 2023 00:38:39 +0300 Subject: [PATCH 025/755] Update launcher/tasks/ConcurrentTask.cpp Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- launcher/tasks/ConcurrentTask.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 7bb02e57d..b8d6ae598 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -134,8 +134,7 @@ void ConcurrentTask::startNext() connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); }); connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); - // this should never happen but if it does better to fail the task that being stuck - // most + // this should never happen but if it does, it's better to fail the task than get stuck connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); }); From 43cc04433d2bccd8c81bae56920bd464cbb27fe8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 19 Jul 2023 11:58:27 +0300 Subject: [PATCH 026/755] feat: refactored Instance ImportTask Signed-off-by: Trial97 --- launcher/InstanceImportTask.cpp | 201 +++++++++---------- launcher/InstanceImportTask.h | 50 ++--- launcher/MMCZip.cpp | 105 ++++++++++ launcher/MMCZip.h | 25 +++ launcher/ui/pages/modplatform/ImportPage.cpp | 1 + 5 files changed, 245 insertions(+), 137 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 352848f02..36c5b8f94 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -45,14 +45,15 @@ #include "icons/IconList.h" #include "icons/IconUtils.h" -#include "modplatform/technic/TechnicPackProcessor.h" -#include "modplatform/modrinth/ModrinthInstanceCreationTask.h" #include "modplatform/flame/FlameInstanceCreationTask.h" +#include "modplatform/modrinth/ModrinthInstanceCreationTask.h" +#include "modplatform/technic/TechnicPackProcessor.h" #include "settings/INISettingsObject.h" #include #include +#include #include @@ -65,15 +66,8 @@ bool InstanceImportTask::abort() if (!canAbort()) return false; - if (m_filesNetJob) - m_filesNetJob->abort(); - if (m_extractFuture.isRunning()) { - // NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled, - // but we can use this call to check the state when the extraction finishes. - m_extractFuture.cancel(); - m_extractFuture.waitForFinished(); - } - + if (task) + task->abort(); return Task::abort(); } @@ -86,7 +80,6 @@ void InstanceImportTask::executeTask() processZipPack(); } else { setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); - m_downloadRequired = true; const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); @@ -94,153 +87,153 @@ void InstanceImportTask::executeTask() entry->setStale(true); m_archivePath = entry->getFullPath(); - m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + auto filesNetJob = makeShared(tr("Modpack download"), APPLICATION->network()); + filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); - connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); - connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); - connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); - connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); - - m_filesNetJob->start(); + connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack); + connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress); + connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed); + connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted); + task.reset(filesNetJob); + filesNetJob->start(); } } -void InstanceImportTask::downloadSucceeded() +QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root) { - processZipPack(); - m_filesNetJob.reset(); -} + if (!isRunning()) { + return {}; + } + QuaZipDir rootDir(zip, root); + for (auto&& fileName : rootDir.entryList(QDir::Files)) { + setDetails(fileName); + if (fileName == "instance.cfg") { + qDebug() << "MultiMC:" << true; + m_modpackType = ModpackType::MultiMC; + return root; + } + if (fileName == "manifest.json") { + qDebug() << "Flame:" << true; + m_modpackType = ModpackType::Flame; + return root; + } -void InstanceImportTask::downloadFailed(QString reason) -{ - emitFailed(reason); - m_filesNetJob.reset(); -} + QCoreApplication::processEvents(); + } -void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total) -{ - setProgress(current, total); -} + // Recurse the search to non-ignored subfolders + for (auto&& fileName : rootDir.entryList(QDir::Dirs)) { + if ("overrides/" == fileName) + continue; -void InstanceImportTask::downloadAborted() -{ - emitAborted(); - m_filesNetJob.reset(); + QString result = getRootFromZip(zip, root + fileName); + if (!result.isEmpty()) + return result; + } + + return {}; } void InstanceImportTask::processZipPack() { - setStatus(tr("Extracting modpack")); + setStatus(tr("Attempting to determine instance type")); QDir extractDir(m_stagingPath); qDebug() << "Attempting to create instance from" << m_archivePath; // open the zip and find relevant files in it - m_packZip.reset(new QuaZip(m_archivePath)); - if (!m_packZip->open(QuaZip::mdUnzip)) - { + auto packZip = std::make_shared(m_archivePath); + if (!packZip->open(QuaZip::mdUnzip)) { emitFailed(tr("Unable to open supplied modpack zip file.")); return; } - QuaZipDir packZipDir(m_packZip.get()); + QuaZipDir packZipDir(packZip.get()); + qDebug() << "Attempting to determine instance type"; - // https://docs.modrinth.com/docs/modpacks/format_definition/#storage - bool modrinthFound = packZipDir.exists("/modrinth.index.json"); - bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json"); QString root; // NOTE: Prioritize modpack platforms that aren't searched for recursively. // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example - if(modrinthFound) - { + // https://docs.modrinth.com/docs/modpacks/format_definition/#storage + if (packZipDir.exists("/modrinth.index.json")) { // process as Modrinth pack - qDebug() << "Modrinth:" << modrinthFound; + qDebug() << "Modrinth:" << true; m_modpackType = ModpackType::Modrinth; - } - else if (technicFound) - { + } else if (packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json")) { // process as Technic pack - qDebug() << "Technic:" << technicFound; + qDebug() << "Technic:" << true; extractDir.mkpath(".minecraft"); extractDir.cd(".minecraft"); m_modpackType = ModpackType::Technic; + } else { + root = getRootFromZip(packZip.get()); + setDetails(""); } - else - { - QStringList paths_to_ignore { "overrides/" }; - - if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { - // process as MultiMC instance/pack - qDebug() << "MultiMC:" << mmcRoot; - root = mmcRoot; - m_modpackType = ModpackType::MultiMC; - } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) { - // process as Flame pack - qDebug() << "Flame:" << flameRoot; - root = flameRoot; - m_modpackType = ModpackType::Flame; - } - } - if(m_modpackType == ModpackType::Unknown) - { + if (m_modpackType == ModpackType::Unknown) { emitFailed(tr("Archive does not contain a recognized modpack type.")); return; } + setStatus(tr("Extracting modpack")); // make sure we extract just the pack - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); - connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &InstanceImportTask::extractFinished); - m_extractFutureWatcher.setFuture(m_extractFuture); + auto zipTask = makeShared(packZip, extractDir, root); + + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); + }); + + connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished); + connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } void InstanceImportTask::extractFinished() { - m_packZip.reset(); - - if (m_extractFuture.isCanceled()) - return; - if (!m_extractFuture.result().has_value()) { - emitFailed(tr("Failed to extract modpack")); - return; - } - QDir extractDir(m_stagingPath); qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); - while (it.hasNext()) - { + while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; - if(file.isDir()) - { + if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; - } - else - { + } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } - if(origPermissions != permissions) - { - if(!QFile::setPermissions(filepath, permissions)) - { + if (origPermissions != permissions) { + if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); - } - else - { + } else { qDebug() << "Fixed" << filepath; } } } - switch(m_modpackType) - { + switch (m_modpackType) { case ModpackType::MultiMC: processMultiMC(); return; @@ -276,7 +269,8 @@ void InstanceImportTask::processFlame() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = + makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { // FIXME: Find a way to get IDs in directly imported ZIPs inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); @@ -286,7 +280,7 @@ void InstanceImportTask::processFlame() inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); - + connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] { setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); emitSucceeded(); @@ -362,7 +356,8 @@ void InstanceImportTask::processModrinth() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = + new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { QString pack_id; if (!m_sourceUrl.isEmpty()) { @@ -378,7 +373,7 @@ void InstanceImportTask::processModrinth() inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); - + connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); emitSucceeded(); diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index 7fda439fc..cf4025194 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -35,64 +35,46 @@ #pragma once -#include "InstanceTask.h" -#include "net/NetJob.h" -#include #include #include -#include "settings/SettingsObject.h" -#include "QObjectPtr.h" -#include "modplatform/flame/PackManifest.h" +#include +#include "InstanceTask.h" +#include #include class QuaZip; -namespace Flame -{ - class FileResolvingTask; +namespace Flame { +class FileResolvingTask; } -class InstanceImportTask : public InstanceTask -{ +class InstanceImportTask : public InstanceTask { Q_OBJECT -public: + public: explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); bool abort() override; - const QVector &getBlockedFiles() const - { - return m_blockedMods; - } -protected: + protected: //! Entry point for tasks. virtual void executeTask() override; -private: - void processZipPack(); + private: void processMultiMC(); void processTechnic(); void processFlame(); void processModrinth(); + QString getRootFromZip(QuaZip* zip, const QString& root = ""); -private slots: - void downloadSucceeded(); - void downloadFailed(QString reason); - void downloadProgressChanged(qint64 current, qint64 total); - void downloadAborted(); + private slots: + void processZipPack(); void extractFinished(); -private: /* data */ - NetJob::Ptr m_filesNetJob; - shared_qobject_ptr m_modIdResolver; + private: /* data */ QUrl m_sourceUrl; QString m_archivePath; - bool m_downloadRequired = false; - std::unique_ptr m_packZip; - QFuture> m_extractFuture; - QFutureWatcher> m_extractFutureWatcher; - QVector m_blockedMods; - enum class ModpackType{ + Task::Ptr task; + enum class ModpackType { Unknown, MultiMC, Technic, @@ -104,6 +86,6 @@ private: /* data */ // the source URL / the resource it points to alone. QMap m_extra_info; - //FIXME: nuke + // FIXME: nuke QWidget* m_parent; }; diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index acd6bf7e4..13ad96e4e 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -501,4 +501,109 @@ bool ExportToZipTask::abort() return false; } +void ExtractZipTask::executeTask() +{ + m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); }); + connect(&m_zip_watcher, &QFutureWatcher::finished, this, &ExtractZipTask::finish); + m_zip_watcher.setFuture(m_zip_future); +} + +auto ExtractZipTask::extractZip() -> ZipResult +{ + auto target = m_output_dir.absolutePath(); + auto target_top_dir = QUrl::fromLocalFile(target); + + QStringList extracted; + + qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input->getZipName() << "to" << target; + auto numEntries = m_input->getEntriesCount(); + if (numEntries < 0) { + return ZipResult(tr("Failed to enumerate files in archive")); + } + if (numEntries == 0) { + logWarning(tr("Extracting empty archives seems odd...")); + return ZipResult(); + } + if (!m_input->goToFirstFile()) { + return ZipResult(tr("Failed to seek to first file in zip")); + } + + setStatus("Extracting files..."); + setProgress(0, numEntries); + do { + if (m_zip_future.isCanceled()) + return ZipResult(); + setProgress(m_progress + 1, m_progressTotal); + QString file_name = m_input->getCurrentFileName(); + if (!file_name.startsWith(m_subdirectory)) + continue; + + auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, m_subdirectory.size())); + auto original_name = relative_file_name; + setStatus("Unziping: " + relative_file_name); + + // Fix subdirs/files ending with a / getting transformed into absolute paths + if (relative_file_name.startsWith('/')) + relative_file_name = relative_file_name.mid(1); + + // Fix weird "folders with a single file get squashed" thing + QString sub_path; + if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) { + sub_path = relative_file_name.section('/', 0, -2) + '/'; + FS::ensureFolderPathExists(FS::PathCombine(target, sub_path)); + + relative_file_name = relative_file_name.split('/').last(); + } + + QString target_file_path; + if (relative_file_name.isEmpty()) { + target_file_path = target + '/'; + } else { + target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name); + if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/')) + target_file_path += '/'; + } + + if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { + return ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2") + .arg(relative_file_name, target)); + } + + if (!JlCompress::extractFile(m_input.get(), "", target_file_path)) { + JlCompress::removeFile(extracted); + return ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path)); + } + + extracted.append(target_file_path); + QFile::setPermissions(target_file_path, + QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + + qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; + } while (m_input->goToNextFile()); + + return ZipResult(); +} + +void ExtractZipTask::finish() +{ + if (m_zip_future.isCanceled()) { + emitAborted(); + } else if (auto result = m_zip_future.result(); result.has_value()) { + emitFailed(result.value()); + } else { + emitSucceeded(); + } +} + +bool ExtractZipTask::abort() +{ + if (m_zip_future.isRunning()) { + m_zip_future.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} + } // namespace MMCZip \ No newline at end of file diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index bc527ad1b..212be6f51 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -189,4 +189,29 @@ class ExportToZipTask : public Task { QFuture m_build_zip_future; QFutureWatcher m_build_zip_watcher; }; + +class ExtractZipTask : public Task { + public: + ExtractZipTask(std::shared_ptr input, QDir outputDir, QString subdirectory = "") + : m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory) + {} + virtual ~ExtractZipTask() = default; + + typedef std::optional ZipResult; + + protected: + virtual void executeTask() override; + bool abort() override; + + ZipResult extractZip(); + void finish(); + + private: + std::shared_ptr m_input; + QDir m_output_dir; + QString m_subdirectory; + + QFuture m_zip_future; + QFutureWatcher m_zip_watcher; +}; } // namespace MMCZip diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 30196aad6..a45f3a81c 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -38,6 +38,7 @@ #include "ui_ImportPage.h" #include +#include #include #include "ui/dialogs/NewInstanceDialog.h" From ce6a36c8b57fb9eb0a22c4f70b74e8fa200a88ad Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 28 Jul 2023 14:31:53 +0100 Subject: [PATCH 027/755] Deduplicate fix Signed-off-by: TheKodeToad --- launcher/MMCZip.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 49fd0e707..1a336375b 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -315,11 +315,6 @@ std::optional MMCZip::extractSubDir(QuaZip *zip, const QString & su if (relative_file_name.startsWith('/')) relative_file_name = relative_file_name.mid(1); - // Fix subdirs/files ending with a / getting transformed into absolute paths - if(name.startsWith('/')){ - name = name.mid(1); - } - // Fix weird "folders with a single file get squashed" thing QString sub_path; if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) { From 5538c1d0afba06d1a639dca45e88b0e94a3742f0 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 28 Jul 2023 15:11:24 +0100 Subject: [PATCH 028/755] Fix compilation (actually this time!), incorporating new changes Signed-off-by: TheKodeToad --- launcher/JavaDownloader.cpp | 55 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp index 165573599..f5960bbe9 100644 --- a/launcher/JavaDownloader.cpp +++ b/launcher/JavaDownloader.cpp @@ -30,8 +30,8 @@ void JavaDownloader::executeTask() } void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) { - auto netJob = new NetJob(QString("JRE::QueryVersions"), APPLICATION->network()); - auto response = new QByteArray(); + auto netJob = makeShared(QString("JRE::QueryVersions"), APPLICATION->network()); + auto response = std::make_shared(); setStatus(tr("Querying mojang meta")); netJob->addNetAction(Net::Download::makeByteArray( QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); @@ -41,18 +41,17 @@ void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) .removeRecursively(); }); - connect(netJob, &NetJob::finished, [netJob, response, this] { + connect(netJob.get(), &NetJob::finished, [netJob, response, this] { // delete so that it's not called on a deleted job - disconnect(this, &Task::aborted, netJob, &NetJob::abort); - netJob->deleteLater(); - delete response; + // FIXME: is this needed? qt should handle this + disconnect(this, &Task::aborted, netJob.get(), &NetJob::abort); }); - connect(netJob, &NetJob::progress, this, &JavaDownloader::progress); - connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(netJob.get(), &NetJob::progress, this, &JavaDownloader::progress); + connect(netJob.get(), &NetJob::failed, this, &JavaDownloader::emitFailed); - connect(this, &Task::aborted, netJob, &NetJob::abort); + connect(this, &Task::aborted, netJob.get(), &NetJob::abort); - connect(netJob, &NetJob::succeeded, [response, OS, isLegacy, this, netJob] { + connect(netJob.get(), &NetJob::succeeded, [response, OS, isLegacy, this, netJob] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -66,7 +65,7 @@ void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) } else { // mojang does not have a JRE for us, let's get azul zulu - downloadAzulMeta(OS, isLegacy, netJob); + downloadAzulMeta(OS, isLegacy, netJob.get()); } }); @@ -76,21 +75,19 @@ void JavaDownloader::parseMojangManifest(bool isLegacy, const QJsonArray& versio { setStatus(tr("Downloading Java from Mojang")); auto url = Json::ensureString(Json::ensureObject(Json::ensureObject(versionArray[0]), "manifest"), "url"); - auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); - auto files = new QByteArray(); + auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); + auto files = std::make_shared(); download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); - connect(download, &NetJob::finished, [download, files, this] { - disconnect(this, &Task::aborted, download, &NetJob::abort); - download->deleteLater(); - delete files; + connect(download.get(), &NetJob::finished, [download, files, this] { + disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download, &NetJob::progress, this, &JavaDownloader::progress); - connect(download, &NetJob::failed, this, &JavaDownloader::emitFailed); - connect(this, &Task::aborted, download, &NetJob::abort); + connect(download.get(), &NetJob::progress, this, &JavaDownloader::progress); + connect(download.get(), &NetJob::failed, this, &JavaDownloader::emitFailed); + connect(this, &Task::aborted, download.get(), &NetJob::abort); - connect(download, &NetJob::succeeded, [files, isLegacy, this] { + connect(download.get(), &NetJob::succeeded, [files, isLegacy, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -166,8 +163,8 @@ void JavaDownloader::downloadAzulMeta(const QString& OS, bool isLegacy, const Ne QString bitness; mojangOStoAzul(OS, azulOS, arch, bitness); - auto metaResponse = new QByteArray(); - auto downloadJob = new NetJob(QString("JRE::QueryAzulMeta"), APPLICATION->network()); + auto metaResponse = std::make_shared(); + auto downloadJob = makeShared(QString("JRE::QueryAzulMeta"), APPLICATION->network()); downloadJob->addNetAction( Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" "java_version=%1" @@ -180,15 +177,13 @@ void JavaDownloader::downloadAzulMeta(const QString& OS, bool isLegacy, const Ne ) .arg(javaVersion, azulOS, arch, bitness), metaResponse)); - connect(downloadJob, &NetJob::finished, [downloadJob, metaResponse, this] { - disconnect(this, &Task::aborted, downloadJob, &NetJob::abort); - downloadJob->deleteLater(); - delete metaResponse; + connect(downloadJob.get(), &NetJob::finished, [downloadJob, metaResponse, this] { + disconnect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); }); - connect(this, &Task::aborted, downloadJob, &NetJob::abort); + connect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); - connect(downloadJob, &NetJob::progress, this, &JavaDownloader::progress); - connect(downloadJob, &NetJob::succeeded, [metaResponse, isLegacy, this] { + connect(downloadJob.get(), &NetJob::progress, this, &JavaDownloader::progress); + connect(downloadJob.get(), &NetJob::succeeded, [metaResponse, isLegacy, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); if (parse_error.error != QJsonParseError::NoError) { From 748a644e2cf173540a60b7fc5815c89f02382cfd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 15 Aug 2023 18:50:32 +0300 Subject: [PATCH 029/755] added wait profiler Signed-off-by: Trial97 --- launcher/Application.cpp | 2 ++ launcher/CMakeLists.txt | 2 ++ launcher/tools/WaitProfiler.cpp | 44 +++++++++++++++++++++++++++++++++ launcher/tools/WaitProfiler.h | 29 ++++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 launcher/tools/WaitProfiler.cpp create mode 100644 launcher/tools/WaitProfiler.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a13935101..a546d0a85 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -49,6 +49,7 @@ #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" #include "settings/INIFile.h" +#include "tools/WaitProfiler.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" @@ -816,6 +817,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory())); + m_profilers.insert("waitprofiler", std::shared_ptr(new WaitProfilerFactory())); for (auto profiler : m_profilers.values()) { profiler->registerSettings(m_settings); } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..28359a4b1 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -457,6 +457,8 @@ set(TOOLS_SOURCES tools/JVisualVM.h tools/MCEditTool.cpp tools/MCEditTool.h + tools/WaitProfiler.cpp + tools/WaitProfiler.h ) set(META_SOURCES diff --git a/launcher/tools/WaitProfiler.cpp b/launcher/tools/WaitProfiler.cpp new file mode 100644 index 000000000..c43af0a89 --- /dev/null +++ b/launcher/tools/WaitProfiler.cpp @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "WaitProfiler.h" + +#include "BaseInstance.h" +#include "launch/LaunchTask.h" +#include "settings/SettingsObject.h" + +class WaitProfiler : public BaseProfiler { + Q_OBJECT + public: + WaitProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); + + protected: + void beginProfilingImpl(shared_qobject_ptr process); +}; + +WaitProfiler::WaitProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} + +void WaitProfiler::beginProfilingImpl(shared_qobject_ptr process) +{ + emit readyToLaunch(tr("Started process: %1").arg(process->pid())); +} + +BaseExternalTool* WaitProfilerFactory::createTool(InstancePtr instance, QObject* parent) +{ + return new WaitProfiler(globalSettings, instance, parent); +} +#include "WaitProfiler.moc" \ No newline at end of file diff --git a/launcher/tools/WaitProfiler.h b/launcher/tools/WaitProfiler.h new file mode 100644 index 000000000..ff2b9c2bf --- /dev/null +++ b/launcher/tools/WaitProfiler.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "BaseProfiler.h" + +class WaitProfilerFactory : public BaseProfilerFactory { + public: + QString name() const override { return "WaitProfiler"; } + void registerSettings(SettingsObjectPtr settings) override{}; + BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override; + bool check(QString* error) override { return true; }; + bool check(const QString& path, QString* error) override { return true; }; +}; From c28d9161cfc52aa21f1217d36032eb4e174b215c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 15 Aug 2023 18:54:22 +0300 Subject: [PATCH 030/755] added unused Signed-off-by: Trial97 --- launcher/tools/WaitProfiler.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/tools/WaitProfiler.h b/launcher/tools/WaitProfiler.h index ff2b9c2bf..fe813402c 100644 --- a/launcher/tools/WaitProfiler.h +++ b/launcher/tools/WaitProfiler.h @@ -22,8 +22,8 @@ class WaitProfilerFactory : public BaseProfilerFactory { public: QString name() const override { return "WaitProfiler"; } - void registerSettings(SettingsObjectPtr settings) override{}; + void registerSettings([[maybe_unused]] SettingsObjectPtr settings) override{}; BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override; - bool check(QString* error) override { return true; }; - bool check(const QString& path, QString* error) override { return true; }; + bool check([[maybe_unused]] QString* error) override { return true; }; + bool check([[maybe_unused]] const QString& path, [[maybe_unused]] QString* error) override { return true; }; }; From f7f7bc68653a3bfbfd9a571624582d45ef5e224b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 15 Aug 2023 20:52:17 +0300 Subject: [PATCH 031/755] Removed update disabled warning Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 5 ++++- launcher/modplatform/CheckUpdateTask.h | 11 ++++++++++- launcher/modplatform/flame/FlameCheckUpdate.cpp | 7 +------ launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp | 7 +------ launcher/ui/dialogs/ModUpdateDialog.cpp | 4 +--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 280e70d7b..1b96590a4 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -196,7 +196,10 @@ Task* ModFolderModel::createParseTask(Resource& resource) bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata) { for (auto mod : allMods()) { - if (mod->fileinfo().fileName() == filename) { + auto modfilename = mod->fileinfo().fileName(); + if (!mod->enabled() && modfilename.endsWith(".disabled")) + modfilename.chop(9); + if (modfilename == filename) { auto index_dir = indexDir(); mod->destroy(index_dir, preserve_metadata, false); diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 6d968ea48..6afa9c290 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -24,6 +24,7 @@ class CheckUpdateTask : public Task { QString old_version; QString new_version; QString changelog; + bool enabled; ModPlatform::ResourceProvider provider; shared_qobject_ptr download; @@ -33,9 +34,17 @@ class CheckUpdateTask : public Task { QString old_v, QString new_v, QString changelog, + bool enabled, 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) + , changelog(changelog) + , enabled(enabled) + , provider(p) + , download(t) {} }; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index e10fedc3c..79ae82e69 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -124,11 +124,6 @@ void FlameCheckUpdate::executeTask() int i = 0; for (auto* mod : m_mods) { - if (!mod->enabled()) { - emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); - continue; - } - setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name())); setProgress(i++, m_mods.size()); @@ -176,7 +171,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, - api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), + api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), mod->enabled(), ModPlatform::ResourceProvider::FLAME, download_task); } } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index a7c22832a..41eac6415 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -41,11 +41,6 @@ void ModrinthCheckUpdate::executeTask() ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", 10); for (auto* mod : m_mods) { - if (!mod->enabled()) { - emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); - continue; - } - auto hash = mod->metadata()->hash; // Sadly the API can only handle one hash type per call, se we @@ -163,7 +158,7 @@ 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); + mod->enabled(), ModPlatform::ResourceProvider::MODRINTH, download_task); } } } catch (Json::JsonException& e) { diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 0af1ec59b..639887a5d 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -5,8 +5,6 @@ #include "ScrollMessageBox.h" #include "ui_ReviewMessageBox.h" -#include "FileSystem.h" -#include "Json.h" #include "Markdown.h" #include "tasks/ConcurrentTask.h" @@ -351,7 +349,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info) { auto item_top = new QTreeWidgetItem(ui->modTreeWidget); - item_top->setCheckState(0, Qt::CheckState::Checked); + item_top->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); item_top->setText(0, info.name); item_top->setExpanded(true); From fa164aa0813687eed862fbd781aab47288d9bb8f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 15 Aug 2023 22:18:28 +0300 Subject: [PATCH 032/755] rename to GenericProfiler Signed-off-by: Trial97 --- launcher/Application.cpp | 4 ++-- launcher/CMakeLists.txt | 4 ++-- .../{WaitProfiler.cpp => GenericProfiler.cpp} | 18 ++++++++++-------- .../{WaitProfiler.h => GenericProfiler.h} | 4 ++-- 4 files changed, 16 insertions(+), 14 deletions(-) rename launcher/tools/{WaitProfiler.cpp => GenericProfiler.cpp} (63%) rename launcher/tools/{WaitProfiler.h => GenericProfiler.h} (90%) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a546d0a85..7f03b4d10 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -49,7 +49,7 @@ #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" #include "settings/INIFile.h" -#include "tools/WaitProfiler.h" +#include "tools/GenericProfiler.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" @@ -817,7 +817,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory())); - m_profilers.insert("waitprofiler", std::shared_ptr(new WaitProfilerFactory())); + m_profilers.insert("generic", std::shared_ptr(new GenericProfilerFactory())); for (auto profiler : m_profilers.values()) { profiler->registerSettings(m_settings); } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 28359a4b1..a14f279e7 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -457,8 +457,8 @@ set(TOOLS_SOURCES tools/JVisualVM.h tools/MCEditTool.cpp tools/MCEditTool.h - tools/WaitProfiler.cpp - tools/WaitProfiler.h + tools/GenericProfiler.cpp + tools/GenericProfiler.h ) set(META_SOURCES diff --git a/launcher/tools/WaitProfiler.cpp b/launcher/tools/GenericProfiler.cpp similarity index 63% rename from launcher/tools/WaitProfiler.cpp rename to launcher/tools/GenericProfiler.cpp index c43af0a89..594024a7d 100644 --- a/launcher/tools/WaitProfiler.cpp +++ b/launcher/tools/GenericProfiler.cpp @@ -15,30 +15,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "WaitProfiler.h" +#include "GenericProfiler.h" #include "BaseInstance.h" #include "launch/LaunchTask.h" #include "settings/SettingsObject.h" -class WaitProfiler : public BaseProfiler { +class GenericProfiler : public BaseProfiler { Q_OBJECT public: - WaitProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); + GenericProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent = 0); protected: void beginProfilingImpl(shared_qobject_ptr process); }; -WaitProfiler::WaitProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} +GenericProfiler::GenericProfiler(SettingsObjectPtr settings, InstancePtr instance, QObject* parent) + : BaseProfiler(settings, instance, parent) +{} -void WaitProfiler::beginProfilingImpl(shared_qobject_ptr process) +void GenericProfiler::beginProfilingImpl(shared_qobject_ptr process) { emit readyToLaunch(tr("Started process: %1").arg(process->pid())); } -BaseExternalTool* WaitProfilerFactory::createTool(InstancePtr instance, QObject* parent) +BaseExternalTool* GenericProfilerFactory::createTool(InstancePtr instance, QObject* parent) { - return new WaitProfiler(globalSettings, instance, parent); + return new GenericProfiler(globalSettings, instance, parent); } -#include "WaitProfiler.moc" \ No newline at end of file +#include "GenericProfiler.moc" \ No newline at end of file diff --git a/launcher/tools/WaitProfiler.h b/launcher/tools/GenericProfiler.h similarity index 90% rename from launcher/tools/WaitProfiler.h rename to launcher/tools/GenericProfiler.h index fe813402c..e99fc059f 100644 --- a/launcher/tools/WaitProfiler.h +++ b/launcher/tools/GenericProfiler.h @@ -19,9 +19,9 @@ #include "BaseProfiler.h" -class WaitProfilerFactory : public BaseProfilerFactory { +class GenericProfilerFactory : public BaseProfilerFactory { public: - QString name() const override { return "WaitProfiler"; } + QString name() const override { return "Generic"; } void registerSettings([[maybe_unused]] SettingsObjectPtr settings) override{}; BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override; bool check([[maybe_unused]] QString* error) override { return true; }; From c41c39b5d86b168a189a82460fc6edb7873cfa6e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 15 Aug 2023 23:55:09 +0300 Subject: [PATCH 033/755] cleanup the mod name checking Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 5 +---- launcher/minecraft/mod/Resource.cpp | 8 ++++++++ launcher/minecraft/mod/Resource.h | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 1b96590a4..1f10a5a2f 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -196,10 +196,7 @@ Task* ModFolderModel::createParseTask(Resource& resource) bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata) { for (auto mod : allMods()) { - auto modfilename = mod->fileinfo().fileName(); - if (!mod->enabled() && modfilename.endsWith(".disabled")) - modfilename.chop(9); - if (modfilename == filename) { + if (mod->getOriginalFileName() == filename) { auto index_dir = indexDir(); mod->destroy(index_dir, preserve_metadata, false); diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index da806f0f4..6a60473fb 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -169,3 +169,11 @@ bool Resource::isMoreThanOneHardLink() const { return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; } + +auto Resource::getOriginalFileName() const -> QString +{ + auto fileName = m_file_info.fileName(); + if (!m_enabled) + fileName.chop(9); + return fileName; +} \ No newline at end of file diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c1ed49461..f0d34705c 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -45,6 +45,7 @@ class Resource : public QObject { [[nodiscard]] auto internal_id() const -> QString { return m_internal_id; } [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } + [[nodiscard]] auto getOriginalFileName() const -> QString; [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } From f3baa420b2154c37a8f418a090c4392d526482c2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 16 Aug 2023 13:43:07 +0100 Subject: [PATCH 034/755] Fix broken tab order Signed-off-by: TheKodeToad --- launcher/ui/pages/global/JavaPage.ui | 8 +++++++- launcher/ui/pages/instance/InstanceSettingsPage.ui | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 44289eb86..0bf27fef1 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -320,8 +320,14 @@ minMemSpinBox maxMemSpinBox permGenSpinBox - javaBrowseBtn javaPathTextBox + javaBrowseBtn + javaDownloadBtn + javaDetectBtn + javaTestBtn + skipCompatibilityCheckbox + skipJavaWizardCheckbox + jvmArgsTextBox tabWidget diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index f9e679b2e..08a74671c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -714,6 +714,12 @@ openGlobalJavaSettingsButton settingsTabs javaSettingsGroupBox + javaPathTextBox + javaBrowseBtn + javaDownloadBtn + javaDetectBtn + javaTestBtn + skipCompatibilityCheckbox memoryGroupBox minMemSpinBox maxMemSpinBox @@ -733,12 +739,6 @@ useNativeOpenALCheck showGameTime recordGameTime - skipCompatibilityCheckbox - javaPathTextBox - javaBrowseBtn - javaDownloadBtn - javaDetectBtn - javaTestBtn miscellaneousSettingsBox closeAfterLaunchCheck quitAfterGameStopCheck From 8fbc665acb83e47fed1ffd0ae5c9aa1f59fd49bd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 16 Aug 2023 18:28:46 +0300 Subject: [PATCH 035/755] Added UserData as another posible path for portable builds Signed-off-by: Trial97 --- launcher/Application.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a13935101..6237967fb 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -263,7 +263,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) adjustedBy = "Persistent data path"; #ifndef Q_OS_MACOS - if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { + if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) { + dataPath = portableUserData; + adjustedBy = "Portable user data path"; + m_portable = true; + } else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { dataPath = m_rootPath; adjustedBy = "Portable data path"; m_portable = true; From 907c2fd19c148230d58a3d02fd31ecd8bf7aae86 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 09:16:17 +0300 Subject: [PATCH 036/755] added missing header Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ImportPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 3cb161629..cc11d2c16 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -52,6 +52,7 @@ #include "Json.h" #include "InstanceImportTask.h" +#include "net/NetJob.h" class UrlValidator : public QValidator { public: From bf810053b720ac728affd45878147ce593b4f6fd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 17:21:11 +0300 Subject: [PATCH 037/755] updated instance copy Signed-off-by: Trial97 --- launcher/FileSystem.h | 1 + launcher/InstanceCopyTask.cpp | 83 +++++++++++++++++++++++------------ launcher/InstanceCopyTask.h | 1 + 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index bfed576c1..946e55a2c 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -218,6 +218,7 @@ class create_link : public QObject { bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } int totalLinked() { return m_linked; } + int totalToLink() { return static_cast(m_links_to_make.size()); } void runPrivileged() { runPrivileged(QString()); } void runPrivileged(const QString& offset); diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index 8abf30640..3a40bc06d 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -1,10 +1,12 @@ #include "InstanceCopyTask.h" #include #include +#include #include "FileSystem.h" #include "NullInstance.h" #include "pathmatcher/RegexpMatcher.h" #include "settings/INISettingsObject.h" +#include "tasks/Task.h" InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs) { @@ -38,38 +40,50 @@ void InstanceCopyTask::executeTask() { setStatus(tr("Copying instance %1").arg(m_origInstance->name())); - auto copySaves = [&]() { - QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft")); - QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft")); - - QString staging_mc_dir; - if (mcDir.exists() && !dotMCDir.exists()) - staging_mc_dir = mcDir.filePath(); - else - staging_mc_dir = dotMCDir.filePath(); - - FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves")); - savesCopy.followSymlinks(true); - - return savesCopy(); - }; - - m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] { + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { if (m_useClone) { FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath); folderClone.matcher(m_matcher.get()); + folderClone(true); + setProgress(0, folderClone.totalCloned()); + connect(&folderClone, &FS::clone::fileCloned, + [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); return folderClone(); - } else if (m_useLinks || m_useHardLinks) { + } + if (m_useLinks || m_useHardLinks) { + std::unique_ptr savesCopy; + if (m_copySaves) { + QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft")); + QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft")); + + QString staging_mc_dir; + if (mcDir.exists() && !dotMCDir.exists()) + staging_mc_dir = mcDir.filePath(); + else + staging_mc_dir = dotMCDir.filePath(); + + savesCopy = std::make_unique(FS::PathCombine(m_origInstance->gameRoot(), "saves"), + FS::PathCombine(staging_mc_dir, "saves")); + savesCopy->followSymlinks(true); + (*savesCopy)(true); + setProgress(0, savesCopy->totalCopied()); + connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); + } FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath); int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get()); + folderLink(true); + setProgress(0, m_progressTotal + folderLink.totalToLink()); + connect(&folderLink, &FS::create_link::fileLinked, + [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); bool there_were_errors = false; if (!folderLink()) { #if defined Q_OS_WIN32 if (!m_useHardLinks) { + setProgress(0, m_progressTotal); qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks"; qDebug() << "attempting to run with privelage"; @@ -94,13 +108,11 @@ void InstanceCopyTask::executeTask() } } - if (m_copySaves) { - there_were_errors |= !copySaves(); + if (savesCopy) { + there_were_errors |= !(*savesCopy)(); } return got_priv_results && !there_were_errors; - } else { - qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str(); } #else qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str(); @@ -108,17 +120,19 @@ void InstanceCopyTask::executeTask() return false; } - if (m_copySaves) { - there_were_errors |= !copySaves(); + if (savesCopy) { + there_were_errors |= !(*savesCopy)(); } return !there_were_errors; - } else { - FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); - folderCopy.followSymlinks(false).matcher(m_matcher.get()); - - return folderCopy(); } + FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); + folderCopy.followSymlinks(false).matcher(m_matcher.get()); + + folderCopy(true); + setProgress(0, folderCopy.totalCopied()); + connect(&folderCopy, &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); + return folderCopy(); }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &InstanceCopyTask::copyAborted); @@ -171,3 +185,14 @@ void InstanceCopyTask::copyAborted() emitFailed(tr("Instance folder copy has been aborted.")); return; } + +bool InstanceCopyTask::abort() +{ + if (m_copyFutureWatcher.isRunning()) { + m_copyFutureWatcher.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_copyFutureWatcher` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} \ No newline at end of file diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h index 357c6df0b..0f7f1020d 100644 --- a/launcher/InstanceCopyTask.h +++ b/launcher/InstanceCopyTask.h @@ -19,6 +19,7 @@ class InstanceCopyTask : public InstanceTask { protected: //! Entry point for tasks. virtual void executeTask() override; + bool abort() override; void copyFinished(); void copyAborted(); From 609eaa67abd9e9844f5cd52f81c70826689cd90a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 21:23:51 +0300 Subject: [PATCH 038/755] refactored skin apis Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 1 + launcher/minecraft/services/CapeChange.cpp | 89 +++++--------------- launcher/minecraft/services/CapeChange.h | 30 +++---- launcher/minecraft/services/SkinDelete.cpp | 59 ++++--------- launcher/minecraft/services/SkinDelete.h | 26 +++--- launcher/minecraft/services/SkinUpload.cpp | 84 +++++++----------- launcher/minecraft/services/SkinUpload.h | 31 +++---- launcher/net/Logging.cpp | 1 + launcher/net/Logging.h | 1 + launcher/net/NetRequest.cpp | 2 + launcher/net/StaticHeaderProxy.h | 39 +++++++++ launcher/ui/dialogs/SkinUploadDialog.cpp | 5 +- launcher/ui/pages/global/AccountListPage.cpp | 2 +- 13 files changed, 145 insertions(+), 225 deletions(-) create mode 100644 launcher/net/StaticHeaderProxy.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..3b6218b7f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -139,6 +139,7 @@ set(NET_SOURCES net/HeaderProxy.h net/RawHeaderProxy.h net/ApiHeaderProxy.h + net/StaticHeaderProxy.h net/ApiDownload.h net/ApiDownload.cpp net/ApiUpload.cpp diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index 2ba38a6af..5a7820b54 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -35,87 +35,38 @@ #include "CapeChange.h" -#include -#include +#include -#include "Application.h" +#include "net/ByteArraySink.h" +#include "net/StaticHeaderProxy.h" -CapeChange::CapeChange(QObject* parent, QString token, QString cape) : Task(parent), m_capeId(cape), m_token(token) {} - -void CapeChange::setCape([[maybe_unused]] QString& cape) +CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); - auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->put(request, requestString.toUtf8()); + logCat = taskMCServicesLogC; +}; - setStatus(tr("Equipping cape")); - - m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); - connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); -} - -void CapeChange::clearCape() -{ - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); - auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->deleteResource(request); - - setStatus(tr("Removing cape")); - - m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); - connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); -} - -void CapeChange::executeTask() +QNetworkReply* CapeChange::getReply(QNetworkRequest& request) { if (m_capeId.isEmpty()) { - clearCape(); + setStatus(tr("Removing cape")); + return m_network->deleteResource(request); } else { - setCape(m_capeId); + setStatus(tr("Equipping cape")); + return m_network->post(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); } } -void CapeChange::downloadError(QNetworkReply::NetworkError error) +void CapeChange::init() { - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); + addHeaderProxy(new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() }, + })); } -void CapeChange::sslErrors(const QList& errors) +CapeChange::Ptr CapeChange::make(QString token, QString capeId) { - int i = 1; - for (auto error : errors) { - qCritical() << "Cape change SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void CapeChange::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); + auto up = makeShared(token, capeId); + up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"); + up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); + return up; } diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h index d0c893c44..74805ef43 100644 --- a/launcher/minecraft/services/CapeChange.h +++ b/launcher/minecraft/services/CapeChange.h @@ -1,31 +1,21 @@ #pragma once -#include -#include -#include -#include "QObjectPtr.h" -#include "tasks/Task.h" +#include "net/NetRequest.h" -class CapeChange : public Task { +class CapeChange : public Net::NetRequest { Q_OBJECT public: - CapeChange(QObject* parent, QString token, QString capeId); - virtual ~CapeChange() {} + using Ptr = shared_qobject_ptr; + CapeChange(QString token, QString capeId); + virtual ~CapeChange() = default; - private: - void setCape(QString& cape); - void clearCape(); + static CapeChange::Ptr make(QString token, QString capeId); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; private: QString m_capeId; QString m_token; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - void downloadFinished(); }; diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp index 9e9020692..7944637f6 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/services/SkinDelete.cpp @@ -35,56 +35,31 @@ #include "SkinDelete.h" -#include -#include +#include "net/ByteArraySink.h" +#include "net/StaticHeaderProxy.h" -#include "Application.h" - -SkinDelete::SkinDelete(QObject* parent, QString token) : Task(parent), m_token(token) {} - -void SkinDelete::executeTask() +SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active")); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->deleteResource(request); - m_reply = shared_qobject_ptr(rep); + logCat = taskMCServicesLogC; +}; +QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) +{ setStatus(tr("Deleting skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &SkinDelete::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &SkinDelete::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinDelete::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &SkinDelete::sslErrors); - connect(rep, &QNetworkReply::finished, this, &SkinDelete::downloadFinished); + return m_network->deleteResource(request); } -void SkinDelete::downloadError(QNetworkReply::NetworkError error) +void SkinDelete::init() { - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); + addHeaderProxy(new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() }, + })); } -void SkinDelete::sslErrors(const QList& errors) +SkinDelete::Ptr SkinDelete::make(QString token) { - int i = 1; - for (auto error : errors) { - qCritical() << "Skin Delete SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void SkinDelete::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); + auto up = makeShared(token); + up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"); + up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); + return up; } diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index d5b2e63db..b0fb866cd 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -1,26 +1,20 @@ #pragma once -#include -#include -#include "tasks/Task.h" +#include "net/NetRequest.h" -typedef shared_qobject_ptr SkinDeletePtr; - -class SkinDelete : public Task { +class SkinDelete : public Net::NetRequest { Q_OBJECT public: - SkinDelete(QObject* parent, QString token); + using Ptr = shared_qobject_ptr; + SkinDelete(QString token); virtual ~SkinDelete() = default; + static SkinDelete::Ptr make(QString token); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + private: QString m_token; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - void downloadFinished(); }; diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp index 163b481b1..0400fa0f4 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/services/SkinUpload.cpp @@ -36,30 +36,17 @@ #include "SkinUpload.h" #include -#include -#include "Application.h" +#include "net/ByteArraySink.h" +#include "net/StaticHeaderProxy.h" -QByteArray getVariant(SkinUpload::Model model) +SkinUpload::SkinUpload(QString token, QByteArray skin, SkinUpload::Model model) : NetRequest(), m_model(model), m_skin(skin), m_token(token) { - switch (model) { - default: - qDebug() << "Unknown skin type!"; - case SkinUpload::STEVE: - return "CLASSIC"; - case SkinUpload::ALEX: - return "SLIM"; - } -} + logCat = taskMCServicesLogC; +}; -SkinUpload::SkinUpload(QObject* parent, QString token, QByteArray skin, SkinUpload::Model model) - : Task(parent), m_model(model), m_skin(skin), m_token(token) -{} - -void SkinUpload::executeTask() +QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins")); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart skin; @@ -69,50 +56,37 @@ void SkinUpload::executeTask() QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - model.setBody(getVariant(m_model)); + + switch (m_model) { + default: + qDebug() << "Unknown skin type!"; + emitFailed("Unknown skin type!"); + return nullptr; + case SkinUpload::STEVE: + model.setBody("CLASSIC"); + break; + case SkinUpload::ALEX: + model.setBody("SLIM"); + break; + } multiPart->append(skin); multiPart->append(model); - - QNetworkReply* rep = APPLICATION->network()->post(request, multiPart); - m_reply = shared_qobject_ptr(rep); - setStatus(tr("Uploading skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &SkinUpload::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &SkinUpload::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinUpload::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &SkinUpload::sslErrors); - connect(rep, &QNetworkReply::finished, this, &SkinUpload::downloadFinished); + return m_network->post(request, multiPart); } -void SkinUpload::downloadError(QNetworkReply::NetworkError error) +void SkinUpload::init() { - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); + addHeaderProxy(new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() }, + })); } -void SkinUpload::sslErrors(const QList& errors) +SkinUpload::Ptr SkinUpload::make(QString token, QByteArray skin, SkinUpload::Model model) { - int i = 1; - for (auto error : errors) { - qCritical() << "Skin Upload SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void SkinUpload::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); + auto up = makeShared(token, skin, model); + up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); + up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); + return up; } diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index 5716aa996..2da836d52 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -1,34 +1,25 @@ #pragma once -#include -#include -#include -#include "tasks/Task.h" +#include "net/NetRequest.h" -typedef shared_qobject_ptr SkinUploadPtr; - -class SkinUpload : public Task { +class SkinUpload : public Net::NetRequest { Q_OBJECT public: + using Ptr = shared_qobject_ptr; enum Model { STEVE, ALEX }; // Note this class takes ownership of the file. - SkinUpload(QObject* parent, QString token, QByteArray skin, Model model = STEVE); - virtual ~SkinUpload() {} + SkinUpload(QString token, QByteArray skin, Model model = STEVE); + virtual ~SkinUpload() = default; + + static SkinUpload::Ptr make(QString token, QByteArray skin, Model model = STEVE); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; private: Model m_model; QByteArray m_skin; QString m_token; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - - void downloadFinished(); }; diff --git a/launcher/net/Logging.cpp b/launcher/net/Logging.cpp index a9b9db7cf..45d2dcc20 100644 --- a/launcher/net/Logging.cpp +++ b/launcher/net/Logging.cpp @@ -22,5 +22,6 @@ Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net") Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download") Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload") +Q_LOGGING_CATEGORY(taskMCServicesLogC, "launcher.task.minecraft.servicies") Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache") Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http") diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h index 4deed2b49..d3a11cdce 100644 --- a/launcher/net/Logging.h +++ b/launcher/net/Logging.h @@ -24,5 +24,6 @@ Q_DECLARE_LOGGING_CATEGORY(taskNetLogC) Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC) Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC) +Q_DECLARE_LOGGING_CATEGORY(taskMCServicesLogC) Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC) Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC) diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index ff59da18b..eef550e15 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -111,6 +111,8 @@ void NetRequest::executeTask() m_last_progress_bytes = 0; QNetworkReply* rep = getReply(request); + if (rep == nullptr) // it failed + return; m_reply.reset(rep); connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); diff --git a/launcher/net/StaticHeaderProxy.h b/launcher/net/StaticHeaderProxy.h new file mode 100644 index 000000000..0e62d80ff --- /dev/null +++ b/launcher/net/StaticHeaderProxy.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include "net/HeaderProxy.h" + +namespace Net { + +class StaticHeaderProxy : public HeaderProxy { + public: + StaticHeaderProxy(QList hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){}; + virtual ~StaticHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest&) const override { return m_hdrs; }; + void setHeaders(QList hdrs) { m_hdrs = hdrs; }; + + private: + QList m_hdrs; +}; + +} // namespace Net \ No newline at end of file diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index 5b3ebfa23..70f1e6760 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -101,12 +102,12 @@ void SkinUploadDialog::on_buttonBox_accepted() } else if (ui->alexBtn->isChecked()) { model = SkinUpload::ALEX; } - skinUpload.addTask(shared_qobject_ptr(new SkinUpload(this, m_acct->accessToken(), FS::read(fileName), model))); + skinUpload.addTask(SkinUpload::make(m_acct->accessToken(), FS::read(fileName), model)); } auto selectedCape = ui->capeCombo->currentData().toString(); if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(shared_qobject_ptr(new CapeChange(this, m_acct->accessToken(), selectedCape))); + skinUpload.addTask(CapeChange::make(m_acct->accessToken(), selectedCape)); } if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index c95bfabdd..3dcf05e0a 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -268,7 +268,7 @@ void AccountListPage::on_actionDeleteSkin_triggered() QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); ProgressDialog prog(this); - auto deleteSkinTask = std::make_shared(this, account->accessToken()); + auto deleteSkinTask = SkinDelete::make(account->accessToken()); if (prog.execWithTask((Task*)deleteSkinTask.get()) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); return; From 9ad029e0286ee18a6531d673c052a48a5f40d8d5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 23:10:33 +0300 Subject: [PATCH 039/755] added skins directory Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/ui/MainWindow.cpp | 5 ++++ launcher/ui/MainWindow.h | 2 ++ launcher/ui/MainWindow.ui | 13 +++++++++++ launcher/ui/pages/global/LauncherPage.cpp | 13 +++++++++++ launcher/ui/pages/global/LauncherPage.h | 1 + launcher/ui/pages/global/LauncherPage.ui | 28 +++++++++++++++++++---- 7 files changed, 59 insertions(+), 4 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 66044d9ac..9cff11546 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -536,6 +536,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); + m_settings->registerSetting("SkinsDir", "skins"); // Editors m_settings->registerSetting("JsonEditor", QString()); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 5e55a5abb..a82932e08 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1186,6 +1186,11 @@ void MainWindow::on_actionViewCentralModsFolder_triggered() DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); } +void MainWindow::on_actionViewSkinsFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->settings()->get("SkinsDir").toString(), true); +} + void MainWindow::on_actionViewIconThemeFolder_triggered() { DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 0b6144522..5d816b5d0 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -118,6 +118,8 @@ class MainWindow : public QMainWindow { void on_actionViewWidgetThemeFolder_triggered(); void on_actionViewCatPackFolder_triggered(); + void on_actionViewSkinsFolder_triggered(); + void on_actionViewSelectedInstFolder_triggered(); void refreshInstances(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 91b2c2703..266551588 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -190,6 +190,7 @@ + @@ -575,6 +576,18 @@ Open the central mods folder in a file browser.
+ + + + .. + + + View &Skins Folder + + + Open the skins folder in a file browser. + + Themes diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 7f22fdb50..5576a0339 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -173,6 +173,17 @@ void LauncherPage::on_downloadsDirBrowseBtn_clicked() } } +void LauncherPage::on_skinsDirBrowseBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Skins Folder"), ui->skinsDirTextBox->text()); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { + QString cooked_dir = FS::NormalizePath(raw_dir); + ui->skinsDirTextBox->setText(cooked_dir); + } +} + void LauncherPage::on_metadataDisableBtn_clicked() { ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); @@ -205,6 +216,7 @@ void LauncherPage::applySettings() s->set("CentralModsDir", ui->modsDirTextBox->text()); s->set("IconsDir", ui->iconsDirTextBox->text()); s->set("DownloadsDir", ui->downloadsDirTextBox->text()); + s->set("SkinsDir", ui->skinsDirTextBox->text()); s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked()); auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId(); @@ -259,6 +271,7 @@ void LauncherPage::loadSettings() ui->modsDirTextBox->setText(s->get("CentralModsDir").toString()); ui->iconsDirTextBox->setText(s->get("IconsDir").toString()); ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString()); + ui->skinsDirTextBox->setText(s->get("SkinsDir").toString()); ui->downloadsDirWatchRecursiveCheckBox->setChecked(s->get("DownloadsDirWatchRecursive").toBool()); QString sortMode = s->get("InstSortMode").toString(); diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index e733224d2..f9aefb171 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -74,6 +74,7 @@ class LauncherPage : public QWidget, public BasePage { void on_modsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked(); void on_downloadsDirBrowseBtn_clicked(); + void on_skinsDirBrowseBtn_clicked(); void on_metadataDisableBtn_clicked(); /*! diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index bc259a9b8..87c4b5ef9 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -67,7 +67,7 @@ Folders - + &Downloads: @@ -90,13 +90,16 @@ - + - + + + + Browse @@ -147,7 +150,24 @@ - + + + + Browse + + + + + + + &Skins: + + + skinsDirTextBox + + + + When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge). From c86b8b0f70894e447c8a08e136b919987c3dffae Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 5 Sep 2023 00:18:36 +0300 Subject: [PATCH 040/755] added skin manage dialog Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 28 +- launcher/SkinUtils.cpp | 52 --- launcher/SkinUtils.h | 22 - launcher/minecraft/services/CapeChange.h | 21 - launcher/minecraft/services/SkinDelete.h | 20 - launcher/minecraft/services/SkinUpload.h | 25 -- .../{services => skins}/CapeChange.cpp | 6 +- launcher/minecraft/skins/CapeChange.h | 39 ++ .../{services => skins}/SkinDelete.cpp | 3 +- launcher/minecraft/skins/SkinDelete.h | 38 ++ launcher/minecraft/skins/SkinList.cpp | 393 ++++++++++++++++++ launcher/minecraft/skins/SkinList.h | 80 ++++ launcher/minecraft/skins/SkinModel.cpp | 128 ++++++ launcher/minecraft/skins/SkinModel.h | 58 +++ .../{services => skins}/SkinUpload.cpp | 28 +- launcher/minecraft/skins/SkinUpload.h | 42 ++ launcher/net/Logging.cpp | 2 +- launcher/net/Logging.h | 2 +- launcher/net/NetJob.h | 4 +- launcher/net/NetRequest.cpp | 1 + launcher/net/NetRequest.h | 1 + launcher/net/StaticHeaderProxy.h | 2 +- launcher/ui/MainWindow.cpp | 1 - launcher/ui/dialogs/ProfileSelectDialog.cpp | 1 - launcher/ui/dialogs/SkinUploadDialog.cpp | 165 -------- launcher/ui/dialogs/SkinUploadDialog.h | 28 -- launcher/ui/dialogs/SkinUploadDialog.ui | 95 ----- .../ui/dialogs/skins/SkinManageDialog.cpp | 339 +++++++++++++++ launcher/ui/dialogs/skins/SkinManageDialog.h | 62 +++ launcher/ui/dialogs/skins/SkinManageDialog.ui | 193 +++++++++ launcher/ui/pages/global/AccountListPage.cpp | 34 +- launcher/ui/pages/global/AccountListPage.h | 3 +- launcher/ui/pages/global/AccountListPage.ui | 24 +- 33 files changed, 1425 insertions(+), 515 deletions(-) delete mode 100644 launcher/SkinUtils.cpp delete mode 100644 launcher/SkinUtils.h delete mode 100644 launcher/minecraft/services/CapeChange.h delete mode 100644 launcher/minecraft/services/SkinDelete.h delete mode 100644 launcher/minecraft/services/SkinUpload.h rename launcher/minecraft/{services => skins}/CapeChange.cpp (90%) create mode 100644 launcher/minecraft/skins/CapeChange.h rename launcher/minecraft/{services => skins}/SkinDelete.cpp (96%) create mode 100644 launcher/minecraft/skins/SkinDelete.h create mode 100644 launcher/minecraft/skins/SkinList.cpp create mode 100644 launcher/minecraft/skins/SkinList.h create mode 100644 launcher/minecraft/skins/SkinModel.cpp create mode 100644 launcher/minecraft/skins/SkinModel.h rename launcher/minecraft/{services => skins}/SkinUpload.cpp (79%) create mode 100644 launcher/minecraft/skins/SkinUpload.h delete mode 100644 launcher/ui/dialogs/SkinUploadDialog.cpp delete mode 100644 launcher/ui/dialogs/SkinUploadDialog.h delete mode 100644 launcher/ui/dialogs/SkinUploadDialog.ui create mode 100644 launcher/ui/dialogs/skins/SkinManageDialog.cpp create mode 100644 launcher/ui/dialogs/skins/SkinManageDialog.h create mode 100644 launcher/ui/dialogs/skins/SkinManageDialog.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 3b6218b7f..6914b3385 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -377,13 +377,17 @@ set(MINECRAFT_SOURCES minecraft/AssetsUtils.h minecraft/AssetsUtils.cpp - # Minecraft services - minecraft/services/CapeChange.cpp - minecraft/services/CapeChange.h - minecraft/services/SkinUpload.cpp - minecraft/services/SkinUpload.h - minecraft/services/SkinDelete.cpp - minecraft/services/SkinDelete.h + # Minecraft skins + minecraft/skins/CapeChange.cpp + minecraft/skins/CapeChange.h + minecraft/skins/SkinUpload.cpp + minecraft/skins/SkinUpload.h + minecraft/skins/SkinDelete.cpp + minecraft/skins/SkinDelete.h + minecraft/skins/SkinModel.cpp + minecraft/skins/SkinModel.h + minecraft/skins/SkinList.cpp + minecraft/skins/SkinList.h minecraft/Agent.h) @@ -742,8 +746,6 @@ SET(LAUNCHER_SOURCES ui/InstanceWindow.cpp # FIXME: maybe find a better home for this. - SkinUtils.cpp - SkinUtils.h FileIgnoreProxy.cpp FileIgnoreProxy.h FastFileIconProvider.cpp @@ -965,8 +967,6 @@ SET(LAUNCHER_SOURCES ui/dialogs/ReviewMessageBox.h ui/dialogs/VersionSelectDialog.cpp ui/dialogs/VersionSelectDialog.h - ui/dialogs/SkinUploadDialog.cpp - ui/dialogs/SkinUploadDialog.h ui/dialogs/ResourceDownloadDialog.cpp ui/dialogs/ResourceDownloadDialog.h ui/dialogs/ScrollMessageBox.cpp @@ -980,6 +980,9 @@ SET(LAUNCHER_SOURCES ui/dialogs/InstallLoaderDialog.cpp ui/dialogs/InstallLoaderDialog.h + ui/dialogs/skins/SkinManageDialog.cpp + ui/dialogs/skins/SkinManageDialog.h + # GUI - widgets ui/widgets/Common.cpp ui/widgets/Common.h @@ -1096,7 +1099,6 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/NewComponentDialog.ui ui/dialogs/NewsDialog.ui ui/dialogs/ProfileSelectDialog.ui - ui/dialogs/SkinUploadDialog.ui ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportPackDialog.ui ui/dialogs/ExportToModListDialog.ui @@ -1111,6 +1113,8 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ScrollMessageBox.ui ui/dialogs/BlockedModsDialog.ui ui/dialogs/ChooseProviderDialog.ui + + ui/dialogs/skins/SkinManageDialog.ui ) qt_add_resources(LAUNCHER_RESOURCES diff --git a/launcher/SkinUtils.cpp b/launcher/SkinUtils.cpp deleted file mode 100644 index 989114ad5..000000000 --- a/launcher/SkinUtils.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SkinUtils.h" -#include "Application.h" -#include "net/HttpMetaCache.h" - -#include -#include -#include -#include -#include - -namespace SkinUtils { -/* - * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise - */ -QPixmap getFaceFromCache(QString username, int height, int width) -{ - QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath()); - - if (fskin.exists()) { - QPixmap skinTexture(fskin.fileName()); - if (!skinTexture.isNull()) { - QPixmap skin = QPixmap(8, 8); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - skin.fill(QColorConstants::Transparent); -#else - skin.fill(QColor(0, 0, 0, 0)); -#endif - QPainter painter(&skin); - painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); - painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); - return skin.scaled(height, width, Qt::KeepAspectRatio); - } - } - - return QPixmap(); -} -} // namespace SkinUtils diff --git a/launcher/SkinUtils.h b/launcher/SkinUtils.h deleted file mode 100644 index 11bc8bc6f..000000000 --- a/launcher/SkinUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace SkinUtils { -QPixmap getFaceFromCache(QString id, int height = 64, int width = 64); -} diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h deleted file mode 100644 index 74805ef43..000000000 --- a/launcher/minecraft/services/CapeChange.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "net/NetRequest.h" - -class CapeChange : public Net::NetRequest { - Q_OBJECT - public: - using Ptr = shared_qobject_ptr; - CapeChange(QString token, QString capeId); - virtual ~CapeChange() = default; - - static CapeChange::Ptr make(QString token, QString capeId); - void init() override; - - protected: - virtual QNetworkReply* getReply(QNetworkRequest&) override; - - private: - QString m_capeId; - QString m_token; -}; diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h deleted file mode 100644 index b0fb866cd..000000000 --- a/launcher/minecraft/services/SkinDelete.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "net/NetRequest.h" - -class SkinDelete : public Net::NetRequest { - Q_OBJECT - public: - using Ptr = shared_qobject_ptr; - SkinDelete(QString token); - virtual ~SkinDelete() = default; - - static SkinDelete::Ptr make(QString token); - void init() override; - - protected: - virtual QNetworkReply* getReply(QNetworkRequest&) override; - - private: - QString m_token; -}; diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h deleted file mode 100644 index 2da836d52..000000000 --- a/launcher/minecraft/services/SkinUpload.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "net/NetRequest.h" - -class SkinUpload : public Net::NetRequest { - Q_OBJECT - public: - using Ptr = shared_qobject_ptr; - enum Model { STEVE, ALEX }; - - // Note this class takes ownership of the file. - SkinUpload(QString token, QByteArray skin, Model model = STEVE); - virtual ~SkinUpload() = default; - - static SkinUpload::Ptr make(QString token, QByteArray skin, Model model = STEVE); - void init() override; - - protected: - virtual QNetworkReply* getReply(QNetworkRequest&) override; - - private: - Model m_model; - QByteArray m_skin; - QString m_token; -}; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/skins/CapeChange.cpp similarity index 90% rename from launcher/minecraft/services/CapeChange.cpp rename to launcher/minecraft/skins/CapeChange.cpp index 5a7820b54..863e89844 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/skins/CapeChange.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,7 +43,7 @@ CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token) { - logCat = taskMCServicesLogC; + logCat = taskMCSkinsLogC; }; QNetworkReply* CapeChange::getReply(QNetworkRequest& request) @@ -52,7 +53,7 @@ QNetworkReply* CapeChange::getReply(QNetworkRequest& request) return m_network->deleteResource(request); } else { setStatus(tr("Equipping cape")); - return m_network->post(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); + return m_network->put(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); } } @@ -67,6 +68,7 @@ CapeChange::Ptr CapeChange::make(QString token, QString capeId) { auto up = makeShared(token, capeId); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"); + up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); return up; } diff --git a/launcher/minecraft/skins/CapeChange.h b/launcher/minecraft/skins/CapeChange.h new file mode 100644 index 000000000..bcafcde87 --- /dev/null +++ b/launcher/minecraft/skins/CapeChange.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "net/NetRequest.h" + +class CapeChange : public Net::NetRequest { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + CapeChange(QString token, QString capeId); + virtual ~CapeChange() = default; + + static CapeChange::Ptr make(QString token, QString capeId); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + + private: + QString m_capeId; + QString m_token; +}; diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/skins/SkinDelete.cpp similarity index 96% rename from launcher/minecraft/services/SkinDelete.cpp rename to launcher/minecraft/skins/SkinDelete.cpp index 7944637f6..982cac1b7 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/skins/SkinDelete.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,7 +41,7 @@ SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token) { - logCat = taskMCServicesLogC; + logCat = taskMCSkinsLogC; }; QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) diff --git a/launcher/minecraft/skins/SkinDelete.h b/launcher/minecraft/skins/SkinDelete.h new file mode 100644 index 000000000..5d02e0cc4 --- /dev/null +++ b/launcher/minecraft/skins/SkinDelete.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "net/NetRequest.h" + +class SkinDelete : public Net::NetRequest { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + SkinDelete(QString token); + virtual ~SkinDelete() = default; + + static SkinDelete::Ptr make(QString token); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + + private: + QString m_token; +}; diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp new file mode 100644 index 000000000..be329564b --- /dev/null +++ b/launcher/minecraft/skins/SkinList.cpp @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SkinList.h" + +#include +#include + +#include "FileSystem.h" +#include "Json.h" +#include "minecraft/skins/SkinModel.h" + +SkinList::SkinList(QObject* parent, QString path, MinecraftAccountPtr acct) : QAbstractListModel(parent), m_acct(acct) +{ + FS::ensureFolderPathExists(m_dir.absolutePath()); + m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); + m_watcher.reset(new QFileSystemWatcher(this)); + is_watching = false; + connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &SkinList::directoryChanged); + connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &SkinList::fileChanged); + directoryChanged(path); +} + +void SkinList::startWatching() +{ + if (is_watching) { + return; + } + update(); + is_watching = m_watcher->addPath(m_dir.absolutePath()); + if (is_watching) { + qDebug() << "Started watching " << m_dir.absolutePath(); + } else { + qDebug() << "Failed to start watching " << m_dir.absolutePath(); + } +} + +void SkinList::stopWatching() +{ + save(); + if (!is_watching) { + return; + } + is_watching = !m_watcher->removePath(m_dir.absolutePath()); + if (!is_watching) { + qDebug() << "Stopped watching " << m_dir.absolutePath(); + } else { + qDebug() << "Failed to stop watching " << m_dir.absolutePath(); + } +} + +bool SkinList::update() +{ + QVector newSkins; + m_dir.refresh(); + + auto manifestInfo = QFileInfo(m_dir.absoluteFilePath("index.json")); + if (manifestInfo.exists()) { + try { + auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); + const auto root = doc.object(); + auto skins = Json::ensureArray(root, "skins"); + for (auto jSkin : skins) { + SkinModel s(m_dir, Json::ensureObject(jSkin)); + if (s.isValid()) { + newSkins << s; + } + } + } catch (const Exception& e) { + qCritical() << "Couldn't load skins json:" << e.cause(); + } + } else { + newSkins = loadMinecraftSkins(); + } + + bool needsSave = false; + const auto& skin = m_acct->accountData()->minecraftProfile.skin; + if (!skin.url.isEmpty() && !skin.data.isEmpty()) { + QPixmap skinTexture; + SkinModel* nskin = nullptr; + for (auto i = 0; i < newSkins.size(); i++) { + if (newSkins[i].getURL() == skin.url) { + nskin = &newSkins[i]; + break; + } + } + if (!nskin) { + auto name = m_acct->profileName() + ".png"; + if (QFileInfo(m_dir.absoluteFilePath(name)).exists()) { + name = QUrl(skin.url).fileName() + ".png"; + } + auto path = m_dir.absoluteFilePath(name); + if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { + SkinModel s(path); + s.setModel(SkinModel::CLASSIC); // maybe better model detection + s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); + s.setURL(skin.url); + newSkins << s; + needsSave = true; + } + } else { + nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); + } + } + + auto folderContents = m_dir.entryInfoList(); + // if there are any untracked files... + for (QFileInfo entry : folderContents) { + if (!entry.isFile() && entry.suffix() != "png") + continue; + + SkinModel w(entry.absoluteFilePath()); + if (w.isValid()) { + auto add = true; + for (auto s : newSkins) { + if (s.name() == w.name()) { + add = false; + break; + } + } + if (add) { + newSkins.append(w); + needsSave = true; + } + } + } + std::sort(newSkins.begin(), newSkins.end(), + [](const SkinModel& a, const SkinModel& b) { return a.getPath().localeAwareCompare(b.getPath()) < 0; }); + beginResetModel(); + m_skin_list.swap(newSkins); + endResetModel(); + if (needsSave) + save(); + return true; +} + +void SkinList::directoryChanged(const QString& path) +{ + QDir new_dir(path); + if (!new_dir.exists()) + if (!FS::ensureFolderPathExists(new_dir.absolutePath())) + return; + if (m_dir.absolutePath() != new_dir.absolutePath()) { + m_dir.setPath(path); + m_dir.refresh(); + if (is_watching) + stopWatching(); + startWatching(); + } + update(); +} + +void SkinList::fileChanged(const QString& path) +{ + qDebug() << "Checking " << path; + QFileInfo checkfile(path); + if (!checkfile.exists()) + return; + + for (int i = 0; i < m_skin_list.count(); i++) { + if (m_skin_list[i].getPath() == checkfile.absoluteFilePath()) { + m_skin_list[i].refresh(); + dataChanged(index(i), index(i)); + break; + } + } +} + +QStringList SkinList::mimeTypes() const +{ + return { "text/uri-list" }; +} + +Qt::DropActions SkinList::supportedDropActions() const +{ + return Qt::CopyAction; +} + +bool SkinList::dropMimeData(const QMimeData* data, + Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) +{ + if (action == Qt::IgnoreAction) + return true; + // check if the action is supported + if (!data || !(action & supportedDropActions())) + return false; + + // files dropped from outside? + if (data->hasUrls()) { + auto urls = data->urls(); + QStringList iconFiles; + for (auto url : urls) { + // only local files may be dropped... + if (!url.isLocalFile()) + continue; + iconFiles += url.toLocalFile(); + } + installSkins(iconFiles); + return true; + } + return false; +} + +Qt::ItemFlags SkinList::flags(const QModelIndex& index) const +{ + Qt::ItemFlags f = Qt::ItemIsDropEnabled | QAbstractListModel::flags(index); + if (index.isValid()) { + f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + } + return f; +} + +QVariant SkinList::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + + if (row < 0 || row >= m_skin_list.size()) + return QVariant(); + auto skin = m_skin_list[row]; + switch (role) { + case Qt::DecorationRole: + return skin.getTexture(); + case Qt::DisplayRole: + return skin.name(); + case Qt::UserRole: + return skin.name(); + case Qt::EditRole: + return skin.name(); + default: + return QVariant(); + } +} + +int SkinList::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() ? 0 : m_skin_list.size(); +} + +void SkinList::installSkins(const QStringList& iconFiles) +{ + for (QString file : iconFiles) + installSkin(file, {}); +} + +void SkinList::installSkin(const QString& file, const QString& name) +{ + QFileInfo fileinfo(file); + if (!fileinfo.isReadable() || !fileinfo.isFile()) + return; + + if (fileinfo.suffix() != "png" && !SkinModel(fileinfo.absoluteFilePath()).isValid()) + return; + + QString target = FS::PathCombine(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name); + QFile::copy(file, target); +} + +int SkinList::getSkinIndex(const QString& key) const +{ + for (int i = 0; i < m_skin_list.count(); i++) { + if (m_skin_list[i].name() == key) { + return i; + } + } + return -1; +} + +const SkinModel* SkinList::skin(const QString& key) const +{ + int idx = getSkinIndex(key); + if (idx == -1) + return nullptr; + return &m_skin_list[idx]; +} + +SkinModel* SkinList::skin(const QString& key) +{ + int idx = getSkinIndex(key); + if (idx == -1) + return nullptr; + return &m_skin_list[idx]; +} + +bool SkinList::deleteSkin(const QString& key, const bool trash) +{ + int idx = getSkinIndex(key); + if (idx != -1) { + auto s = m_skin_list[idx]; + if (trash) { + if (FS::trash(s.getPath(), nullptr)) { + m_skin_list.remove(idx); + return true; + } + } else if (QFile::remove(s.getPath())) { + m_skin_list.remove(idx); + return true; + } + } + return false; +} + +void SkinList::save() +{ + QJsonObject doc; + QJsonArray arr; + for (auto s : m_skin_list) { + arr << s.toJSON(); + } + doc["skins"] = arr; + Json::write(doc, m_dir.absoluteFilePath("index.json")); +} + +int SkinList::getSelectedAccountSkin() +{ + const auto& skin = m_acct->accountData()->minecraftProfile.skin; + for (int i = 0; i < m_skin_list.count(); i++) { + if (m_skin_list[i].getURL() == skin.url) { + return i; + } + } + return -1; +} + +bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) +{ + if (!idx.isValid() || role != Qt::EditRole) { + return false; + } + + int row = idx.row(); + if (row < 0 || row >= m_skin_list.size()) + return false; + auto skin = m_skin_list[row]; + auto newName = value.toString(); + if (skin.name() != newName) { + skin.rename(newName); + save(); + } + return true; +} + +QVector SkinList::loadMinecraftSkins() +{ + QString partialPath; +#if defined(Q_OS_OSX) + partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); +#elif defined(Q_OS_WIN32) + partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); +#else + partialPath = QDir::homePath(); +#endif + QVector newSkins; + auto path = FS::PathCombine(partialPath, ".minecraft", "launcher_custom_skins.json"); + auto manifestInfo = QFileInfo(path); + if (!manifestInfo.exists()) + return {}; + try { + auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); + const auto root = doc.object(); + auto skins = Json::ensureObject(root, "customSkins"); + for (auto key : skins.keys()) { + SkinModel s(m_dir, Json::ensureObject(skins, key)); + if (s.isValid()) { + newSkins << s; + } + } + } catch (const Exception& e) { + qCritical() << "Couldn't load minecraft skins json:" << e.cause(); + } + return newSkins; +} diff --git a/launcher/minecraft/skins/SkinList.h b/launcher/minecraft/skins/SkinList.h new file mode 100644 index 000000000..8d8266d79 --- /dev/null +++ b/launcher/minecraft/skins/SkinList.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "QObjectPtr.h" +#include "SkinModel.h" +#include "minecraft/auth/MinecraftAccount.h" + +class SkinList : public QAbstractListModel { + Q_OBJECT + public: + explicit SkinList(QObject* parent, QString path, MinecraftAccountPtr acct); + virtual ~SkinList() { save(); }; + + int getSkinIndex(const QString& key) const; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex& idx, const QVariant& value, int role) override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + virtual QStringList mimeTypes() const override; + virtual Qt::DropActions supportedDropActions() const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + + bool deleteSkin(const QString& key, const bool trash); + + void installSkins(const QStringList& iconFiles); + void installSkin(const QString& file, const QString& name); + + const SkinModel* skin(const QString& key) const; + SkinModel* skin(const QString& key); + + void startWatching(); + void stopWatching(); + + QString getDir() const { return m_dir.absolutePath(); } + void save(); + int getSelectedAccountSkin(); + + private: + // hide copy constructor + SkinList(const SkinList&) = delete; + // hide assign op + SkinList& operator=(const SkinList&) = delete; + + QVector loadMinecraftSkins(); + + protected slots: + void directoryChanged(const QString& path); + void fileChanged(const QString& path); + bool update(); + + private: + shared_qobject_ptr m_watcher; + bool is_watching; + QVector m_skin_list; + QDir m_dir; + MinecraftAccountPtr m_acct; +}; \ No newline at end of file diff --git a/launcher/minecraft/skins/SkinModel.cpp b/launcher/minecraft/skins/SkinModel.cpp new file mode 100644 index 000000000..3b467019c --- /dev/null +++ b/launcher/minecraft/skins/SkinModel.cpp @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SkinModel.h" +#include +#include +#include +#include + +#include "FileSystem.h" +#include "Json.h" + +SkinModel::SkinModel(QString path) : m_path(path), m_texture(path), m_model(Model::CLASSIC) {} + +SkinModel::SkinModel(QDir skinDir, QJsonObject obj) + : m_cape_id(Json::ensureString(obj, "capeId")), m_model(Model::CLASSIC), m_url(Json::ensureString(obj, "url")) +{ + auto name = Json::ensureString(obj, "name"); + auto skinImage = Json::ensureString(obj, "skinImage"); + if (!skinImage.isEmpty()) { // minecraft skin model + skinImage = skinImage.mid(22); + m_texture.loadFromData(QByteArray::fromBase64(skinImage.toUtf8()), "PNG"); + auto textureId = Json::ensureString(obj, "textureId"); + if (name.isEmpty()) { + name = textureId; + } + if (Json::ensureBoolean(obj, "slim", false)) { + m_model = Model::SLIM; + } + } else { + if (auto model = Json::ensureString(obj, "model"); model == "SLIM") { + m_model = Model::SLIM; + } + } + m_path = skinDir.absoluteFilePath(name) + ".png"; + if (!QFileInfo(m_path).exists() && isValid()) { + m_texture.save(m_path, "PNG"); + } else { + m_texture = QPixmap(m_path); + } +} + +QString SkinModel::name() const +{ + return QFileInfo(m_path).baseName(); +} + +bool SkinModel::rename(QString newName) +{ + auto info = QFileInfo(m_path); + m_path = FS::PathCombine(info.absolutePath(), newName + ".png"); + return FS::move(info.absoluteFilePath(), m_path); +} + +QJsonObject SkinModel::toJSON() const +{ + QJsonObject obj; + obj["name"] = name(); + obj["capeId"] = m_cape_id; + obj["url"] = m_url; + obj["model"] = getModelString(); + return obj; +} + +QString SkinModel::getModelString() const +{ + switch (m_model) { + case CLASSIC: + return "CLASSIC"; + case SLIM: + return "SLIM"; + } + return {}; +} + +bool SkinModel::isValid() const +{ + return !m_texture.isNull() && (m_texture.size().height() == 32 || m_texture.size().height() == 64) && m_texture.size().width() == 64; +} + +QPixmap SkinModel::renderFrontBody() const +{ + auto isSlim = m_model == SLIM; + auto slimOffset = isSlim ? 1 : 0; + auto isOldSkin = m_texture.height() < 64; + + auto head = m_texture.copy(QRect(8, 8, 16, 16)); + auto torso = m_texture.copy(QRect(20, 20, 28, 32)); + auto rightArm = m_texture.copy(QRect(44, 20, 48 - slimOffset, 32)); + auto rightLeg = m_texture.copy(QRect(4, 20, 8, 32)); + QPixmap leftArm, leftLeg; + + if (isOldSkin) { + leftArm = rightArm.transformed(QTransform().scale(-1, 1)); + leftLeg = rightLeg.transformed(QTransform().scale(-1, 1)); + } else { + leftArm = m_texture.copy(QRect(36, 52, 40 - slimOffset, 64)); + leftLeg = m_texture.copy(QRect(20, 52, 24, 64)); + } + QPixmap output(16, 32); + output.fill(Qt::black); + QPainter p; + if (!p.begin(&output)) + return {}; + p.drawPixmap(QPoint(4, 0), head); + p.drawPixmap(QPoint(4, 8), torso); + p.drawPixmap(QPoint(12, 8), leftArm); + p.drawPixmap(QPoint(slimOffset, 8), rightArm); + p.drawPixmap(QPoint(8, 20), leftLeg); + p.drawPixmap(QPoint(4, 20), leftArm); + + return output.scaled(128, 128, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation); +} \ No newline at end of file diff --git a/launcher/minecraft/skins/SkinModel.h b/launcher/minecraft/skins/SkinModel.h new file mode 100644 index 000000000..6d135c7f7 --- /dev/null +++ b/launcher/minecraft/skins/SkinModel.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +class SkinModel { + public: + enum Model { CLASSIC, SLIM }; + + SkinModel(QString path); + SkinModel(QDir skinDir, QJsonObject obj); + virtual ~SkinModel() = default; + + QString name() const; + QString getModelString() const; + bool isValid() const; + QString getPath() const { return m_path; } + QPixmap getTexture() const { return m_texture; } + QString getCapeId() const { return m_cape_id; } + Model getModel() const { return m_model; } + QString getURL() const { return m_url; } + + bool rename(QString newName); + void setCapeId(QString capeID) { m_cape_id = capeID; } + void setModel(Model model) { m_model = model; } + void setURL(QString url) { m_url = url; } + void refresh() { m_texture = QPixmap(m_path); } + + QJsonObject toJSON() const; + + QPixmap renderFrontBody() const; + + private: + QString m_path; + QPixmap m_texture; + QString m_cape_id; + Model m_model; + QString m_url; +}; \ No newline at end of file diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp similarity index 79% rename from launcher/minecraft/services/SkinUpload.cpp rename to launcher/minecraft/skins/SkinUpload.cpp index 0400fa0f4..4e56bd7e6 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,12 +38,13 @@ #include +#include "FileSystem.h" #include "net/ByteArraySink.h" #include "net/StaticHeaderProxy.h" -SkinUpload::SkinUpload(QString token, QByteArray skin, SkinUpload::Model model) : NetRequest(), m_model(model), m_skin(skin), m_token(token) +SkinUpload::SkinUpload(QString token, SkinModel* skin) : NetRequest(), m_skin(skin), m_token(token) { - logCat = taskMCServicesLogC; + logCat = taskUploadLogC; }; QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) @@ -52,23 +54,12 @@ QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) QHttpPart skin; skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\"")); - skin.setBody(m_skin); + + skin.setBody(FS::read(m_skin->getPath())); QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - - switch (m_model) { - default: - qDebug() << "Unknown skin type!"; - emitFailed("Unknown skin type!"); - return nullptr; - case SkinUpload::STEVE: - model.setBody("CLASSIC"); - break; - case SkinUpload::ALEX: - model.setBody("SLIM"); - break; - } + model.setBody(m_skin->getModelString().toUtf8()); multiPart->append(skin); multiPart->append(model); @@ -83,10 +74,11 @@ void SkinUpload::init() })); } -SkinUpload::Ptr SkinUpload::make(QString token, QByteArray skin, SkinUpload::Model model) +SkinUpload::Ptr SkinUpload::make(QString token, SkinModel* skin) { - auto up = makeShared(token, skin, model); + auto up = makeShared(token, skin); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); + up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); return up; } diff --git a/launcher/minecraft/skins/SkinUpload.h b/launcher/minecraft/skins/SkinUpload.h new file mode 100644 index 000000000..d070f301d --- /dev/null +++ b/launcher/minecraft/skins/SkinUpload.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "minecraft/skins/SkinModel.h" +#include "net/NetRequest.h" + +class SkinUpload : public Net::NetRequest { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + + // Note this class takes ownership of the file. + SkinUpload(QString token, SkinModel* skin); + virtual ~SkinUpload() = default; + + static SkinUpload::Ptr make(QString token, SkinModel* skin); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + + private: + SkinModel* m_skin; + QString m_token; +}; diff --git a/launcher/net/Logging.cpp b/launcher/net/Logging.cpp index 45d2dcc20..cd0c88d3c 100644 --- a/launcher/net/Logging.cpp +++ b/launcher/net/Logging.cpp @@ -22,6 +22,6 @@ Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net") Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download") Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload") -Q_LOGGING_CATEGORY(taskMCServicesLogC, "launcher.task.minecraft.servicies") +Q_LOGGING_CATEGORY(taskMCSkinsLogC, "launcher.task.minecraft.skins") Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache") Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http") diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h index d3a11cdce..2536f31aa 100644 --- a/launcher/net/Logging.h +++ b/launcher/net/Logging.h @@ -24,6 +24,6 @@ Q_DECLARE_LOGGING_CATEGORY(taskNetLogC) Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC) Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC) -Q_DECLARE_LOGGING_CATEGORY(taskMCServicesLogC) +Q_DECLARE_LOGGING_CATEGORY(taskMCSkinsLogC) Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC) Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC) diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index cc63f4497..26791bc0d 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -52,8 +52,8 @@ class NetJob : public ConcurrentTask { public: using Ptr = shared_qobject_ptr; - explicit NetJob(QString job_name, shared_qobject_ptr network) - : ConcurrentTask(nullptr, job_name), m_network(network) + explicit NetJob(QString job_name, shared_qobject_ptr network, int max_concurrent = 6) + : ConcurrentTask(nullptr, job_name, max_concurrent), m_network(network) {} ~NetJob() override = default; diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index eef550e15..853873528 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -5,6 +5,7 @@ * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/NetRequest.h b/launcher/net/NetRequest.h index ee47ab2a6..917495ed9 100644 --- a/launcher/net/NetRequest.h +++ b/launcher/net/NetRequest.h @@ -4,6 +4,7 @@ * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/net/StaticHeaderProxy.h b/launcher/net/StaticHeaderProxy.h index 0e62d80ff..aabbc9c92 100644 --- a/launcher/net/StaticHeaderProxy.h +++ b/launcher/net/StaticHeaderProxy.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index a82932e08..4858e7d46 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -77,7 +77,6 @@ #include #include #include -#include #include #include #include diff --git a/launcher/ui/dialogs/ProfileSelectDialog.cpp b/launcher/ui/dialogs/ProfileSelectDialog.cpp index a62238bdb..fe03e1b6b 100644 --- a/launcher/ui/dialogs/ProfileSelectDialog.cpp +++ b/launcher/ui/dialogs/ProfileSelectDialog.cpp @@ -20,7 +20,6 @@ #include #include "Application.h" -#include "SkinUtils.h" #include "ui/dialogs/ProgressDialog.h" diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp deleted file mode 100644 index 70f1e6760..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "CustomMessageBox.h" -#include "ProgressDialog.h" -#include "SkinUploadDialog.h" -#include "ui_SkinUploadDialog.h" - -void SkinUploadDialog::on_buttonBox_rejected() -{ - close(); -} - -void SkinUploadDialog::on_buttonBox_accepted() -{ - QString fileName; - QString input = ui->skinPathTextBox->text(); - ProgressDialog prog(this); - SequentialTask skinUpload; - - if (!input.isEmpty()) { - QRegularExpression urlPrefixMatcher(QRegularExpression::anchoredPattern("^([a-z]+)://.+$")); - bool isLocalFile = false; - // it has an URL prefix -> it is an URL - if (urlPrefixMatcher.match(input).hasMatch()) { - QUrl fileURL = input; - if (fileURL.isValid()) { - // local? - if (fileURL.isLocalFile()) { - isLocalFile = true; - fileName = fileURL.toLocalFile(); - } else { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Using remote URLs for setting skins is not implemented yet."), - QMessageBox::Warning) - ->exec(); - close(); - return; - } - } else { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("You cannot use an invalid URL for uploading skins."), - QMessageBox::Warning) - ->exec(); - close(); - return; - } - } else { - // just assume it's a path then - isLocalFile = true; - fileName = ui->skinPathTextBox->text(); - } - if (isLocalFile && !QFile::exists(fileName)) { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); - close(); - return; - } - SkinUpload::Model model = SkinUpload::STEVE; - if (ui->steveBtn->isChecked()) { - model = SkinUpload::STEVE; - } else if (ui->alexBtn->isChecked()) { - model = SkinUpload::ALEX; - } - skinUpload.addTask(SkinUpload::make(m_acct->accessToken(), FS::read(fileName), model)); - } - - auto selectedCape = ui->capeCombo->currentData().toString(); - if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(CapeChange::make(m_acct->accessToken(), selectedCape)); - } - if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); - close(); - return; - } - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Success"), QMessageBox::Information)->exec(); - close(); -} - -void SkinUploadDialog::on_skinBrowseBtn_clicked() -{ - auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); - QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); - if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { - return; - } - QString cooked_path = FS::NormalizePath(raw_path); - ui->skinPathTextBox->setText(cooked_path); -} - -SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent) : QDialog(parent), m_acct(acct), ui(new Ui::SkinUploadDialog) -{ - ui->setupUi(this); - - // FIXME: add a model for this, download/refresh the capes on demand - auto& accountData = *acct->accountData(); - int index = 0; - ui->capeCombo->addItem(tr("No Cape"), QVariant()); - auto currentCape = accountData.minecraftProfile.currentCape; - if (currentCape.isEmpty()) { - ui->capeCombo->setCurrentIndex(index); - } - - for (auto& cape : accountData.minecraftProfile.capes) { - index++; - if (cape.data.size()) { - QPixmap capeImage; - if (capeImage.loadFromData(cape.data, "PNG")) { - QPixmap preview = QPixmap(10, 16); - QPainter painter(&preview); - painter.drawPixmap(0, 0, capeImage.copy(1, 1, 10, 16)); - ui->capeCombo->addItem(capeImage, cape.alias, cape.id); - if (currentCape == cape.id) { - ui->capeCombo->setCurrentIndex(index); - } - continue; - } - } - ui->capeCombo->addItem(cape.alias, cape.id); - if (currentCape == cape.id) { - ui->capeCombo->setCurrentIndex(index); - } - } -} diff --git a/launcher/ui/dialogs/SkinUploadDialog.h b/launcher/ui/dialogs/SkinUploadDialog.h deleted file mode 100644 index 81d6140cc..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -namespace Ui { -class SkinUploadDialog; -} - -class SkinUploadDialog : public QDialog { - Q_OBJECT - public: - explicit SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent = 0); - virtual ~SkinUploadDialog(){}; - - public slots: - void on_buttonBox_accepted(); - - void on_buttonBox_rejected(); - - void on_skinBrowseBtn_clicked(); - - protected: - MinecraftAccountPtr m_acct; - - private: - Ui::SkinUploadDialog* ui; -}; diff --git a/launcher/ui/dialogs/SkinUploadDialog.ui b/launcher/ui/dialogs/SkinUploadDialog.ui deleted file mode 100644 index c6df92df3..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.ui +++ /dev/null @@ -1,95 +0,0 @@ - - - SkinUploadDialog - - - - 0 - 0 - 394 - 360 - - - - Skin Upload - - - - - - Skin File - - - - - - Leave empty to keep current skin - - - - - - - - 0 - 0 - - - - Browse - - - - - - - - - - Player Model - - - - - - Steve Model - - - true - - - - - - - Alex Model - - - - - - - - - - Cape - - - - - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp new file mode 100644 index 000000000..1ba7e7055 --- /dev/null +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Application.h" +#include "DesktopServices.h" +#include "QObjectPtr.h" +#include "SkinManageDialog.h" + +#include "minecraft/auth/AccountTask.h" +#include "minecraft/skins/CapeChange.h" +#include "minecraft/skins/SkinDelete.h" +#include "minecraft/skins/SkinList.h" +#include "minecraft/skins/SkinModel.h" +#include "minecraft/skins/SkinUpload.h" + +#include "net/NetJob.h" +#include "tasks/Task.h" + +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/instanceview/InstanceDelegate.h" +#include "ui_SkinManageDialog.h" + +SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct) + : QDialog(parent), m_acct(acct), ui(new Ui::SkinManageDialog), m_list(this, APPLICATION->settings()->get("SkinsDir").toString(), acct) +{ + ui->setupUi(this); + + setWindowModality(Qt::WindowModal); + + auto contentsWidget = ui->listView; + contentsWidget->setViewMode(QListView::IconMode); + contentsWidget->setFlow(QListView::LeftToRight); + contentsWidget->setIconSize(QSize(48, 48)); + contentsWidget->setMovement(QListView::Static); + contentsWidget->setResizeMode(QListView::Adjust); + contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection); + contentsWidget->setSpacing(5); + contentsWidget->setWordWrap(false); + contentsWidget->setWrapping(true); + contentsWidget->setUniformItemSizes(true); + contentsWidget->setTextElideMode(Qt::ElideRight); + contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + contentsWidget->installEventFilter(this); + contentsWidget->setItemDelegate(new ListViewDelegate()); + + contentsWidget->setAcceptDrops(true); + contentsWidget->setDropIndicatorShown(true); + contentsWidget->viewport()->setAcceptDrops(true); + contentsWidget->setDragDropMode(QAbstractItemView::DropOnly); + contentsWidget->setDefaultDropAction(Qt::CopyAction); + + contentsWidget->installEventFilter(this); + contentsWidget->setModel(&m_list); + + connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex))); + + connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SLOT(selectionChanged(QItemSelection, QItemSelection))); + connect(ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu); + + setupCapes(); + + ui->listView->setCurrentIndex(m_list.index(m_list.getSelectedAccountSkin())); +} + +SkinManageDialog::~SkinManageDialog() +{ + delete ui; +} + +void SkinManageDialog::activated(QModelIndex index) +{ + m_selected_skin = index.data(Qt::UserRole).toString(); + accept(); +} + +void SkinManageDialog::selectionChanged(QItemSelection selected, QItemSelection deselected) +{ + if (selected.empty()) + return; + + QString key = selected.first().indexes().first().data(Qt::UserRole).toString(); + if (key.isEmpty()) + return; + m_selected_skin = key; + auto skin = m_list.skin(key); + if (!skin) + return; + ui->selectedModel->setPixmap(skin->getTexture().scaled(128, 128, Qt::KeepAspectRatio, Qt::FastTransformation)); + ui->capeCombo->setCurrentIndex(m_capes_idx.value(skin->getCapeId())); + ui->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC); + ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM); +} + +void SkinManageDialog::delayed_scroll(QModelIndex model_index) +{ + auto contentsWidget = ui->listView; + contentsWidget->scrollTo(model_index); +} + +void SkinManageDialog::on_openDirBtn_clicked() +{ + DesktopServices::openDirectory(m_list.getDir(), true); +} + +void SkinManageDialog::on_addBtn_clicked() +{ + auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); + QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); + if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { + return; + } + if (!SkinModel(raw_path).isValid()) { + CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), + tr("Skin images must be 64x64 or 64x32 pixel PNG files."), QMessageBox::Critical) + ->show(); + return; + } + m_list.installSkin(raw_path, {}); +} + +QPixmap previewCape(QPixmap capeImage) +{ + QPixmap preview = QPixmap(10, 16); + QPainter painter(&preview); + painter.drawPixmap(0, 0, capeImage.copy(1, 1, 10, 16)); + return preview.scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation); +} + +void SkinManageDialog::setupCapes() +{ + // FIXME: add a model for this, download/refresh the capes on demand + auto& accountData = *m_acct->accountData(); + int index = 0; + ui->capeCombo->addItem(tr("No Cape"), QVariant()); + auto currentCape = accountData.minecraftProfile.currentCape; + if (currentCape.isEmpty()) { + ui->capeCombo->setCurrentIndex(index); + } + + auto capesDir = FS::PathCombine(m_list.getDir(), "capes"); + NetJob::Ptr job{ new NetJob(tr("Download capes"), APPLICATION->network()) }; + bool needsToDownload = false; + for (auto& cape : accountData.minecraftProfile.capes) { + auto path = FS::PathCombine(capesDir, cape.id + ".png"); + if (cape.data.size()) { + QPixmap capeImage; + if (capeImage.loadFromData(cape.data, "PNG") && capeImage.save(path)) { + m_capes[cape.id] = previewCape(capeImage); + continue; + } + } + if (QFileInfo(path).exists()) { + continue; + } + if (!cape.url.isEmpty()) { + needsToDownload = true; + job->addNetAction(Net::Download::makeFile(cape.url, path)); + } + } + if (needsToDownload) { + ProgressDialog dlg(this); + dlg.execWithTask(job.get()); + } + for (auto& cape : accountData.minecraftProfile.capes) { + index++; + QPixmap capeImage; + if (!m_capes.contains(cape.id)) { + auto path = FS::PathCombine(capesDir, cape.id + ".png"); + if (QFileInfo(path).exists() && capeImage.load(path)) { + capeImage = previewCape(capeImage); + m_capes[cape.id] = capeImage; + } + } + if (!capeImage.isNull()) { + ui->capeCombo->addItem(capeImage, cape.alias, cape.id); + } else { + ui->capeCombo->addItem(cape.alias, cape.id); + } + + m_capes_idx[cape.id] = index; + } +} + +void SkinManageDialog::on_capeCombo_currentIndexChanged(int index) +{ + auto id = ui->capeCombo->currentData(); + ui->capeImage->setPixmap(m_capes.value(id.toString(), {})); + if (auto skin = m_list.skin(m_selected_skin); skin) { + skin->setCapeId(id.toString()); + } +} + +void SkinManageDialog::on_steveBtn_toggled(bool checked) +{ + if (auto skin = m_list.skin(m_selected_skin); skin) { + skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM); + } +} + +void SkinManageDialog::accept() +{ + auto skin = m_list.skin(m_selected_skin); + if (!skin) + reject(); + auto path = skin->getPath(); + + ProgressDialog prog(this); + NetJob::Ptr skinUpload{ new NetJob(tr("Change skin"), APPLICATION->network(), 1) }; + + if (!QFile::exists(path)) { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); + reject(); + return; + } + + skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin)); + + auto selectedCape = skin->getCapeId(); + if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { + skinUpload->addNetAction(CapeChange::make(m_acct->accessToken(), selectedCape)); + } + + skinUpload->addTask(m_acct->refresh().staticCast()); + if (prog.execWithTask(skinUpload.get()) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); + reject(); + return; + } + skin->setURL(m_acct->accountData()->minecraftProfile.skin.url); + QDialog::accept(); +} + +void SkinManageDialog::on_resetBtn_clicked() +{ + ProgressDialog prog(this); + NetJob::Ptr skinReset{ new NetJob(tr("Reset skin"), APPLICATION->network(), 1) }; + skinReset->addNetAction(SkinDelete::make(m_acct->accessToken())); + skinReset->addTask(m_acct->refresh().staticCast()); + if (prog.execWithTask(skinReset.get()) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); + reject(); + return; + } + QDialog::accept(); +} + +void SkinManageDialog::show_context_menu(const QPoint& pos) +{ + QMenu myMenu(tr("Context menu"), this); + myMenu.addAction(ui->action_Rename_Skin); + myMenu.addAction(ui->action_Delete_Skin); + + myMenu.exec(ui->listView->mapToGlobal(pos)); +} + +bool SkinManageDialog::eventFilter(QObject* obj, QEvent* ev) +{ + if (obj == ui->listView) { + if (ev->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(ev); + switch (keyEvent->key()) { + case Qt::Key_Delete: + on_action_Delete_Skin_triggered(false); + return true; + case Qt::Key_F2: + on_action_Rename_Skin_triggered(false); + return true; + default: + break; + } + } + } + return QDialog::eventFilter(obj, ev); +} + +void SkinManageDialog::on_action_Rename_Skin_triggered(bool checked) +{ + if (!m_selected_skin.isEmpty()) { + ui->listView->edit(ui->listView->currentIndex()); + } +} + +void SkinManageDialog::on_action_Delete_Skin_triggered(bool checked) +{ + if (m_selected_skin.isEmpty()) + return; + + if (m_list.getSkinIndex(m_selected_skin) == m_list.getSelectedAccountSkin()) { + CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning); + return; + } + + auto skin = m_list.skin(m_selected_skin); + if (!skin) + return; + + auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"), + tr("You are about to delete \"%1\".\n" + "Are you sure?") + .arg(skin->name()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) { + if (!m_list.deleteSkin(m_selected_skin, true)) { + m_list.deleteSkin(m_selected_skin, false); + } + } +} diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.h b/launcher/ui/dialogs/skins/SkinManageDialog.h new file mode 100644 index 000000000..8c55c3310 --- /dev/null +++ b/launcher/ui/dialogs/skins/SkinManageDialog.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "minecraft/auth/MinecraftAccount.h" +#include "minecraft/skins/SkinList.h" + +namespace Ui { +class SkinManageDialog; +} + +class SkinManageDialog : public QDialog { + Q_OBJECT + public: + explicit SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct); + virtual ~SkinManageDialog(); + + public slots: + void selectionChanged(QItemSelection, QItemSelection); + void activated(QModelIndex); + void delayed_scroll(QModelIndex); + void on_openDirBtn_clicked(); + void on_addBtn_clicked(); + void accept() override; + void on_capeCombo_currentIndexChanged(int index); + void on_steveBtn_toggled(bool checked); + void on_resetBtn_clicked(); + void show_context_menu(const QPoint& pos); + bool eventFilter(QObject* obj, QEvent* ev) override; + void on_action_Rename_Skin_triggered(bool checked); + void on_action_Delete_Skin_triggered(bool checked); + + private: + void setupCapes(); + + MinecraftAccountPtr m_acct; + Ui::SkinManageDialog* ui; + SkinList m_list; + QString m_selected_skin; + QHash m_capes; + QHash m_capes_idx; +}; diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.ui b/launcher/ui/dialogs/skins/SkinManageDialog.ui new file mode 100644 index 000000000..6ad826478 --- /dev/null +++ b/launcher/ui/dialogs/skins/SkinManageDialog.ui @@ -0,0 +1,193 @@ + + + SkinManageDialog + + + + 0 + 0 + 968 + 757 + + + + Skin Upload + + + + + + + + + + + + + true + + + + + + + + 0 + 0 + + + + Model + + + + + + Clasic + + + true + + + + + + + Slim + + + + + + + + + + Cape + + + + + + + + + + + + true + + + + + + + + + + + + Qt::CustomContextMenu + + + false + + + 0 + + + + + + + + + + + Open Folder + + + + + + + Import Skin + + + + + + + Reset Skin + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + &Delete Skin + + + Deletes selected skin + + + Del + + + + + &Rename Skin + + + Rename selected skin + + + F2 + + + + + + + buttonBox + rejected() + SkinManageDialog + reject() + + + 617 + 736 + + + 483 + 378 + + + + + buttonBox + accepted() + SkinManageDialog + accept() + + + 617 + 736 + + + 483 + 378 + + + + + diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 3dcf05e0a..25ccfd0d7 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -35,6 +35,7 @@ */ #include "AccountListPage.h" +#include "ui/dialogs/skins/SkinManageDialog.h" #include "ui_AccountListPage.h" #include @@ -42,23 +43,13 @@ #include -#include "net/NetJob.h" - #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/LoginDialog.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/OfflineLoginDialog.h" -#include "ui/dialogs/ProgressDialog.h" -#include "ui/dialogs/SkinUploadDialog.h" - -#include "minecraft/auth/AccountTask.h" -#include "minecraft/services/SkinDelete.h" -#include "tasks/Task.h" #include "Application.h" -#include "BuildConfig.h" - AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage) { ui->setupUi(this); @@ -235,8 +226,7 @@ void AccountListPage::updateButtonStates() } ui->actionRemove->setEnabled(accountIsReady); ui->actionSetDefault->setEnabled(accountIsReady); - ui->actionUploadSkin->setEnabled(accountIsReady && accountIsOnline); - ui->actionDeleteSkin->setEnabled(accountIsReady && accountIsOnline); + ui->actionManageSkins->setEnabled(accountIsReady && accountIsOnline); ui->actionRefresh->setEnabled(accountIsReady && accountIsOnline); if (m_accounts->defaultAccount().get() == nullptr) { @@ -248,29 +238,13 @@ void AccountListPage::updateButtonStates() } } -void AccountListPage::on_actionUploadSkin_triggered() +void AccountListPage::on_actionManageSkins_triggered() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); if (selection.size() > 0) { QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); - SkinUploadDialog dialog(account, this); + SkinManageDialog dialog(this, account); dialog.exec(); } } - -void AccountListPage::on_actionDeleteSkin_triggered() -{ - QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); - if (selection.size() <= 0) - return; - - QModelIndex selected = selection.first(); - MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); - ProgressDialog prog(this); - auto deleteSkinTask = SkinDelete::make(account->accessToken()); - if (prog.execWithTask((Task*)deleteSkinTask.get()) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); - return; - } -} diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h index add0f4aa0..64702cff7 100644 --- a/launcher/ui/pages/global/AccountListPage.h +++ b/launcher/ui/pages/global/AccountListPage.h @@ -77,8 +77,7 @@ class AccountListPage : public QMainWindow, public BasePage { void on_actionRefresh_triggered(); void on_actionSetDefault_triggered(); void on_actionNoDefault_triggered(); - void on_actionUploadSkin_triggered(); - void on_actionDeleteSkin_triggered(); + void on_actionManageSkins_triggered(); void listChanged(); diff --git a/launcher/ui/pages/global/AccountListPage.ui b/launcher/ui/pages/global/AccountListPage.ui index 469955b51..0e73f8c52 100644 --- a/launcher/ui/pages/global/AccountListPage.ui +++ b/launcher/ui/pages/global/AccountListPage.ui @@ -60,19 +60,13 @@ - - + Add &Mojang - - - Remo&ve - - &Set Default @@ -86,17 +80,12 @@ &No Default - + - &Upload Skin - - - - - &Delete Skin + &Manage Skins - Delete the currently active skin and go back to the default one + Manage Skins @@ -117,6 +106,11 @@ Refresh the account tokens + + + Remo&ve + +
From 8bad255a9191cd76808a73942da366c981643d35 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 5 Sep 2023 20:13:16 +0300 Subject: [PATCH 041/755] added more import options Signed-off-by: Trial97 --- launcher/minecraft/skins/CapeChange.cpp | 2 +- launcher/minecraft/skins/SkinDelete.cpp | 2 +- launcher/minecraft/skins/SkinList.cpp | 74 ++++---- launcher/minecraft/skins/SkinList.h | 6 +- launcher/minecraft/skins/SkinModel.cpp | 58 +------ launcher/minecraft/skins/SkinModel.h | 3 +- launcher/minecraft/skins/SkinUpload.cpp | 4 +- launcher/net/NetAction.h | 1 + .../ui/dialogs/skins/SkinManageDialog.cpp | 158 ++++++++++++++++-- launcher/ui/dialogs/skins/SkinManageDialog.h | 4 +- launcher/ui/dialogs/skins/SkinManageDialog.ui | 43 ++++- 11 files changed, 229 insertions(+), 126 deletions(-) diff --git a/launcher/minecraft/skins/CapeChange.cpp b/launcher/minecraft/skins/CapeChange.cpp index 863e89844..4db28e245 100644 --- a/launcher/minecraft/skins/CapeChange.cpp +++ b/launcher/minecraft/skins/CapeChange.cpp @@ -44,7 +44,7 @@ CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token) { logCat = taskMCSkinsLogC; -}; +} QNetworkReply* CapeChange::getReply(QNetworkRequest& request) { diff --git a/launcher/minecraft/skins/SkinDelete.cpp b/launcher/minecraft/skins/SkinDelete.cpp index 982cac1b7..3c50cf313 100644 --- a/launcher/minecraft/skins/SkinDelete.cpp +++ b/launcher/minecraft/skins/SkinDelete.cpp @@ -42,7 +42,7 @@ SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token) { logCat = taskMCSkinsLogC; -}; +} QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) { diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index be329564b..15d0b0a8e 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -85,8 +85,6 @@ bool SkinList::update() } catch (const Exception& e) { qCritical() << "Couldn't load skins json:" << e.cause(); } - } else { - newSkins = loadMinecraftSkins(); } bool needsSave = false; @@ -108,7 +106,7 @@ bool SkinList::update() auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); - s.setModel(SkinModel::CLASSIC); // maybe better model detection + s.setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; @@ -116,6 +114,7 @@ bool SkinList::update() } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); + nskin->setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); } } @@ -207,14 +206,14 @@ bool SkinList::dropMimeData(const QMimeData* data, // files dropped from outside? if (data->hasUrls()) { auto urls = data->urls(); - QStringList iconFiles; + QStringList skinFiles; for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; - iconFiles += url.toLocalFile(); + skinFiles << url.toLocalFile(); } - installSkins(iconFiles); + installSkins(skinFiles); return true; } return false; @@ -261,20 +260,26 @@ int SkinList::rowCount(const QModelIndex& parent) const void SkinList::installSkins(const QStringList& iconFiles) { for (QString file : iconFiles) - installSkin(file, {}); + installSkin(file); } -void SkinList::installSkin(const QString& file, const QString& name) +QString SkinList::installSkin(const QString& file, const QString& name) { + if (file.isEmpty()) + return tr("Path is empty."); QFileInfo fileinfo(file); - if (!fileinfo.isReadable() || !fileinfo.isFile()) - return; - + if (!fileinfo.exists()) + return tr("File doesn't exist."); + if (!fileinfo.isFile()) + return tr("Not a file."); + if (!fileinfo.isReadable()) + return tr("File is not readable."); if (fileinfo.suffix() != "png" && !SkinModel(fileinfo.absoluteFilePath()).isValid()) - return; + return tr("Skin images must be 64x64 or 64x32 pixel PNG files."); QString target = FS::PathCombine(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name); - QFile::copy(file, target); + + return QFile::copy(file, target) ? "" : tr("Unable to copy file"); } int SkinList::getSkinIndex(const QString& key) const @@ -311,10 +316,12 @@ bool SkinList::deleteSkin(const QString& key, const bool trash) if (trash) { if (FS::trash(s.getPath(), nullptr)) { m_skin_list.remove(idx); + save(); return true; } } else if (QFile::remove(s.getPath())) { m_skin_list.remove(idx); + save(); return true; } } @@ -361,33 +368,22 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) return true; } -QVector SkinList::loadMinecraftSkins() +void SkinList::updateSkin(SkinModel s) { - QString partialPath; -#if defined(Q_OS_OSX) - partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); -#elif defined(Q_OS_WIN32) - partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); -#else - partialPath = QDir::homePath(); -#endif - QVector newSkins; - auto path = FS::PathCombine(partialPath, ".minecraft", "launcher_custom_skins.json"); - auto manifestInfo = QFileInfo(path); - if (!manifestInfo.exists()) - return {}; - try { - auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); - const auto root = doc.object(); - auto skins = Json::ensureObject(root, "customSkins"); - for (auto key : skins.keys()) { - SkinModel s(m_dir, Json::ensureObject(skins, key)); - if (s.isValid()) { - newSkins << s; - } + auto done = false; + for (auto i = 0; i < m_skin_list.size(); i++) { + if (m_skin_list[i].getPath() == s.getPath()) { + m_skin_list[i].setCapeId(s.getCapeId()); + m_skin_list[i].setModel(s.getModel()); + m_skin_list[i].setURL(s.getURL()); + done = true; + break; } - } catch (const Exception& e) { - qCritical() << "Couldn't load minecraft skins json:" << e.cause(); } - return newSkins; + if (!done) { + beginInsertRows(QModelIndex(), m_skin_list.count(), m_skin_list.count() + 1); + m_skin_list.append(s); + endInsertRows(); + } + save(); } diff --git a/launcher/minecraft/skins/SkinList.h b/launcher/minecraft/skins/SkinList.h index 8d8266d79..b6981e1b4 100644 --- a/launcher/minecraft/skins/SkinList.h +++ b/launcher/minecraft/skins/SkinList.h @@ -46,7 +46,7 @@ class SkinList : public QAbstractListModel { bool deleteSkin(const QString& key, const bool trash); void installSkins(const QStringList& iconFiles); - void installSkin(const QString& file, const QString& name); + QString installSkin(const QString& file, const QString& name = {}); const SkinModel* skin(const QString& key) const; SkinModel* skin(const QString& key); @@ -58,14 +58,14 @@ class SkinList : public QAbstractListModel { void save(); int getSelectedAccountSkin(); + void updateSkin(SkinModel s); + private: // hide copy constructor SkinList(const SkinList&) = delete; // hide assign op SkinList& operator=(const SkinList&) = delete; - QVector loadMinecraftSkins(); - protected slots: void directoryChanged(const QString& path); void fileChanged(const QString& path); diff --git a/launcher/minecraft/skins/SkinModel.cpp b/launcher/minecraft/skins/SkinModel.cpp index 3b467019c..d53b9e762 100644 --- a/launcher/minecraft/skins/SkinModel.cpp +++ b/launcher/minecraft/skins/SkinModel.cpp @@ -31,28 +31,12 @@ SkinModel::SkinModel(QDir skinDir, QJsonObject obj) : m_cape_id(Json::ensureString(obj, "capeId")), m_model(Model::CLASSIC), m_url(Json::ensureString(obj, "url")) { auto name = Json::ensureString(obj, "name"); - auto skinImage = Json::ensureString(obj, "skinImage"); - if (!skinImage.isEmpty()) { // minecraft skin model - skinImage = skinImage.mid(22); - m_texture.loadFromData(QByteArray::fromBase64(skinImage.toUtf8()), "PNG"); - auto textureId = Json::ensureString(obj, "textureId"); - if (name.isEmpty()) { - name = textureId; - } - if (Json::ensureBoolean(obj, "slim", false)) { - m_model = Model::SLIM; - } - } else { - if (auto model = Json::ensureString(obj, "model"); model == "SLIM") { - m_model = Model::SLIM; - } + + if (auto model = Json::ensureString(obj, "model"); model == "SLIM") { + m_model = Model::SLIM; } m_path = skinDir.absoluteFilePath(name) + ".png"; - if (!QFileInfo(m_path).exists() && isValid()) { - m_texture.save(m_path, "PNG"); - } else { - m_texture = QPixmap(m_path); - } + m_texture = QPixmap(m_path); } QString SkinModel::name() const @@ -92,37 +76,3 @@ bool SkinModel::isValid() const { return !m_texture.isNull() && (m_texture.size().height() == 32 || m_texture.size().height() == 64) && m_texture.size().width() == 64; } - -QPixmap SkinModel::renderFrontBody() const -{ - auto isSlim = m_model == SLIM; - auto slimOffset = isSlim ? 1 : 0; - auto isOldSkin = m_texture.height() < 64; - - auto head = m_texture.copy(QRect(8, 8, 16, 16)); - auto torso = m_texture.copy(QRect(20, 20, 28, 32)); - auto rightArm = m_texture.copy(QRect(44, 20, 48 - slimOffset, 32)); - auto rightLeg = m_texture.copy(QRect(4, 20, 8, 32)); - QPixmap leftArm, leftLeg; - - if (isOldSkin) { - leftArm = rightArm.transformed(QTransform().scale(-1, 1)); - leftLeg = rightLeg.transformed(QTransform().scale(-1, 1)); - } else { - leftArm = m_texture.copy(QRect(36, 52, 40 - slimOffset, 64)); - leftLeg = m_texture.copy(QRect(20, 52, 24, 64)); - } - QPixmap output(16, 32); - output.fill(Qt::black); - QPainter p; - if (!p.begin(&output)) - return {}; - p.drawPixmap(QPoint(4, 0), head); - p.drawPixmap(QPoint(4, 8), torso); - p.drawPixmap(QPoint(12, 8), leftArm); - p.drawPixmap(QPoint(slimOffset, 8), rightArm); - p.drawPixmap(QPoint(8, 20), leftLeg); - p.drawPixmap(QPoint(4, 20), leftArm); - - return output.scaled(128, 128, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation); -} \ No newline at end of file diff --git a/launcher/minecraft/skins/SkinModel.h b/launcher/minecraft/skins/SkinModel.h index 6d135c7f7..46e9d6cf1 100644 --- a/launcher/minecraft/skins/SkinModel.h +++ b/launcher/minecraft/skins/SkinModel.h @@ -26,6 +26,7 @@ class SkinModel { public: enum Model { CLASSIC, SLIM }; + SkinModel() = default; SkinModel(QString path); SkinModel(QDir skinDir, QJsonObject obj); virtual ~SkinModel() = default; @@ -47,8 +48,6 @@ class SkinModel { QJsonObject toJSON() const; - QPixmap renderFrontBody() const; - private: QString m_path; QPixmap m_texture; diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index 4e56bd7e6..4496f3f1c 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -44,8 +44,8 @@ SkinUpload::SkinUpload(QString token, SkinModel* skin) : NetRequest(), m_skin(skin), m_token(token) { - logCat = taskUploadLogC; -}; + logCat = taskMCSkinsLogC; +} QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index b66b91941..6440d38b9 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -55,6 +55,7 @@ class NetAction : public Task { virtual ~NetAction() = default; QUrl url() { return m_url; } + void setUrl(QUrl url) { m_url = url; } void setNetwork(shared_qobject_ptr network) { m_network = network; } diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 1ba7e7055..0afb6cbc4 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -16,37 +16,41 @@ * along with this program. If not, see . */ -#include -#include -#include -#include +#include "SkinManageDialog.h" +#include "ui_SkinManageDialog.h" #include #include #include +#include +#include #include #include #include +#include +#include +#include #include "Application.h" #include "DesktopServices.h" +#include "Json.h" #include "QObjectPtr.h" -#include "SkinManageDialog.h" #include "minecraft/auth/AccountTask.h" +#include "minecraft/auth/Parsers.h" #include "minecraft/skins/CapeChange.h" #include "minecraft/skins/SkinDelete.h" #include "minecraft/skins/SkinList.h" #include "minecraft/skins/SkinModel.h" #include "minecraft/skins/SkinUpload.h" +#include "net/Download.h" #include "net/NetJob.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/InstanceDelegate.h" -#include "ui_SkinManageDialog.h" SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct) : QDialog(parent), m_acct(acct), ui(new Ui::SkinManageDialog), m_list(this, APPLICATION->settings()->get("SkinsDir").toString(), acct) @@ -132,20 +136,15 @@ void SkinManageDialog::on_openDirBtn_clicked() DesktopServices::openDirectory(m_list.getDir(), true); } -void SkinManageDialog::on_addBtn_clicked() +void SkinManageDialog::on_fileBtn_clicked() { auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); - if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { + auto message = m_list.installSkin(raw_path, {}); + if (!message.isEmpty()) { + CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), message, QMessageBox::Critical)->show(); return; } - if (!SkinModel(raw_path).isValid()) { - CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), - tr("Skin images must be 64x64 or 64x32 pixel PNG files."), QMessageBox::Critical) - ->show(); - return; - } - m_list.installSkin(raw_path, {}); } QPixmap previewCape(QPixmap capeImage) @@ -337,3 +336,132 @@ void SkinManageDialog::on_action_Delete_Skin_triggered(bool checked) } } } + +void SkinManageDialog::on_urlBtn_clicked() +{ + auto url = QUrl(ui->urlLine->text()); + if (!url.isValid()) { + CustomMessageBox::selectable(this, tr("Invalid url"), tr("Invalid url"), QMessageBox::Critical)->show(); + return; + } + ui->urlLine->setText(""); + + NetJob::Ptr job{ new NetJob(tr("Download skin"), APPLICATION->network()) }; + + auto path = FS::PathCombine(m_list.getDir(), url.fileName()); + job->addNetAction(Net::Download::makeFile(url, path)); + ProgressDialog dlg(this); + dlg.execWithTask(job.get()); + SkinModel s(path); + if (!s.isValid()) { + CustomMessageBox::selectable(this, tr("URL is not a valid skin"), tr("Skin images must be 64x64 or 64x32 pixel PNG files."), + QMessageBox::Critical) + ->show(); + QFile::remove(path); + return; + } + if (QFileInfo(path).suffix().isEmpty()) { + QFile::rename(path, path + ".png"); + } +} + +class WaitTask : public Task { + public: + WaitTask() : m_loop(), m_done(false){}; + virtual ~WaitTask() = default; + + public slots: + void quit() + { + m_done = true; + m_loop.quit(); + } + + protected: + virtual void executeTask() + { + if (!m_done) + m_loop.exec(); + emitSucceeded(); + }; + + private: + QEventLoop m_loop; + bool m_done; +}; + +void SkinManageDialog::on_userBtn_clicked() +{ + auto user = ui->urlLine->text(); + if (user.isEmpty()) { + return; + } + ui->urlLine->setText(""); + MinecraftProfile mcProfile; + auto path = FS::PathCombine(m_list.getDir(), user + ".png"); + + NetJob::Ptr job{ new NetJob(tr("Download user skin"), APPLICATION->network(), 1) }; + + auto uuidOut = std::make_shared(); + auto profileOut = std::make_shared(); + + auto uuidLoop = makeShared(); + auto profileLoop = makeShared(); + + auto getUUID = Net::Download::makeByteArray("https://api.mojang.com/users/profiles/minecraft/" + user, uuidOut); + auto getProfile = Net::Download::makeByteArray(QUrl(), profileOut); + auto downloadSkin = Net::Download::makeFile(QUrl(), path); + + connect(getUUID.get(), &Task::aborted, uuidLoop.get(), &WaitTask::quit); + connect(getUUID.get(), &Task::failed, uuidLoop.get(), &WaitTask::quit); + connect(getProfile.get(), &Task::aborted, profileLoop.get(), &WaitTask::quit); + connect(getProfile.get(), &Task::failed, profileLoop.get(), &WaitTask::quit); + + connect(getUUID.get(), &Task::succeeded, this, [uuidLoop, uuidOut, job, getProfile] { + try { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*uuidOut, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Minecraft skin service at " << parse_error.offset + << " reason: " << parse_error.errorString(); + uuidLoop->quit(); + return; + } + const auto root = doc.object(); + auto id = Json::ensureString(root, "id"); + if (!id.isEmpty()) { + getProfile->setUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + id); + } else { + job->abort(); + } + } catch (const Exception& e) { + qCritical() << "Couldn't load skin json:" << e.cause(); + } + uuidLoop->quit(); + }); + + connect(getProfile.get(), &Task::succeeded, this, [profileLoop, profileOut, job, getProfile, &mcProfile, downloadSkin] { + if (Parsers::parseMinecraftProfileMojang(*profileOut, mcProfile)) { + downloadSkin->setUrl(mcProfile.skin.url); + } else { + job->abort(); + } + profileLoop->quit(); + }); + + job->addNetAction(getUUID); + job->addTask(uuidLoop); + job->addNetAction(getProfile); + job->addTask(profileLoop); + job->addNetAction(downloadSkin); + ProgressDialog dlg(this); + dlg.execWithTask(job.get()); + + SkinModel s(path); + s.setModel(mcProfile.skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setURL(mcProfile.skin.url); + if (m_capes.contains(mcProfile.currentCape)) { + s.setCapeId(mcProfile.currentCape); + } + m_list.updateSkin(s); +} \ No newline at end of file diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.h b/launcher/ui/dialogs/skins/SkinManageDialog.h index 8c55c3310..ce8fc9348 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.h +++ b/launcher/ui/dialogs/skins/SkinManageDialog.h @@ -40,7 +40,9 @@ class SkinManageDialog : public QDialog { void activated(QModelIndex); void delayed_scroll(QModelIndex); void on_openDirBtn_clicked(); - void on_addBtn_clicked(); + void on_fileBtn_clicked(); + void on_urlBtn_clicked(); + void on_userBtn_clicked(); void accept() override; void on_capeCombo_currentIndexChanged(int index); void on_steveBtn_toggled(bool checked); diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.ui b/launcher/ui/dialogs/skins/SkinManageDialog.ui index 6ad826478..c2ce9143c 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.ui +++ b/launcher/ui/dialogs/skins/SkinManageDialog.ui @@ -100,7 +100,7 @@
- + @@ -108,13 +108,6 @@ - - - - Import Skin - - - @@ -122,8 +115,42 @@ + + + + + + + + + + + Import URL + + + + + + + Import user + + + + + + + Import File + + + + + + 0 + 0 + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok From 8c8e4329d72afb1b5141725127c883b52513761b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 5 Sep 2023 23:45:32 +0300 Subject: [PATCH 042/755] fix codeql Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 12 ++++++------ launcher/minecraft/skins/SkinList.h | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index 15d0b0a8e..b3a593454 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -368,21 +368,21 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) return true; } -void SkinList::updateSkin(SkinModel s) +void SkinList::updateSkin(SkinModel* s) { auto done = false; for (auto i = 0; i < m_skin_list.size(); i++) { - if (m_skin_list[i].getPath() == s.getPath()) { - m_skin_list[i].setCapeId(s.getCapeId()); - m_skin_list[i].setModel(s.getModel()); - m_skin_list[i].setURL(s.getURL()); + if (m_skin_list[i].getPath() == s->getPath()) { + m_skin_list[i].setCapeId(s->getCapeId()); + m_skin_list[i].setModel(s->getModel()); + m_skin_list[i].setURL(s->getURL()); done = true; break; } } if (!done) { beginInsertRows(QModelIndex(), m_skin_list.count(), m_skin_list.count() + 1); - m_skin_list.append(s); + m_skin_list.append(*s); endInsertRows(); } save(); diff --git a/launcher/minecraft/skins/SkinList.h b/launcher/minecraft/skins/SkinList.h index b6981e1b4..66af6a17b 100644 --- a/launcher/minecraft/skins/SkinList.h +++ b/launcher/minecraft/skins/SkinList.h @@ -58,7 +58,7 @@ class SkinList : public QAbstractListModel { void save(); int getSelectedAccountSkin(); - void updateSkin(SkinModel s); + void updateSkin(SkinModel* s); private: // hide copy constructor diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 0afb6cbc4..4ef91a2bf 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -463,5 +463,5 @@ void SkinManageDialog::on_userBtn_clicked() if (m_capes.contains(mcProfile.currentCape)) { s.setCapeId(mcProfile.currentCape); } - m_list.updateSkin(s); + m_list.updateSkin(&s); } \ No newline at end of file From 6ec1cf6e4933013e04acb9086f3fcf4a844d181a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 8 Sep 2023 19:50:46 +0300 Subject: [PATCH 043/755] made skin upload more generic Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinUpload.cpp | 10 +++++----- launcher/minecraft/skins/SkinUpload.h | 8 ++++---- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index 4496f3f1c..4a88faedf 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -42,7 +42,7 @@ #include "net/ByteArraySink.h" #include "net/StaticHeaderProxy.h" -SkinUpload::SkinUpload(QString token, SkinModel* skin) : NetRequest(), m_skin(skin), m_token(token) +SkinUpload::SkinUpload(QString token, QString path, QString variant) : NetRequest(), m_token(token), m_path(path), m_variant(variant) { logCat = taskMCSkinsLogC; } @@ -55,11 +55,11 @@ QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\"")); - skin.setBody(FS::read(m_skin->getPath())); + skin.setBody(FS::read(m_path)); QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - model.setBody(m_skin->getModelString().toUtf8()); + model.setBody(m_variant.toUtf8()); multiPart->append(skin); multiPart->append(model); @@ -74,9 +74,9 @@ void SkinUpload::init() })); } -SkinUpload::Ptr SkinUpload::make(QString token, SkinModel* skin) +SkinUpload::Ptr SkinUpload::make(QString token, QString path, QString variant) { - auto up = makeShared(token, skin); + auto up = makeShared(token, path, variant); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); diff --git a/launcher/minecraft/skins/SkinUpload.h b/launcher/minecraft/skins/SkinUpload.h index d070f301d..f24cef5a2 100644 --- a/launcher/minecraft/skins/SkinUpload.h +++ b/launcher/minecraft/skins/SkinUpload.h @@ -18,7 +18,6 @@ #pragma once -#include "minecraft/skins/SkinModel.h" #include "net/NetRequest.h" class SkinUpload : public Net::NetRequest { @@ -27,16 +26,17 @@ class SkinUpload : public Net::NetRequest { using Ptr = shared_qobject_ptr; // Note this class takes ownership of the file. - SkinUpload(QString token, SkinModel* skin); + SkinUpload(QString token, QString path, QString variant); virtual ~SkinUpload() = default; - static SkinUpload::Ptr make(QString token, SkinModel* skin); + static SkinUpload::Ptr make(QString token, QString path, QString variant); void init() override; protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; private: - SkinModel* m_skin; QString m_token; + QString m_path; + QString m_variant; }; diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 4ef91a2bf..24197baeb 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -242,7 +242,7 @@ void SkinManageDialog::accept() return; } - skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin)); + skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin->getPath(), skin->getModelString())); auto selectedCape = skin->getCapeId(); if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { From d2e662ddbb518b147de76ca3f613e046d2912db5 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 12:35:34 -0400 Subject: [PATCH 044/755] added support for components in resource pack descriptions. Signed-off-by: Caden Miller --- .../mod/tasks/LocalResourcePackParseTask.cpp | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 73cbf891c..f78d3b7b3 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -186,7 +186,35 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - pack.setDescription(Json::ensureString(pack_obj, "description", "")); + + // description could either be string, or array of dictionaries + auto desc_val = pack_obj.value("description"); + + if (desc_val.isString()) { + pack.setDescription(Json::ensureString(pack_obj, "description", "")); + } else if (desc_val.isArray()) { + + // rebuild the description from the dictionaries without colors + QString build_desc; + auto arr = desc_val.toArray(); + + for(const QJsonValue& dict : arr) { + // must be an object, for a dictionary + if (!dict.isObject()) throw Json::JsonException("Invalid value description."); + + auto obj = dict.toObject(); + auto val = obj.value(obj.keys()[0]); + + // value must be a string type + if (!val.isString()) throw Json::JsonException("Invalid value description type."); + + build_desc.append(val.toString()); + }; + + pack.setDescription(build_desc); + } else { + throw Json::JsonException("Invalid description type."); + } } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; From 093d09efe36dbe1b18e4ae0ccb48150ef2f9f3ab Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:03:33 -0400 Subject: [PATCH 045/755] fix style, and use qWarning instead of throw. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index f78d3b7b3..22f5fa163 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -189,9 +189,13 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); + if (desc_val.isUndefined()) { + qWarning() << "No resource pack description found."; + return false; + } if (desc_val.isString()) { - pack.setDescription(Json::ensureString(pack_obj, "description", "")); + pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { // rebuild the description from the dictionaries without colors @@ -200,20 +204,27 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) for(const QJsonValue& dict : arr) { // must be an object, for a dictionary - if (!dict.isObject()) throw Json::JsonException("Invalid value description."); + if (!dict.isObject()) + { + qWarning() << "Invalid component object."; + continue; + } auto obj = dict.toObject(); auto val = obj.value(obj.keys()[0]); - // value must be a string type - if (!val.isString()) throw Json::JsonException("Invalid value description type."); + if (!val.isString()) + { + qWarning() << "Invalid text description type in components."; + continue; + } build_desc.append(val.toString()); }; pack.setDescription(build_desc); } else { - throw Json::JsonException("Invalid description type."); + qWarning() << "Invalid description type."; } } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); From 04aa0155bf181dcd17236ef5baa3b91a4b9df449 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:45:30 -0400 Subject: [PATCH 046/755] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: d2e662ddbb518b147de76ca3f613e046d2912db5 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 22f5fa163..7a2fd7a86 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -221,7 +221,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) build_desc.append(val.toString()); }; - pack.setDescription(build_desc); } else { qWarning() << "Invalid description type."; From 05f4214cc5d3a9005c0d259ca185303792aa2fa7 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:50:13 -0400 Subject: [PATCH 047/755] fix clang-format failing --- .../minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 7a2fd7a86..b696adea5 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -202,10 +202,9 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) QString build_desc; auto arr = desc_val.toArray(); - for(const QJsonValue& dict : arr) { + for (const QJsonValue& dict : arr) { // must be an object, for a dictionary - if (!dict.isObject()) - { + if (!dict.isObject()) { qWarning() << "Invalid component object."; continue; } @@ -213,8 +212,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) auto obj = dict.toObject(); auto val = obj.value(obj.keys()[0]); - if (!val.isString()) - { + if (!val.isString()) { qWarning() << "Invalid text description type in components."; continue; } From ef1dc2afaccc681fdfd53513ac134b36a5d943cc Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:50:59 -0400 Subject: [PATCH 048/755] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: 05f4214cc5d3a9005c0d259ca185303792aa2fa7 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index b696adea5..eaef76a6c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -197,7 +197,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { - // rebuild the description from the dictionaries without colors QString build_desc; auto arr = desc_val.toArray(); From 1261908ef7465e000390c9c12133171f4e73302f Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:55:55 -0400 Subject: [PATCH 049/755] remove space for clang-format Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index eaef76a6c..1f535695b 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -194,7 +194,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) return false; } - if (desc_val.isString()) { + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { // rebuild the description from the dictionaries without colors From 7a7c3015f47cba94eb34deba6748dc8555d8e457 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 20:25:49 -0400 Subject: [PATCH 050/755] fix not properly reading json text. The text now displays properly in the GUI of Prism. Signed-off-by: cullvox --- .../minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 1f535695b..4c30c1204 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -209,14 +209,8 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) } auto obj = dict.toObject(); - auto val = obj.value(obj.keys()[0]); - if (!val.isString()) { - qWarning() << "Invalid text description type in components."; - continue; - } - - build_desc.append(val.toString()); + build_desc.append(Json::ensureString(obj, "text", {})); }; pack.setDescription(build_desc); } else { From 58bd6d929f20e43c38cb372e431d1e1240d736b6 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sun, 10 Sep 2023 23:37:26 -0400 Subject: [PATCH 051/755] added formatting with colors. This is somewhat dirty implementation and I will clean it up soon. Using the HTML rich text features of Qt, made it much easier to understand what was needed. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 170 +++++++++++++++--- 1 file changed, 143 insertions(+), 27 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 4c30c1204..e6dde4ba0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -178,9 +178,143 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta -bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) +struct TextFormat { + QString color = "#000000"; + bool bold = false; + bool italic = false; + bool underlined = false; + bool strikethrough = false; + + TextFormat() = default; + ~TextFormat() = default; + + void clear() { + color = "#00000"; + bold = false; + italic = false; + underlined = false; + strikethrough = false; + } +}; + +QString textColorToHexColor(const QString& color) { + static const std::unordered_map str_to_hex = { + { "black", "#000000" }, + { "dark_blue", "#0000AA" }, + { "dark_green", "#00AA00" }, + { "dark_aqua", "#00AAAA" }, + { "dark_red", "#AA0000" }, + { "dark_purple", "#AA00AA" }, + { "gold", "#FFAA00" }, + { "gray", "#AAAAAA" }, + { "dark_gray", "#555555" }, + { "blue", "#5555FF" }, + { "green", "#55FF55" }, + { "aqua", "#55FFFF" }, + { "red", "#FF5555" }, + { "light_purple", "#FF55FF" }, + { "yellow", "#FFFF55" }, + { "white", "#FFFFFF" }, + }; + + auto it = str_to_hex.find(color); + return (it != str_to_hex.end()) ? it->second : QString("#000000"); // return black if color not found +} + +bool readFormat(const QJsonObject& obj, TextFormat& format) { + auto text = obj.value("text"); + auto color = obj.value("color"); + auto bold = obj.value("bold"); + auto italic = obj.value("italic"); + auto underlined = obj.value("underlined"); + auto strikethrough = obj.value("strikethrough"); + auto extra = obj.value("extra"); + + if (color.isString()) { + format.color = textColorToHexColor(color.toString()); + } + if (bold.isBool()) + format.bold = bold.toBool(); + if (italic.isBool()) + format.italic = italic.toBool(); + if (underlined.isBool()) + format.underlined = underlined.toBool(); + if (strikethrough.isBool()) + format.strikethrough = strikethrough.toBool(); + + return true; +} + +void appendBeginFormat(TextFormat& format, QString& toAppend) { + toAppend.append(""); + if (format.bold) + toAppend.append(""); + if (format.italic) + toAppend.append(""); + if (format.underlined) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); +} + +void appendEndFormat(TextFormat& format, QString& toAppend) { + toAppend.append(""); + if (format.bold) + toAppend.append(""); + if (format.italic) + toAppend.append(""); + if (format.underlined) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); +} + +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); + +bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { + + for (const QJsonValue& val : arr) { + processComponent(val, result, parentFormat); + } + + return true; +} + +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) { + if (value.isString()) { + result.append(value.toString()); + } else if (value.isObject()) { + auto obj = value.toObject(); + + TextFormat format{}; + if (parentFormat) + format = *parentFormat; + + if (!readFormat(obj, format)) + return false; + + appendBeginFormat(format, result); + + auto text = obj.value("text"); + if (text.isString()) + result.append(text.toString()); + + auto extra = obj.value("extra"); + if (extra.isArray()) + if (!processComponentList(extra.toArray(), result, &format)) + return false; + + appendEndFormat(format, result); + } + + return true; +} + +// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { + + try { auto json_doc = QJsonDocument::fromJson(raw_data); auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); @@ -189,33 +323,15 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - if (desc_val.isUndefined()) { - qWarning() << "No resource pack description found."; - return false; - } - - if (desc_val.isString()) { + + if (desc_val.isString()) pack.setDescription(desc_val.toString()); - } else if (desc_val.isArray()) { - // rebuild the description from the dictionaries without colors - QString build_desc; - auto arr = desc_val.toArray(); - - for (const QJsonValue& dict : arr) { - // must be an object, for a dictionary - if (!dict.isObject()) { - qWarning() << "Invalid component object."; - continue; - } - - auto obj = dict.toObject(); - - build_desc.append(Json::ensureString(obj, "text", {})); - }; - pack.setDescription(build_desc); - } else { - qWarning() << "Invalid description type."; + else if (desc_val.isArray()) { + QString desc{}; + processComponentList(desc_val.toArray(), desc); + pack.setDescription(desc); } + } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; From fbe4043651bebaca399b9f4499a2097670e485b6 Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 01:34:53 -0400 Subject: [PATCH 052/755] fix formatting and add more proper errors. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 129 +++++++++++------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index e6dde4ba0..ca1a5455c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -184,45 +184,60 @@ struct TextFormat { bool italic = false; bool underlined = false; bool strikethrough = false; - - TextFormat() = default; - ~TextFormat() = default; - - void clear() { - color = "#00000"; - bold = false; - italic = false; - underlined = false; - strikethrough = false; - } }; -QString textColorToHexColor(const QString& color) +bool textColorToHexColor(const QString& text_color, QString& result) { - static const std::unordered_map str_to_hex = { - { "black", "#000000" }, - { "dark_blue", "#0000AA" }, - { "dark_green", "#00AA00" }, - { "dark_aqua", "#00AAAA" }, - { "dark_red", "#AA0000" }, - { "dark_purple", "#AA00AA" }, - { "gold", "#FFAA00" }, - { "gray", "#AAAAAA" }, - { "dark_gray", "#555555" }, - { "blue", "#5555FF" }, - { "green", "#55FF55" }, - { "aqua", "#55FFFF" }, - { "red", "#FF5555" }, - { "light_purple", "#FF55FF" }, - { "yellow", "#FFFF55" }, - { "white", "#FFFFFF" }, + static const QHash text_to_hex = { + { "black", "#000000" }, { "dark_blue", "#0000AA" }, { "dark_green", "#00AA00" }, { "dark_aqua", "#00AAAA" }, + { "dark_red", "#AA0000" }, { "dark_purple", "#AA00AA" }, { "gold", "#FFAA00" }, { "gray", "#AAAAAA" }, + { "dark_gray", "#555555" }, { "blue", "#5555FF" }, { "green", "#55FF55" }, { "aqua", "#55FFFF" }, + { "red", "#FF5555" }, { "light_purple", "#FF55FF" }, { "yellow", "#FFFF55" }, { "white", "#FFFFFF" }, }; - auto it = str_to_hex.find(color); - return (it != str_to_hex.end()) ? it->second : QString("#000000"); // return black if color not found + auto it = text_to_hex.find(text_color); + + if (it == text_to_hex.end()) { + qWarning() << "Invalid color string in component!"; + result = "#000000"; // return black if color not found + return false; + } + + result = it.value(); + return true; } -bool readFormat(const QJsonObject& obj, TextFormat& format) { +bool getBoolOrFromText(const QJsonValue& val, bool& result) +{ + if (val.isUndefined()) { + result = false; + return true; + } + + if (val.isBool()) { + result = val.toBool(); + return true; + } else if (val.isString()) { + auto bool_str = val.toString(); + + if (bool_str == "true") { + result = true; + return true; + } else if (bool_str == "false") { + result = false; + return true; + } else { + qWarning() << "Invalid bool value in component!"; + return false; + } + } else { + qWarning() << "Invalid type where bool expected!"; + return false; + } +} + +bool readFormat(const QJsonObject& obj, TextFormat& format) +{ auto text = obj.value("text"); auto color = obj.value("color"); auto bold = obj.value("bold"); @@ -232,21 +247,30 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) { auto extra = obj.value("extra"); if (color.isString()) { - format.color = textColorToHexColor(color.toString()); - } - if (bold.isBool()) - format.bold = bold.toBool(); - if (italic.isBool()) - format.italic = italic.toBool(); - if (underlined.isBool()) - format.underlined = underlined.toBool(); - if (strikethrough.isBool()) - format.strikethrough = strikethrough.toBool(); + // colors can either be a hex code or one of a few text colors + auto col_str = color.toString(); - return true; + const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); + const auto hex_match = hex_expression.match(col_str); + + if (hex_match.hasMatch()) { + format.color = color.toString(); + } else { + if (!textColorToHexColor(color.toString(), format.color)) { + qWarning() << "Invalid color type in component!"; + return false; + } + } + } + + return getBoolOrFromText(bold, format.bold) && + getBoolOrFromText(italic, format.italic) && + getBoolOrFromText(underlined, format.underlined) && + getBoolOrFromText(strikethrough, format.strikethrough); } -void appendBeginFormat(TextFormat& format, QString& toAppend) { +void appendBeginFormat(TextFormat& format, QString& toAppend) +{ toAppend.append(""); if (format.bold) toAppend.append(""); @@ -275,7 +299,8 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { for (const QJsonValue& val : arr) { - processComponent(val, result, parentFormat); + if (!processComponent(val, result, parentFormat)) + return false; } return true; @@ -306,6 +331,9 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare return false; appendEndFormat(format, result); + } else { + qWarning() << "Invalid component type!"; + return false; } return true; @@ -324,12 +352,15 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - if (desc_val.isString()) + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); - else if (desc_val.isArray()) { - QString desc{}; - processComponentList(desc_val.toArray(), desc); + } else if (desc_val.isArray()) { + QString desc; + if (!processComponentList(desc_val.toArray(), desc)) + return false; pack.setDescription(desc); + } else { + return false; } } catch (Json::JsonException& e) { From b16085f66c9083a1ec6d0feb08b96ce9173baccd Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 02:05:05 -0400 Subject: [PATCH 053/755] attempt to fix clang-format and ubuntu build. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index ca1a5455c..c2aef06d5 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -26,6 +26,7 @@ #include #include +#include namespace ResourcePackUtils { @@ -236,7 +237,7 @@ bool getBoolOrFromText(const QJsonValue& val, bool& result) } } -bool readFormat(const QJsonObject& obj, TextFormat& format) +bool readFormat(const QJsonObject& obj, TextFormat& format) { auto text = obj.value("text"); auto color = obj.value("color"); @@ -247,7 +248,7 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) auto extra = obj.value("extra"); if (color.isString()) { - // colors can either be a hex code or one of a few text colors + // colors can either be a hex code or one of a few text colors auto col_str = color.toString(); const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); @@ -263,13 +264,11 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) } } - return getBoolOrFromText(bold, format.bold) && - getBoolOrFromText(italic, format.italic) && - getBoolOrFromText(underlined, format.underlined) && - getBoolOrFromText(strikethrough, format.strikethrough); + return getBoolOrFromText(bold, format.bold) && getBoolOrFromText(italic, format.italic) && + getBoolOrFromText(underlined, format.underlined) && getBoolOrFromText(strikethrough, format.strikethrough); } -void appendBeginFormat(TextFormat& format, QString& toAppend) +void appendBeginFormat(TextFormat& format, QString& toAppend) { toAppend.append(""); if (format.bold) @@ -282,7 +281,8 @@ void appendBeginFormat(TextFormat& format, QString& toAppend) toAppend.append(""); } -void appendEndFormat(TextFormat& format, QString& toAppend) { +void appendEndFormat(TextFormat& format, QString& toAppend) +{ toAppend.append(""); if (format.bold) toAppend.append(""); @@ -296,7 +296,8 @@ void appendEndFormat(TextFormat& format, QString& toAppend) { bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); -bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { +bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) +{ for (const QJsonValue& val : arr) { if (!processComponent(val, result, parentFormat)) @@ -306,7 +307,8 @@ bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* pa return true; } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) { +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) +{ if (value.isString()) { result.append(value.toString()); } else if (value.isObject()) { @@ -340,9 +342,8 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta -bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { - - +bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) +{ try { auto json_doc = QJsonDocument::fromJson(raw_data); auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); @@ -351,7 +352,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { From df88ccd4190ad2f605efe96b2b1503bc0ce0d5c8 Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 15:49:01 -0400 Subject: [PATCH 054/755] clean up and add review suggestions, links open Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 133 ++++++------------ .../mod/tasks/LocalResourcePackParseTask.h | 3 + 2 files changed, 43 insertions(+), 93 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index c2aef06d5..5b6f6fee0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -185,87 +185,23 @@ struct TextFormat { bool italic = false; bool underlined = false; bool strikethrough = false; + bool is_linked = false; + QString link_url = ""; }; -bool textColorToHexColor(const QString& text_color, QString& result) -{ - static const QHash text_to_hex = { - { "black", "#000000" }, { "dark_blue", "#0000AA" }, { "dark_green", "#00AA00" }, { "dark_aqua", "#00AAAA" }, - { "dark_red", "#AA0000" }, { "dark_purple", "#AA00AA" }, { "gold", "#FFAA00" }, { "gray", "#AAAAAA" }, - { "dark_gray", "#555555" }, { "blue", "#5555FF" }, { "green", "#55FF55" }, { "aqua", "#55FFFF" }, - { "red", "#FF5555" }, { "light_purple", "#FF55FF" }, { "yellow", "#FFFF55" }, { "white", "#FFFFFF" }, - }; - - auto it = text_to_hex.find(text_color); - - if (it == text_to_hex.end()) { - qWarning() << "Invalid color string in component!"; - result = "#000000"; // return black if color not found - return false; - } - - result = it.value(); - return true; -} - -bool getBoolOrFromText(const QJsonValue& val, bool& result) -{ - if (val.isUndefined()) { - result = false; - return true; - } - - if (val.isBool()) { - result = val.toBool(); - return true; - } else if (val.isString()) { - auto bool_str = val.toString(); - - if (bool_str == "true") { - result = true; - return true; - } else if (bool_str == "false") { - result = false; - return true; - } else { - qWarning() << "Invalid bool value in component!"; - return false; - } - } else { - qWarning() << "Invalid type where bool expected!"; - return false; - } -} - bool readFormat(const QJsonObject& obj, TextFormat& format) { - auto text = obj.value("text"); - auto color = obj.value("color"); - auto bold = obj.value("bold"); - auto italic = obj.value("italic"); - auto underlined = obj.value("underlined"); - auto strikethrough = obj.value("strikethrough"); - auto extra = obj.value("extra"); + format.color = Json::ensureString(obj, "color"); + format.bold = Json::ensureBoolean(obj, "bold", false); + format.italic = Json::ensureBoolean(obj, "italic", false); + format.underlined = Json::ensureBoolean(obj, "underlined", false); + format.strikethrough = Json::ensureBoolean(obj, "strikethrough", false); - if (color.isString()) { - // colors can either be a hex code or one of a few text colors - auto col_str = color.toString(); + auto click_event = Json::ensureObject(obj, "clickEvent"); + format.is_linked = Json::ensureBoolean(click_event, "open_url", false); + format.link_url = Json::ensureString(click_event, "value"); - const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); - const auto hex_match = hex_expression.match(col_str); - - if (hex_match.hasMatch()) { - format.color = color.toString(); - } else { - if (!textColorToHexColor(color.toString(), format.color)) { - qWarning() << "Invalid color type in component!"; - return false; - } - } - } - - return getBoolOrFromText(bold, format.bold) && getBoolOrFromText(italic, format.italic) && - getBoolOrFromText(underlined, format.underlined) && getBoolOrFromText(strikethrough, format.strikethrough); + return true; } void appendBeginFormat(TextFormat& format, QString& toAppend) @@ -279,35 +215,36 @@ void appendBeginFormat(TextFormat& format, QString& toAppend) toAppend.append(""); if (format.strikethrough) toAppend.append(""); + if (format.is_linked) + toAppend.append(""); } void appendEndFormat(TextFormat& format, QString& toAppend) { - toAppend.append(""); - if (format.bold) - toAppend.append(""); + if (format.is_linked) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); if (format.italic) toAppend.append(""); if (format.underlined) toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); + if (format.bold) + toAppend.append(""); + toAppend.append(""); } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); - -bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) +bool processComponentList(const QJsonArray& arr, QString& result) { - for (const QJsonValue& val : arr) { - if (!processComponent(val, result, parentFormat)) + if (!processComponent(val, result)) return false; } return true; } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) +bool processComponent(const QJsonValue& value, QString& result) { if (value.isString()) { result.append(value.toString()); @@ -315,8 +252,6 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare auto obj = value.toObject(); TextFormat format{}; - if (parentFormat) - format = *parentFormat; if (!readFormat(obj, format)) return false; @@ -329,7 +264,7 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare auto extra = obj.value("extra"); if (extra.isArray()) - if (!processComponentList(extra.toArray(), result, &format)) + if (!processComponentList(extra.toArray(), result)) return false; appendEndFormat(format, result); @@ -342,6 +277,7 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +// https://minecraft.fandom.com/wiki/Raw_JSON_text_format#Plain_Text bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { try { @@ -350,20 +286,31 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - // description could either be string, or array of dictionaries + // description could be many things according to minecraft auto desc_val = pack_obj.value("description"); + QString desc; if (desc_val.isString()) { - pack.setDescription(desc_val.toString()); + desc = desc_val.toString(); } else if (desc_val.isArray()) { - QString desc; if (!processComponentList(desc_val.toArray(), desc)) return false; - pack.setDescription(desc); + } else if (desc_val.isObject()) { + if (!processComponent(desc_val, desc)) + return false; + } else if (desc_val.isBool()) { + desc = desc_val.toBool() ? "true" : "false"; + } else if (desc_val.isDouble()) { + desc = QString::number(desc_val.toDouble()); } else { + qWarning() << "Invalid description type!"; return false; } + qInfo() << desc; + + pack.setDescription(desc); + } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 5199bf3f0..ed3317643 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,6 +34,9 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); + +bool processComponent(const QJsonValue& value, QString& result); +bool processComponentList(const QJsonArray& value, QString& result); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); From a4e65305139dc5cdc7ad246b3203175a21262050 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:34:42 -0400 Subject: [PATCH 055/755] added tests, fixed issues with overriding/format In the documentation it states that child values can override the parent values. Originally this code did not support that but now it does. Also added in testing inspired by the previous tests. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 217 ++++++++++-------- .../mod/tasks/LocalResourcePackParseTask.h | 4 +- tests/CMakeLists.txt | 3 + tests/MetaComponentParse_test.cpp | 108 +++++++++ .../MetaComponentParse/component_basic.json | 4 + .../component_with_extra.json | 18 ++ .../component_with_format.json | 13 ++ .../component_with_link.json | 12 + .../component_with_mixed.json | 40 ++++ 9 files changed, 321 insertions(+), 98 deletions(-) create mode 100644 tests/MetaComponentParse_test.cpp create mode 100644 tests/testdata/MetaComponentParse/component_basic.json create mode 100644 tests/testdata/MetaComponentParse/component_with_extra.json create mode 100644 tests/testdata/MetaComponentParse/component_with_format.json create mode 100644 tests/testdata/MetaComponentParse/component_with_link.json create mode 100644 tests/testdata/MetaComponentParse/component_with_mixed.json diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 5b6f6fee0..31cfdca04 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,96 +179,138 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormat { - QString color = "#000000"; - bool bold = false; - bool italic = false; - bool underlined = false; - bool strikethrough = false; - bool is_linked = false; - QString link_url = ""; -}; +struct TextFormatter { + // left is value, right is if the value was explicitly written + QPair color = { "#000000", false }; + QPair bold = { false, false }; + QPair italic = { false, false }; + QPair underlined = { false, false }; + QPair strikethrough = { false, false }; + QPair is_linked = { false, false }; + QPair link_url = { "", false }; -bool readFormat(const QJsonObject& obj, TextFormat& format) -{ - format.color = Json::ensureString(obj, "color"); - format.bold = Json::ensureBoolean(obj, "bold", false); - format.italic = Json::ensureBoolean(obj, "italic", false); - format.underlined = Json::ensureBoolean(obj, "underlined", false); - format.strikethrough = Json::ensureBoolean(obj, "strikethrough", false); + void setColor(const QString& new_color, bool written) { color = { new_color, written}; } + void setBold(bool new_bold, bool written) { bold = { new_bold, written}; } + void setItalic(bool new_italic, bool written) { italic = { new_italic, written}; } + void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written}; } + void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written}; } + void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written}; } + void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written}; } - auto click_event = Json::ensureObject(obj, "clickEvent"); - format.is_linked = Json::ensureBoolean(click_event, "open_url", false); - format.link_url = Json::ensureString(click_event, "value"); - - return true; -} - -void appendBeginFormat(TextFormat& format, QString& toAppend) -{ - toAppend.append(""); - if (format.bold) - toAppend.append(""); - if (format.italic) - toAppend.append(""); - if (format.underlined) - toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); - if (format.is_linked) - toAppend.append(""); -} - -void appendEndFormat(TextFormat& format, QString& toAppend) -{ - if (format.is_linked) - toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); - if (format.italic) - toAppend.append(""); - if (format.underlined) - toAppend.append(""); - if (format.bold) - toAppend.append(""); - toAppend.append(""); -} - -bool processComponentList(const QJsonArray& arr, QString& result) -{ - for (const QJsonValue& val : arr) { - if (!processComponent(val, result)) - return false; + void overrideFrom(const TextFormatter& child) + { + if (child.color.second) + color.first = child.color.first; + if (child.bold.second) + bold.first = child.bold.first; + if (child.italic.second) + italic.first = child.italic.first; + if (child.underlined.second) + underlined.first = child.underlined.first; + if (child.strikethrough.second) + strikethrough.first = child.strikethrough.first; + if (child.is_linked.second) + is_linked.first = child.is_linked.first; + if (child.link_url.second) + link_url.first = child.link_url.first; } - return true; -} + QString format(QString text) + { + if (text.isEmpty()) + return QString(); -bool processComponent(const QJsonValue& value, QString& result) + QString result; + + if (color.first != "#000000") + result.append(""); + if (bold.first) + result.append(""); + if (italic.first) + result.append(""); + if (underlined.first) + result.append(""); + if (strikethrough.first) + result.append(""); + if (is_linked.first) + result.append(""); + + result.append(text); + + if (is_linked.first) + result.append(""); + if (strikethrough.first) + result.append(""); + if (underlined.first) + result.append(""); + if (italic.first) + result.append(""); + if (bold.first) + result.append(""); + if (color.first != "#000000") + result.append(""); + + return result; + } + + bool readFormat(const QJsonObject& obj) + { + setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); + setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); + setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); + setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); + setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); + + auto click_event = Json::ensureObject(obj, "clickEvent"); + setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); + setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); + + return true; + } +}; + +bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) { + TextFormatter formatter; + + if (parentFormat) + formatter = *parentFormat; + if (value.isString()) { - result.append(value.toString()); - } else if (value.isObject()) { + result.append(formatter.format(value.toString())); + } else if (value.isBool()) { + result.append(formatter.format(value.toBool() ? "true" : "false")); + } else if (value.isDouble()) { + result.append(formatter.format(QString::number(value.toDouble()))); + } else if (value.isObject()) { auto obj = value.toObject(); - TextFormat format{}; - - if (!readFormat(obj, format)) + if (not formatter.readFormat(obj)) return false; - - appendBeginFormat(format, result); - - auto text = obj.value("text"); - if (text.isString()) - result.append(text.toString()); + // override the parent format with our new one + TextFormatter mixed; + if (parentFormat) + mixed = *parentFormat; + + mixed.overrideFrom(formatter); + + result.append(mixed.format(Json::ensureString(obj, "text"))); + + // process any 'extra' children with this format auto extra = obj.value("extra"); - if (extra.isArray()) - if (!processComponentList(extra.toArray(), result)) - return false; + if (not extra.isUndefined()) + return processComponent(extra, result, &mixed); - appendEndFormat(format, result); - } else { + } else if (value.isArray()) { + auto array = value.toArray(); + + for (const QJsonValue& current : array) { + if (not processComponent(current, result, parentFormat)) { + return false; + } + } + } else { qWarning() << "Invalid component type!"; return false; } @@ -286,29 +328,12 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - // description could be many things according to minecraft auto desc_val = pack_obj.value("description"); - - QString desc; - if (desc_val.isString()) { - desc = desc_val.toString(); - } else if (desc_val.isArray()) { - if (!processComponentList(desc_val.toArray(), desc)) - return false; - } else if (desc_val.isObject()) { - if (!processComponent(desc_val, desc)) - return false; - } else if (desc_val.isBool()) { - desc = desc_val.toBool() ? "true" : "false"; - } else if (desc_val.isDouble()) { - desc = QString::number(desc_val.toDouble()); - } else { - qWarning() << "Invalid description type!"; + QString desc{}; + if (not processComponent(desc_val, desc)) return false; - } qInfo() << desc; - pack.setDescription(desc); } catch (Json::JsonException& e) { diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index ed3317643..7e893979a 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -35,8 +35,8 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Ful bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -bool processComponent(const QJsonValue& value, QString& result); -bool processComponentList(const QJsonArray& value, QString& result); +struct TextFormatter; +bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a26a49fec..8690c078e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,3 +56,6 @@ ecm_add_test(Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}: ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Version) + +ecm_add_test(MetaComponentParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test + TEST_NAME MetaComponentParse) \ No newline at end of file diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp new file mode 100644 index 000000000..77b49b478 --- /dev/null +++ b/tests/MetaComponentParse_test.cpp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +class MetaComponentParseTest : public QObject { + Q_OBJECT + + void doTest(QString name) + { + QString source = QFINDTESTDATA("testdata/MetaComponentParse"); + + QString comp_rp = FS::PathCombine(source, name); + + QFile file; + file.setFileName(comp_rp); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + QString data = file.readAll(); + file.close(); + + QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8()); + QJsonObject obj = doc.object(); + + QJsonValue description_json = obj.value("description"); + QJsonValue expected_json = obj.value("expected_output"); + + QVERIFY(description_json.isUndefined() == false); + QVERIFY(expected_json.isString() == true); + + QString expected = expected_json.toString(); + + QString processed; + bool valid = ResourcePackUtils::processComponent(description_json, processed); + + QVERIFY(processed == expected); + QVERIFY(valid == true); + } + +private slots: + void test_parseComponentBasic() + { + doTest("component_basic.json"); + } + + void test_parseComponentWithFormat() + { + doTest("component_with_format.json"); + } + + void test_parseComponentWithExtra() + { + doTest("component_with_extra.json"); + } + + void test_parseComponentWithLink() + { + doTest("component_with_link.json"); + } + + void test_parseComponentWithMixed() + { + doTest("component_with_mixed.json"); + } +}; + +QTEST_GUILESS_MAIN(MetaComponentParseTest) + +#include "MetaComponentParse_test.moc" diff --git a/tests/testdata/MetaComponentParse/component_basic.json b/tests/testdata/MetaComponentParse/component_basic.json new file mode 100644 index 000000000..5cfdeeeab --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_basic.json @@ -0,0 +1,4 @@ +{ + "description": [{"text": "Hello, Component!"}], + "expected_output": "Hello, Component!" +} diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json new file mode 100644 index 000000000..49397556d --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -0,0 +1,18 @@ +{ + "description": [ + { + "text": "Hello, ", + "color": "red", + "bold": true, + "italic": true, + "extra": [ + { + "extra": "Component!", + "bold": false, + "italic": false + } + ] + } + ], + "expected_output": "Hello, Component!" +} \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json new file mode 100644 index 000000000..000de20cb --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -0,0 +1,13 @@ +{ + "description": [ + { + "text": "Hello, Component!", + "color": "blue", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true + } + ], + "expected_output": "Hello, Component!" +} \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json new file mode 100644 index 000000000..e190cc5e8 --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -0,0 +1,12 @@ +{ + "description": [ + { + "text": "Hello, Component!", + "clickEvent": { + "open_url": true, + "value": "https://google.com" + } + } + ], + "expected_output": "Hello, Component!" +} diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json new file mode 100644 index 000000000..7e65169af --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -0,0 +1,40 @@ +{ + "description": [ + { + "text": "The quick ", + "color": "blue", + "italic": true + }, + { + "text": "brown fox ", + "color": "#873600", + "bold": true, + "underlined": true, + "extra": { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + }, + { + "text": "the lazy dog's back. ", + "color": "green", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true, + "extra": [ + { + "text": "1234567890 ", + "color": "black", + "strikethrough": false, + "extra": "How vexingly quick daft zebras jump!" + } + ] + } + ], + "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" +} From ddf0c28b1b0b6617b0308405b4d5eefd20ff48b2 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:45:29 -0400 Subject: [PATCH 056/755] clang-format fixes --- .../mod/tasks/LocalResourcePackParseTask.cpp | 22 ++++++------ .../mod/tasks/LocalResourcePackParseTask.h | 1 - tests/MetaComponentParse_test.cpp | 35 +++++-------------- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 31cfdca04..8e209a416 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -189,18 +189,18 @@ struct TextFormatter { QPair is_linked = { false, false }; QPair link_url = { "", false }; - void setColor(const QString& new_color, bool written) { color = { new_color, written}; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written}; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written}; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written}; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written}; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written}; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written}; } + void setColor(const QString& new_color, bool written) { color = { new_color, written }; } + void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } + void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } + void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } + void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } + void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } + void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } void overrideFrom(const TextFormatter& child) { if (child.color.second) - color.first = child.color.first; + color.first = child.color.first; if (child.bold.second) bold.first = child.bold.first; if (child.italic.second) @@ -282,7 +282,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat result.append(formatter.format(value.toBool() ? "true" : "false")); } else if (value.isDouble()) { result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { + } else if (value.isObject()) { auto obj = value.toObject(); if (not formatter.readFormat(obj)) @@ -294,7 +294,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat mixed = *parentFormat; mixed.overrideFrom(formatter); - + result.append(mixed.format(Json::ensureString(obj, "text"))); // process any 'extra' children with this format @@ -310,7 +310,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat return false; } } - } else { + } else { qWarning() << "Invalid component type!"; return false; } diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 7e893979a..945499a91 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,7 +34,6 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); - struct TextFormatter; bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 77b49b478..1b1e2ce3e 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -33,11 +33,11 @@ * limitations under the License. */ -#include -#include #include #include #include +#include +#include #include @@ -76,31 +76,12 @@ class MetaComponentParseTest : public QObject { QVERIFY(valid == true); } -private slots: - void test_parseComponentBasic() - { - doTest("component_basic.json"); - } - - void test_parseComponentWithFormat() - { - doTest("component_with_format.json"); - } - - void test_parseComponentWithExtra() - { - doTest("component_with_extra.json"); - } - - void test_parseComponentWithLink() - { - doTest("component_with_link.json"); - } - - void test_parseComponentWithMixed() - { - doTest("component_with_mixed.json"); - } + private slots: + void test_parseComponentBasic() { doTest("component_basic.json"); } + void test_parseComponentWithFormat() { doTest("component_with_format.json"); } + void test_parseComponentWithExtra() { doTest("component_with_extra.json"); } + void test_parseComponentWithLink() { doTest("component_with_link.json"); } + void test_parseComponentWithMixed() { doTest("component_with_mixed.json"); } }; QTEST_GUILESS_MAIN(MetaComponentParseTest) From e1dda6c005dab379a5b93a2a9279205895b71060 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:50:33 -0400 Subject: [PATCH 057/755] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: ddf0c28b1b0b6617b0308405b4d5eefd20ff48b2 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 8e209a416..5e53244ff 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -333,7 +333,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) if (not processComponent(desc_val, desc)) return false; - qInfo() << desc; pack.setDescription(desc); } catch (Json::JsonException& e) { From ee7016fa542e3ca92bdd691bd2e168d9d43a02c2 Mon Sep 17 00:00:00 2001 From: cullvox Date: Wed, 13 Sep 2023 14:28:56 -0400 Subject: [PATCH 058/755] use clang-format Signed-off-by: cullvox Hello, Component!" + ], + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 000de20cb..00dfc7daf 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -1,13 +1,13 @@ { - "description": [ - { - "text": "Hello, Component!", - "color": "blue", - "bold": true, - "italic": true, - "underlined": true, - "strikethrough": true - } - ], - "expected_output": "Hello, Component!" + "description": [ + { + "text": "Hello, Component!", + "color": "blue", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true + } + ], + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index e190cc5e8..b1e34c7d6 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -1,12 +1,12 @@ { - "description": [ - { - "text": "Hello, Component!", - "clickEvent": { - "open_url": true, - "value": "https://google.com" - } - } - ], - "expected_output": "Hello, Component!" + "description": [ + { + "text": "Hello, Component!", + "clickEvent": { + "open_url": true, + "value": "https://google.com" + } + } + ], + "expected_output": "Hello, Component!" } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7e65169af..7c8c5b032 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -1,40 +1,41 @@ { - "description": [ - { - "text": "The quick ", - "color": "blue", - "italic": true - }, - { - "text": "brown fox ", - "color": "#873600", - "bold": true, - "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } - }, - { - "text": "the lazy dog's back. ", - "color": "green", - "bold": true, - "italic": true, - "underlined": true, - "strikethrough": true, - "extra": [ + "description": [ { - "text": "1234567890 ", - "color": "black", - "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "text": "The quick ", + "color": "blue", + "italic": true + }, + { + "text": "brown fox ", + "color": "#873600", + "bold": true, + "underlined": true, + "extra": { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + }, + { + "text": "the lazy dog's back. ", + "color": "green", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true, + "extra": [ + { + "text": "1234567890 ", + "color": "black", + "strikethrough": false, + "extra": "How vexingly quick daft zebras jump!" + } + ] } - ] - } - ], - "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + ], + "expected_output": + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } From e96d0b6cafb0a0c17e7f5baef1bfbafe4a7fcb07 Mon Sep 17 00:00:00 2001 From: cullvox Date: Wed, 13 Sep 2023 14:31:04 -0400 Subject: [PATCH 059/755] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: ee7016fa542e3ca92bdd691bd2e168d9d43a02c2 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 5e53244ff..fd28aa0b7 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -272,7 +272,6 @@ struct TextFormatter { bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) { TextFormatter formatter; - if (parentFormat) formatter = *parentFormat; From c81689d39322d5ea5c7ed0073d73e4d8d2446b33 Mon Sep 17 00:00:00 2001 From: cullvox Date: Fri, 15 Sep 2023 20:41:21 -0400 Subject: [PATCH 060/755] fixes html elide issue with InfoFrame --- launcher/ui/widgets/InfoFrame.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 1f03f9eaf..0162b4000 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "InfoFrame.h" #include "ui_InfoFrame.h" @@ -274,12 +275,31 @@ void InfoFrame::setDescription(QString text) } QString labeltext; labeltext.reserve(300); - if (finaltext.length() > 290) { + + + // elide rich text by getting characters without formatting + const int maxCharacterElide = 290; + QTextDocument doc; + doc.setHtml(text); + + if (doc.characterCount() > maxCharacterElide) { + ui->descriptionLabel->setOpenExternalLinks(false); - ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); + ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - // This allows injecting HTML here. - labeltext.append("" + finaltext.left(287) + "..."); + + const QString elidedPostfix = "..."; + + // move the cursor to the character elide, doesn't see html + QTextCursor cursor(&doc); + cursor.movePosition(QTextCursor::End); + cursor.setPosition(maxCharacterElide, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + + // insert the post fix at the cursor + cursor.insertHtml(elidedPostfix); + + labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); } else { ui->descriptionLabel->setTextFormat(Qt::TextFormat::AutoText); From 4053229544ca2d80cd569f4d67365f81ed64353d Mon Sep 17 00:00:00 2001 From: cullvox Date: Fri, 15 Sep 2023 20:55:34 -0400 Subject: [PATCH 061/755] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: c81689d39322d5ea5c7ed0073d73e4d8d2446b33 Signed-off-by: cullvox --- launcher/ui/widgets/InfoFrame.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 0162b4000..6423e88d6 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -36,8 +36,9 @@ #include #include -#include #include +#include +#include #include "InfoFrame.h" #include "ui_InfoFrame.h" @@ -276,16 +277,14 @@ void InfoFrame::setDescription(QString text) QString labeltext; labeltext.reserve(300); - // elide rich text by getting characters without formatting const int maxCharacterElide = 290; QTextDocument doc; doc.setHtml(text); if (doc.characterCount() > maxCharacterElide) { - ui->descriptionLabel->setOpenExternalLinks(false); - ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. + ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; const QString elidedPostfix = "..."; @@ -298,7 +297,7 @@ void InfoFrame::setDescription(QString text) // insert the post fix at the cursor cursor.insertHtml(elidedPostfix); - + labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); } else { From 01e98a6ce810ed1e8169d2252dca5cec39f8d335 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 16 Sep 2023 10:20:24 +0300 Subject: [PATCH 062/755] simplify the raw json parsing Signed-off-by: Trial97 Fixed Tests Signed-off-by: Trial97 --- .../mod/tasks/LocalResourcePackParseTask.cpp | 214 +++++++----------- .../mod/tasks/LocalResourcePackParseTask.h | 3 +- launcher/ui/widgets/InfoFrame.cpp | 6 +- tests/MetaComponentParse_test.cpp | 10 +- .../component_with_extra.json | 7 +- .../component_with_format.json | 2 +- .../component_with_link.json | 2 +- .../component_with_mixed.json | 24 +- 8 files changed, 104 insertions(+), 164 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index fd28aa0b7..d9d26b9c2 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,142 +179,85 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormatter { - // left is value, right is if the value was explicitly written - QPair color = { "#000000", false }; - QPair bold = { false, false }; - QPair italic = { false, false }; - QPair underlined = { false, false }; - QPair strikethrough = { false, false }; - QPair is_linked = { false, false }; - QPair link_url = { "", false }; - - void setColor(const QString& new_color, bool written) { color = { new_color, written }; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } - - void overrideFrom(const TextFormatter& child) - { - if (child.color.second) - color.first = child.color.first; - if (child.bold.second) - bold.first = child.bold.first; - if (child.italic.second) - italic.first = child.italic.first; - if (child.underlined.second) - underlined.first = child.underlined.first; - if (child.strikethrough.second) - strikethrough.first = child.strikethrough.first; - if (child.is_linked.second) - is_linked.first = child.is_linked.first; - if (child.link_url.second) - link_url.first = child.link_url.first; - } - - QString format(QString text) - { - if (text.isEmpty()) - return QString(); - - QString result; - - if (color.first != "#000000") - result.append(""); - if (bold.first) - result.append(""); - if (italic.first) - result.append(""); - if (underlined.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (is_linked.first) - result.append(""); - - result.append(text); - - if (is_linked.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (underlined.first) - result.append(""); - if (italic.first) - result.append(""); - if (bold.first) - result.append(""); - if (color.first != "#000000") - result.append(""); - - return result; - } - - bool readFormat(const QJsonObject& obj) - { - setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); - setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); - setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); - setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); - setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); - - auto click_event = Json::ensureObject(obj, "clickEvent"); - setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); - setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); - - return true; - } -}; - -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) +QString buildStyle(const QJsonObject& obj) { - TextFormatter formatter; - if (parentFormat) - formatter = *parentFormat; - - if (value.isString()) { - result.append(formatter.format(value.toString())); - } else if (value.isBool()) { - result.append(formatter.format(value.toBool() ? "true" : "false")); - } else if (value.isDouble()) { - result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { - auto obj = value.toObject(); - - if (not formatter.readFormat(obj)) - return false; - - // override the parent format with our new one - TextFormatter mixed; - if (parentFormat) - mixed = *parentFormat; - - mixed.overrideFrom(formatter); - - result.append(mixed.format(Json::ensureString(obj, "text"))); - - // process any 'extra' children with this format - auto extra = obj.value("extra"); - if (not extra.isUndefined()) - return processComponent(extra, result, &mixed); - - } else if (value.isArray()) { - auto array = value.toArray(); - - for (const QJsonValue& current : array) { - if (not processComponent(current, result, parentFormat)) { - return false; - } + QStringList styles; + if (auto color = Json::ensureString(obj, "color"); !color.isEmpty()) { + styles << QString("color: %1;").arg(color); + } + if (obj.contains("bold")) { + QString weight = "normal"; + if (Json::ensureBoolean(obj, "bold", false)) { + weight = "bold"; } - } else { - qWarning() << "Invalid component type!"; - return false; + styles << QString("font-weight: %1;").arg(weight); + } + if (obj.contains("italic")) { + QString style = "normal"; + if (Json::ensureBoolean(obj, "italic", false)) { + style = "italic"; + } + styles << QString("font-style: %1;").arg(style); } - return true; + return styles.isEmpty() ? "" : QString("style=\"%1\"").arg(styles.join(" ")); +} + +QString processComponent(const QJsonArray& value, bool strikethrough, bool underline) +{ + QString result; + for (auto current : value) + result += processComponent(current, strikethrough, underline); + return result; +} + +QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline) +{ + underline = Json::ensureBoolean(obj, "underlined", underline); + strikethrough = Json::ensureBoolean(obj, "strikethrough", strikethrough); + + QString result = Json::ensureString(obj, "text"); + if (underline) { + result = QString("%1").arg(result); + } + if (strikethrough) { + result = QString("%1").arg(result); + } + // the extra needs to be a array + result += processComponent(Json::ensureArray(obj, "extra"), strikethrough, underline); + if (auto style = buildStyle(obj); !style.isEmpty()) { + result = QString("%2").arg(style, result); + } + if (obj.contains("clickEvent")) { + auto click_event = Json::ensureObject(obj, "clickEvent"); + auto action = Json::ensureString(click_event, "action"); + auto value = Json::ensureString(click_event, "value"); + if (action == "open_url" && !value.isEmpty()) { + result = QString("%2").arg(value, result); + } + } + return result; +} + +QString processComponent(const QJsonValue& value, bool strikethrough, bool underline) +{ + if (value.isString()) { + return value.toString(); + } + if (value.isBool()) { + return value.toBool() ? "true" : "false"; + } + if (value.isDouble()) { + return QString::number(value.toDouble()); + } + if (value.isArray()) { + return processComponent(value.toArray(), strikethrough, underline); + } + if (value.isObject()) { + return processComponent(value.toObject(), strikethrough, underline); + } + qWarning() << "Invalid component type!"; + return {}; } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta @@ -327,12 +270,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - auto desc_val = pack_obj.value("description"); - QString desc{}; - if (not processComponent(desc_val, desc)) - return false; - - pack.setDescription(desc); + pack.setDescription(processComponent(pack_obj.value("description"))); } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 945499a91..97bf7b2ba 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,8 +34,7 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -struct TextFormatter; -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); +QString processComponent(const QJsonValue& value, bool strikethrough = false, bool underline = false); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 6423e88d6..2cf3e7a93 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -287,8 +287,6 @@ void InfoFrame::setDescription(QString text) ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - const QString elidedPostfix = "..."; - // move the cursor to the character elide, doesn't see html QTextCursor cursor(&doc); cursor.movePosition(QTextCursor::End); @@ -296,7 +294,7 @@ void InfoFrame::setDescription(QString text) cursor.removeSelectedText(); // insert the post fix at the cursor - cursor.insertHtml(elidedPostfix); + cursor.insertHtml("..."); labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); @@ -335,7 +333,7 @@ void InfoFrame::setLicense(QString text) if (finaltext.length() > 290) { ui->licenseLabel->setOpenExternalLinks(false); ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText); - m_description = text; + m_license = text; // This allows injecting HTML here. labeltext.append("" + finaltext.left(287) + "..."); QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 1b1e2ce3e..9979a9fa6 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -64,16 +64,14 @@ class MetaComponentParseTest : public QObject { QJsonValue description_json = obj.value("description"); QJsonValue expected_json = obj.value("expected_output"); - QVERIFY(description_json.isUndefined() == false); - QVERIFY(expected_json.isString() == true); + QVERIFY(!description_json.isUndefined()); + QVERIFY(expected_json.isString()); QString expected = expected_json.toString(); - QString processed; - bool valid = ResourcePackUtils::processComponent(description_json, processed); + QString processed = ResourcePackUtils::processComponent(description_json); - QVERIFY(processed == expected); - QVERIFY(valid == true); + QCOMPARE(processed, expected); } private slots: diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json index e26b2abff..887becdbe 100644 --- a/tests/testdata/MetaComponentParse/component_with_extra.json +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -7,12 +7,15 @@ "italic": true, "extra": [ { - "extra": "Component!", + "extra": [ + "Component!" + ], "bold": false, "italic": false } ] } ], - "expected_output": "Hello, Component!" + "expected_output": + "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 00dfc7daf..1078886a6 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -9,5 +9,5 @@ "strikethrough": true } ], - "expected_output": "Hello, Component!" + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index b1e34c7d6..188c004cd 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -3,7 +3,7 @@ { "text": "Hello, Component!", "clickEvent": { - "open_url": true, + "action": "open_url", "value": "https://google.com" } } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7c8c5b032..661fc1a3e 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -10,14 +10,16 @@ "color": "#873600", "bold": true, "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } + "extra": [ + { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + ] }, { "text": "the lazy dog's back. ", @@ -31,11 +33,13 @@ "text": "1234567890 ", "color": "black", "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "extra": [ + "How vexingly quick daft zebras jump!" + ] } ] } ], "expected_output": - "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } From 0e41ceffc4a2b8842e44a8bd1dc027944ce27471 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 16 Sep 2023 19:18:58 +0300 Subject: [PATCH 063/755] removed missed header Signed-off-by: Trial97 --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index d9d26b9c2..c5d899123 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -26,7 +26,6 @@ #include #include -#include namespace ResourcePackUtils { From 47dbb0911509804d8df795d2db0581ab73955e87 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 7 Oct 2023 00:21:40 +0300 Subject: [PATCH 064/755] Improvements to concurrent task Signed-off-by: Trial97 --- launcher/tasks/ConcurrentTask.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 6cfd864cc..2ab69d04f 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -152,8 +152,12 @@ void ConcurrentTask::startNext() QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection); // Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task. - for (int i = 0; i < num_starts; i++) + for (int i = 0; i < num_starts; i++) { + QCoreApplication::processEvents(); + if (m_aborted || m_doing.count() > m_total_max_size) + return; QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection); + } } void ConcurrentTask::subTaskSucceeded(Task::Ptr task) From 8dd640819dd961d1195f515713e0d8938763da54 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 9 Oct 2023 01:50:14 +0300 Subject: [PATCH 065/755] removed processEvents from ConcurrentTask Signed-off-by: Trial97 --- launcher/net/NetJob.cpp | 18 +++-- launcher/net/NetJob.h | 5 +- launcher/tasks/ConcurrentTask.cpp | 96 ++++++++++++-------------- launcher/tasks/ConcurrentTask.h | 9 +-- launcher/tasks/MultipleOptionsTask.cpp | 11 ++- launcher/tasks/MultipleOptionsTask.h | 6 +- launcher/tasks/SequentialTask.cpp | 13 ++-- launcher/tasks/SequentialTask.h | 4 +- tests/Task_test.cpp | 9 ++- 9 files changed, 84 insertions(+), 87 deletions(-) diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index b99c5acb0..52f6f1517 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -37,6 +37,7 @@ #include "NetJob.h" #include "Application.h" +#include "tasks/ConcurrentTask.h" NetJob::NetJob(QString job_name, shared_qobject_ptr network) : ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network) @@ -51,18 +52,15 @@ auto NetJob::addNetAction(NetAction::Ptr action) -> bool return true; } -void NetJob::startNext() +void NetJob::executeNextSubTask() { - if (m_queue.isEmpty() && m_doing.isEmpty()) { - // We're finished, check for failures and retry if we can (up to 3 times) - if (!m_failed.isEmpty() && m_try < 3) { - m_try += 1; - while (!m_failed.isEmpty()) - m_queue.enqueue(m_failed.take(*m_failed.keyBegin())); - } + // We're finished, check for failures and retry if we can (up to 3 times) + if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) { + m_try += 1; + while (!m_failed.isEmpty()) + m_queue.enqueue(m_failed.take(*m_failed.keyBegin())); } - - ConcurrentTask::startNext(); + ConcurrentTask::executeNextSubTask(); } auto NetJob::size() const -> int diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index 1c4337ec6..f6c005809 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -55,8 +55,6 @@ class NetJob : public ConcurrentTask { explicit NetJob(QString job_name, shared_qobject_ptr network); ~NetJob() override = default; - void startNext() override; - auto size() const -> int; auto canAbort() const -> bool override; @@ -69,6 +67,9 @@ class NetJob : public ConcurrentTask { // Qt can't handle auto at the start for some reason? bool abort() override; + protected slots: + void executeNextSubTask() override; + protected: void updateState() override; diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 2ab69d04f..97406d58f 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -35,7 +35,6 @@ */ #include "ConcurrentTask.h" -#include #include #include "tasks/Task.h" @@ -51,6 +50,10 @@ ConcurrentTask::~ConcurrentTask() if (task) task->deleteLater(); } + for (auto task : m_done) { + if (task) + task->deleteLater(); + } } auto ConcurrentTask::getStepProgress() const -> TaskStepProgressList @@ -65,15 +68,13 @@ void ConcurrentTask::addTask(Task::Ptr task) void ConcurrentTask::executeTask() { - // Start one task, startNext handles starting the up to the m_total_max_size - // while tracking the number currently being done - QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection); + for (auto i = 0; i <= m_total_max_size; i++) + executeNextSubTask(); } bool ConcurrentTask::abort() { m_queue.clear(); - m_aborted = true; if (m_doing.isEmpty()) { // Don't call emitAborted() here, we want to bypass the 'is the task running' check @@ -108,27 +109,33 @@ void ConcurrentTask::clear() m_failed.clear(); m_queue.clear(); - m_aborted = false; - m_progress = 0; m_stepProgress = 0; } -void ConcurrentTask::startNext() +void ConcurrentTask::executeNextSubTask() { - if (m_aborted || m_doing.count() > m_total_max_size) + if (!isRunning()) { return; - - if (m_queue.isEmpty() && m_doing.isEmpty() && !wasSuccessful()) { - emitSucceeded(); + } + if (m_queue.isEmpty()) { + if (m_doing.isEmpty()) { + // if (m_failed.isEmpty()) + emitSucceeded(); + // else + // emitFailed(tr("One or more subtasks failed")); + } + return; + } + if (m_doing.count() > m_total_max_size) { return; } - if (m_queue.isEmpty()) - return; - - Task::Ptr next = m_queue.dequeue(); + startSubTask(m_queue.dequeue()); +} +void ConcurrentTask::startSubTask(Task::Ptr next) +{ connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); }); connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); @@ -140,59 +147,42 @@ void ConcurrentTask::startNext() connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); }); m_doing.insert(next.get(), next); - qsizetype num_starts = qMin(m_queue.size(), m_total_max_size - m_doing.size()); + auto task_progress = std::make_shared(next->getUid()); m_task_progress.insert(next->getUid(), task_progress); updateState(); updateStepProgress(*task_progress.get(), Operation::ADDED); - QCoreApplication::processEvents(); - QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection); +} - // Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task. - for (int i = 0; i < num_starts; i++) { - QCoreApplication::processEvents(); - if (m_aborted || m_doing.count() > m_total_max_size) - return; - QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection); - } +void ConcurrentTask::subTaskFinished(Task::Ptr task, TaskStepState state) +{ + m_done.insert(task.get(), task); + (state == TaskStepState::Succeeded ? m_succeeded : m_failed).insert(task.get(), task); + + m_doing.remove(task.get()); + + auto task_progress = m_task_progress.value(task->getUid()); + task_progress->state = state; + + disconnect(task.get(), 0, this, 0); + + emit stepProgress(*task_progress); + updateState(); + updateStepProgress(*task_progress, Operation::REMOVED); + QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection); } void ConcurrentTask::subTaskSucceeded(Task::Ptr task) { - m_done.insert(task.get(), task); - m_succeeded.insert(task.get(), task); - - m_doing.remove(task.get()); - auto task_progress = m_task_progress.value(task->getUid()); - task_progress->state = TaskStepState::Succeeded; - - disconnect(task.get(), 0, this, 0); - - emit stepProgress(*task_progress); - updateState(); - updateStepProgress(*task_progress, Operation::REMOVED); - startNext(); + subTaskFinished(task, TaskStepState::Succeeded); } void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg) { - m_done.insert(task.get(), task); - m_failed.insert(task.get(), task); - - m_doing.remove(task.get()); - - auto task_progress = m_task_progress.value(task->getUid()); - task_progress->state = TaskStepState::Failed; - - disconnect(task.get(), 0, this, 0); - - emit stepProgress(*task_progress); - updateState(); - updateStepProgress(*task_progress, Operation::REMOVED); - startNext(); + subTaskFinished(task, TaskStepState::Failed); } void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg) diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index 00b1d48d6..07ea58575 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -72,10 +72,11 @@ class ConcurrentTask : public Task { protected slots: void executeTask() override; - virtual void startNext(); + virtual void executeNextSubTask(); void subTaskSucceeded(Task::Ptr); - void subTaskFailed(Task::Ptr, const QString& msg); + virtual void subTaskFailed(Task::Ptr, const QString& msg); + void subTaskFinished(Task::Ptr, TaskStepState); void subTaskStatus(Task::Ptr task, const QString& msg); void subTaskDetails(Task::Ptr task, const QString& msg); void subTaskProgress(Task::Ptr task, qint64 current, qint64 total); @@ -90,6 +91,8 @@ class ConcurrentTask : public Task { virtual void updateState(); + void startSubTask(Task::Ptr task); + protected: QString m_name; QString m_step_status; @@ -107,6 +110,4 @@ class ConcurrentTask : public Task { qint64 m_stepProgress = 0; qint64 m_stepTotalProgress = 100; - - bool m_aborted = false; }; diff --git a/launcher/tasks/MultipleOptionsTask.cpp b/launcher/tasks/MultipleOptionsTask.cpp index 89187a26d..b95fc3d5e 100644 --- a/launcher/tasks/MultipleOptionsTask.cpp +++ b/launcher/tasks/MultipleOptionsTask.cpp @@ -34,11 +34,12 @@ */ #include "MultipleOptionsTask.h" +#include #include -MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : SequentialTask(parent, task_name) {} +MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : ConcurrentTask(parent, task_name, 1) {} -void MultipleOptionsTask::startNext() +void MultipleOptionsTask::executeNextSubTask() { if (m_done.size() != m_failed.size()) { emitSucceeded(); @@ -51,7 +52,11 @@ void MultipleOptionsTask::startNext() return; } - ConcurrentTask::startNext(); + ConcurrentTask::executeNextSubTask(); + // not sure why this is needed here but tests fail without it + // as the MultipleOptionsTask is yet to be used not sure if + // it works correcly + QCoreApplication::processEvents(); } void MultipleOptionsTask::updateState() diff --git a/launcher/tasks/MultipleOptionsTask.h b/launcher/tasks/MultipleOptionsTask.h index a344343ef..9a88a9999 100644 --- a/launcher/tasks/MultipleOptionsTask.h +++ b/launcher/tasks/MultipleOptionsTask.h @@ -34,18 +34,18 @@ */ #pragma once -#include "SequentialTask.h" +#include "ConcurrentTask.h" /* This task type will attempt to do run each of it's subtasks in sequence, * until one of them succeeds. When that happens, the remaining tasks will not run. * */ -class MultipleOptionsTask : public SequentialTask { +class MultipleOptionsTask : public ConcurrentTask { Q_OBJECT public: explicit MultipleOptionsTask(QObject* parent = nullptr, const QString& task_name = ""); ~MultipleOptionsTask() override = default; private slots: - void startNext() override; + void executeNextSubTask() override; void updateState() override; }; diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp index abf7536b9..180430c8c 100644 --- a/launcher/tasks/SequentialTask.cpp +++ b/launcher/tasks/SequentialTask.cpp @@ -36,18 +36,15 @@ #include "SequentialTask.h" #include +#include "tasks/ConcurrentTask.h" SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentTask(parent, task_name, 1) {} -void SequentialTask::startNext() +void SequentialTask::subTaskFailed(Task::Ptr task, const QString& msg) { - if (m_failed.size() > 0) { - emitFailed(tr("One of the tasks failed!")); - qWarning() << m_failed.constBegin()->get()->failReason(); - return; - } - - ConcurrentTask::startNext(); + emitFailed(msg); + qWarning() << m_failed.constBegin()->get()->failReason(); + ConcurrentTask::subTaskFailed(task, msg); } void SequentialTask::updateState() diff --git a/launcher/tasks/SequentialTask.h b/launcher/tasks/SequentialTask.h index cec3b2be8..a7c101ab4 100644 --- a/launcher/tasks/SequentialTask.h +++ b/launcher/tasks/SequentialTask.h @@ -50,7 +50,9 @@ class SequentialTask : public ConcurrentTask { explicit SequentialTask(QObject* parent = nullptr, QString task_name = ""); ~SequentialTask() override = default; + protected slots: + virtual void subTaskFailed(Task::Ptr, const QString& msg) override; + protected: - void startNext() override; void updateState() override; }; diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index abc9be905..0740ba0a3 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -37,13 +37,13 @@ class BasicTask_MultiStep : public Task { class BigConcurrentTask : public ConcurrentTask { Q_OBJECT - void startNext() override + void executeNextSubTask() override { // This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^)) // Each tasks thus adds 1024 * 4 bytes to the stack, at the very least. [[maybe_unused]] volatile std::array some_data_on_the_stack{}; - ConcurrentTask::startNext(); + ConcurrentTask::executeNextSubTask(); } }; @@ -71,11 +71,14 @@ class BigConcurrentTaskThread : public QThread { quit(); }); - m_deadline.start(); + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, &BigConcurrentTaskThread::start_timer, Qt::QueuedConnection); + } big_task.run(); exec(); } + void start_timer() { m_deadline.start(); } public: bool passed_the_deadline = false; From ab725eeb1816aea428107dcc9ed9184136c7f766 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 11:52:28 +0300 Subject: [PATCH 066/755] corected side and added loaders Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.cpp | 19 ++++++++++++++++++- launcher/modplatform/ModIndex.h | 4 +++- launcher/modplatform/flame/FlameModIndex.cpp | 6 ++++++ launcher/modplatform/modrinth/ModrinthAPI.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 19 ++++++++++++++++++- launcher/modplatform/packwiz/Packwiz.h | 1 + 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index fc79dff15..5b7624563 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -117,7 +117,7 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID) projectID.toString(); } -auto getModLoaderString(ModLoaderType type) -> const QString +auto getModLoaderAsString(ModLoaderType type) -> const QString { switch (type) { case NeoForge: @@ -138,4 +138,21 @@ auto getModLoaderString(ModLoaderType type) -> const QString return ""; } +auto getModLoaderFromString(QString type) -> const ModLoaderType +{ + if (type == "neoforge") + return NeoForge; + if (type == "forge") + return Forge; + if (type == "cauldron") + return Cauldron; + if (type == "liteloader") + return LiteLoader; + if (type == "fabric") + return Fabric; + if (type == "quilt") + return Quilt; + return {}; +} + } // namespace ModPlatform diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 72294c399..940dddea0 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -109,6 +109,7 @@ struct IndexedVersion { bool is_preferred = true; QString changelog; QList dependencies; + QString side; // this is for flame API // For internal use, not provided by APIs bool is_currently_selected = false; @@ -181,7 +182,8 @@ inline auto getOverrideDeps() -> QList QString getMetaURL(ResourceProvider provider, QVariant projectID); -auto getModLoaderString(ModLoaderType type) -> const QString; +auto getModLoaderAsString(ModLoaderType type) -> const QString; +auto getModLoaderFromString(QString type) -> const ModLoaderType; constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 345883c17..75c10d6f5 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -130,6 +130,12 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.loaders |= ModPlatform::Fabric; if (loader == "quilt") file.loaders |= ModPlatform::Quilt; + if (loader == "server" || loader == "client") { + if (file.side.isEmpty()) + file.side = loader; + else if (file.side != loader) + file.side = "both"; + } } file.addonId = Json::requireInteger(obj, "modId"); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index d0f0811b2..857719902 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -41,7 +41,7 @@ class ModrinthAPI : public NetworkResourceAPI { for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) { if (types & loader) { - l << getModLoaderString(loader); + l << getModLoaderAsString(loader); } } return l; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index e35567f24..90d7d0ed7 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -113,7 +113,8 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.provider = mod_pack.provider; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; - mod.side = stringToSide(mod_pack.side); + mod.side = stringToSide(mod_version.side.isEmpty() ? mod_pack.side : mod_version.side); + mod.loaders = mod_version.loaders; return mod; } @@ -181,6 +182,14 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) break; } + toml::array loaders; + for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, ModPlatform::Fabric, + ModPlatform::Quilt }) { + if (mod.loaders & loader) { + loaders.push_back(getModLoaderAsString(loader)); + } + } + if (!index_file.open(QIODevice::ReadWrite)) { qCritical() << QString("Could not open file %1!").arg(normalized_fname); return; @@ -192,6 +201,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, { "side", sideToString(mod.side).toStdString() }, + { "loader", loaders }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -276,6 +286,13 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); mod.side = stringToSide(stringEntry(table, "side")); + if (auto loaders = table["loaders"]; loaders && loaders.is_array()) { + for (auto&& loader : *loaders.as_array()) { + if (loader.is_string()) { + mod.loaders |= ModPlatform::getModLoaderFromString(QString::fromStdString(loader.as_string()->value_or(""))); + } + } + } } { // [download] info diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index dce198b0e..a61bb0503 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -41,6 +41,7 @@ class V1 { QString name{}; QString filename{}; Side side{ Side::UniversalSide }; + ModPlatform::ModLoaderTypes loaders; // [download] QString mode{}; From 749975e8ef96ba780aa910217911ff0af6ecbe5b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 12:46:23 +0300 Subject: [PATCH 067/755] made side and loaders visible to user Signed-off-by: Trial97 --- launcher/minecraft/mod/MetadataHandler.h | 2 ++ launcher/minecraft/mod/Mod.cpp | 29 +++++++++++++++ launcher/minecraft/mod/Mod.h | 3 ++ launcher/minecraft/mod/ModFolderModel.cpp | 35 ++++++++++++++++--- launcher/minecraft/mod/ModFolderModel.h | 12 ++++++- launcher/minecraft/mod/Resource.h | 2 +- .../modrinth/ModrinthCheckUpdate.cpp | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 4 +-- 8 files changed, 79 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 3496da2a0..ccdd7d559 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -52,4 +52,6 @@ class Metadata { static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); } static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } + + static auto modSideToString(ModSide side) -> QString { return Packwiz::V1::sideToString(side); } }; diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 310946379..132f99f7d 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -45,6 +45,7 @@ #include "MetadataHandler.h" #include "Version.h" #include "minecraft/mod/ModDetails.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" static ModPlatform::ProviderCapabilities ProviderCaps; @@ -109,6 +110,20 @@ std::pair Mod::compare(const Resource& other, SortType type) const return { compare_result, type == SortType::PROVIDER }; break; } + case SortType::SIDE: { + if (side() > cast_other->side()) + return { 1, type == SortType::SIDE }; + else if (side() < cast_other->side()) + return { -1, type == SortType::SIDE }; + break; + } + case SortType::LOADERS: { + if (loaders() > cast_other->loaders()) + return { 1, type == SortType::LOADERS }; + else if (loaders() < cast_other->loaders()) + return { -1, type == SortType::LOADERS }; + break; + } } return { 0, false }; } @@ -232,6 +247,20 @@ auto Mod::provider() const -> std::optional return {}; } +auto Mod::side() const -> Metadata::ModSide +{ + if (metadata()) + return metadata()->side; + return Metadata::ModSide::UniversalSide; +} + +auto Mod::loaders() const -> ModPlatform::ModLoaderTypes +{ + if (metadata()) + return metadata()->loaders; + return {}; +} + auto Mod::licenses() const -> const QList& { return details().licenses; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index e97ee9d3b..970f85a00 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -47,6 +47,7 @@ #include "ModDetails.h" #include "Resource.h" +#include "modplatform/ModIndex.h" class Mod : public Resource { Q_OBJECT @@ -70,6 +71,8 @@ class Mod : public Resource { auto licenses() const -> const QList&; auto issueTracker() const -> QString; auto metaurl() const -> QString; + auto side() const -> Metadata::ModSide; + auto loaders() const -> ModPlatform::ModLoaderTypes; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index a5f1489dd..631425a72 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,6 +52,8 @@ #include "Application.h" #include "Json.h" +#include "minecraft/mod/MetadataHandler.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" @@ -62,12 +64,15 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; + m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders" }); + m_column_names_translated = QStringList( + { tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Side"), tr("Loaders") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, + SortType::DATE, SortType::PROVIDER, SortType::SIDE, SortType::LOADERS }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -105,6 +110,20 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } + case SideColumn: { + return Metadata::modSideToString(at(row)->side()); + } + case LoadersColumn: { + QStringList loaders; + auto modLoaders = at(row)->loaders(); + for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, + ModPlatform::Fabric, ModPlatform::Quilt }) { + if (modLoaders & loader) { + loaders << getModLoaderAsString(loader); + } + } + return loaders.join(","); + } default: return QVariant(); } @@ -154,6 +173,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case DateColumn: case ProviderColumn: case ImageColumn: + case SideColumn: + case LoadersColumn: return columnNames().at(section); default: return QVariant(); @@ -171,6 +192,10 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The date and time this mod was last changed (or added)."); case ProviderColumn: return tr("Where the mod was downloaded from."); + case SideColumn: + return tr("On what environment the mod is running."); + case LoadersColumn: + return tr("The mod loader."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 61d840f9b..1623fadfe 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -61,7 +61,17 @@ class QFileSystemWatcher; class ModFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; + enum Columns { + ActiveColumn = 0, + ImageColumn, + NameColumn, + VersionColumn, + DateColumn, + ProviderColumn, + SideColumn, + LoadersColumn, + NUM_COLUMNS + }; enum ModStatusAction { Disable, Enable, Toggle }; ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c1ed49461..eefb5f8a5 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 9b7c53854..7d491f4fe 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -114,7 +114,7 @@ void ModrinthCheckUpdate::executeTask() ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt }; for (auto flag : flags) { if (m_loaders.value().testFlag(flag)) { - loader_filter = ModPlatform::getModLoaderString(flag); + loader_filter = ModPlatform::getModLoaderAsString(flag); break; } } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 90d7d0ed7..f15ab9e7a 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -186,7 +186,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, ModPlatform::Fabric, ModPlatform::Quilt }) { if (mod.loaders & loader) { - loaders.push_back(getModLoaderAsString(loader)); + loaders.push_back(getModLoaderAsString(loader).toStdString()); } } @@ -201,7 +201,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, { "side", sideToString(mod.side).toStdString() }, - { "loader", loaders }, + { "loaders", loaders }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, From b075711be08b4d3e3badb1def5b0f4d24e0ad51a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 16:03:14 +0300 Subject: [PATCH 068/755] ensured totalTimePlayed is allways greater than 0 Signed-off-by: Trial97 --- launcher/BaseInstance.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 725036395..66b44b229 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -278,6 +278,8 @@ void BaseInstance::setRunning(bool running) QDateTime timeEnded = QDateTime::currentDateTime(); qint64 current = settings()->get("totalTimePlayed").toLongLong(); + if (current < 0) + current = 0; settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded)); settings()->set("lastTimePlayed", m_timeStarted.secsTo(timeEnded)); @@ -290,6 +292,8 @@ void BaseInstance::setRunning(bool running) int64_t BaseInstance::totalTimePlayed() const { qint64 current = m_settings->get("totalTimePlayed").toLongLong(); + if (current < 0) + current = 0; if (m_isRunning) { QDateTime timeNow = QDateTime::currentDateTime(); return current + m_timeStarted.secsTo(timeNow); From 52c94eb568a22e95e7696af0a1177b1b3bf54aec Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 16:24:23 +0300 Subject: [PATCH 069/755] reset the setting if is under 0 Signed-off-by: Trial97 --- launcher/BaseInstance.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 66b44b229..d0b0ad3bc 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -64,6 +64,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("totalTimePlayed", 0); + if (m_settings->get("totalTimePlayed").toLongLong() < 0) + m_settings->set("totalTimePlayed", 0); m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("linkedInstances", "[]"); @@ -278,8 +280,6 @@ void BaseInstance::setRunning(bool running) QDateTime timeEnded = QDateTime::currentDateTime(); qint64 current = settings()->get("totalTimePlayed").toLongLong(); - if (current < 0) - current = 0; settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded)); settings()->set("lastTimePlayed", m_timeStarted.secsTo(timeEnded)); @@ -292,8 +292,6 @@ void BaseInstance::setRunning(bool running) int64_t BaseInstance::totalTimePlayed() const { qint64 current = m_settings->get("totalTimePlayed").toLongLong(); - if (current < 0) - current = 0; if (m_isRunning) { QDateTime timeNow = QDateTime::currentDateTime(); return current + m_timeStarted.secsTo(timeNow); From 0ff98c5876f73556e328cba352fb12566f889e08 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 21:22:59 +0300 Subject: [PATCH 070/755] allways enable dependencies Signed-off-by: Trial97 --- launcher/ui/dialogs/ModUpdateDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index a0c2f2f44..9499378a5 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -220,8 +220,8 @@ void ModUpdateDialog::checkCandidates() 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, dep->version.version_type, - changelog, dep->pack->provider, download_task + dep->pack->name, dep->version.hash, "", dep->version.version, dep->version.version_type, changelog, true, + dep->pack->provider, download_task }; appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString())); From b54376062e87b9f278b3953da5f7f1bb04695b49 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 17 Oct 2023 19:25:01 +0300 Subject: [PATCH 071/755] Added mcVersion column Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 15 ++++++++++++ launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 23 ++++++++++++------- launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/minecraft/mod/Resource.h | 2 +- launcher/modplatform/ModIndex.cpp | 2 +- launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 16 ++++++------- .../modrinth/ModrinthPackIndex.cpp | 10 ++++---- launcher/modplatform/packwiz/Packwiz.cpp | 18 +++++++++++++++ launcher/modplatform/packwiz/Packwiz.h | 1 + 11 files changed, 67 insertions(+), 24 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 132f99f7d..22e652319 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -124,6 +124,14 @@ std::pair Mod::compare(const Resource& other, SortType type) const return { -1, type == SortType::LOADERS }; break; } + case SortType::MC_VERSIONS: { + auto thisVersion = mcVersions().join(","); + auto otherVersion = cast_other->mcVersions().join(","); + auto compare_result = QString::compare(thisVersion, otherVersion, Qt::CaseInsensitive); + if (compare_result != 0) + return { compare_result, type == SortType::MC_VERSIONS }; + break; + } } return { 0, false }; } @@ -261,6 +269,13 @@ auto Mod::loaders() const -> ModPlatform::ModLoaderTypes return {}; } +auto Mod::mcVersions() const -> QStringList +{ + if (metadata()) + return metadata()->mcVersions; + return {}; +} + auto Mod::licenses() const -> const QList& { return details().licenses; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 970f85a00..92de65889 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -73,6 +73,7 @@ class Mod : public Resource { auto metaurl() const -> QString; auto side() const -> Metadata::ModSide; auto loaders() const -> ModPlatform::ModLoaderTypes; + auto mcVersions() const -> QStringList; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 631425a72..ef4ab957c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -64,15 +64,16 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders" }); - m_column_names_translated = QStringList( - { tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Side"), tr("Loaders") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, - SortType::DATE, SortType::PROVIDER, SortType::SIDE, SortType::LOADERS }; + m_column_names = + QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders", "Miecraft Versions" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), + tr("Side"), tr("Loaders"), tr("Miecraft Versions") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, + SortType::PROVIDER, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -122,7 +123,10 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const loaders << getModLoaderAsString(loader); } } - return loaders.join(","); + return loaders.join(", "); + } + case McVersionsColumn: { + return at(row)->mcVersions().join(", "); } default: return QVariant(); @@ -175,6 +179,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case ImageColumn: case SideColumn: case LoadersColumn: + case McVersionsColumn: return columnNames().at(section); default: return QVariant(); @@ -196,6 +201,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("On what environment the mod is running."); case LoadersColumn: return tr("The mod loader."); + case McVersionsColumn: + return tr("The supported minecraft versions."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 1623fadfe..f9a87a741 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -70,6 +70,7 @@ class ModFolderModel : public ResourceFolderModel { ProviderColumn, SideColumn, LoadersColumn, + McVersionsColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index eefb5f8a5..c46980837 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS, MC_VERSIONS }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 5b7624563..2cd7710e3 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -138,7 +138,7 @@ auto getModLoaderAsString(ModLoaderType type) -> const QString return ""; } -auto getModLoaderFromString(QString type) -> const ModLoaderType +auto getModLoaderFromString(QString type) -> ModLoaderType { if (type == "neoforge") return NeoForge; diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 940dddea0..7c472ec9b 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -183,7 +183,7 @@ inline auto getOverrideDeps() -> QList QString getMetaURL(ResourceProvider provider, QVariant projectID); auto getModLoaderAsString(ModLoaderType type) -> const QString; -auto getModLoaderFromString(QString type) -> const ModLoaderType; +auto getModLoaderFromString(QString type) -> ModLoaderType; constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 75c10d6f5..d56e46b23 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -117,20 +117,20 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> if (str.contains('.')) file.mcVersion.append(str); - auto loader = str.toLower(); - if (loader == "neoforge") + + if (auto loader = str.toLower(); loader == "neoforge") file.loaders |= ModPlatform::NeoForge; - if (loader == "forge") + else if (loader == "forge") file.loaders |= ModPlatform::Forge; - if (loader == "cauldron") + else if (loader == "cauldron") file.loaders |= ModPlatform::Cauldron; - if (loader == "liteloader") + else if (loader == "liteloader") file.loaders |= ModPlatform::LiteLoader; - if (loader == "fabric") + else if (loader == "fabric") file.loaders |= ModPlatform::Fabric; - if (loader == "quilt") + else if (loader == "quilt") file.loaders |= ModPlatform::Quilt; - if (loader == "server" || loader == "client") { + else if (loader == "server" || loader == "client") { if (file.side.isEmpty()) file.side = loader; else if (file.side != loader) diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 7d0893261..23770b7fb 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -152,15 +152,15 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t for (auto loader : loaders) { if (loader == "neoforge") file.loaders |= ModPlatform::NeoForge; - if (loader == "forge") + else if (loader == "forge") file.loaders |= ModPlatform::Forge; - if (loader == "cauldron") + else if (loader == "cauldron") file.loaders |= ModPlatform::Cauldron; - if (loader == "liteloader") + else if (loader == "liteloader") file.loaders |= ModPlatform::LiteLoader; - if (loader == "fabric") + else if (loader == "fabric") file.loaders |= ModPlatform::Fabric; - if (loader == "quilt") + else if (loader == "quilt") file.loaders |= ModPlatform::Quilt; } file.version = Json::requireString(obj, "name"); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index f15ab9e7a..0692a627d 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -115,6 +115,8 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.project_id = mod_pack.addonId; mod.side = stringToSide(mod_version.side.isEmpty() ? mod_pack.side : mod_version.side); mod.loaders = mod_version.loaders; + mod.mcVersions = mod_version.mcVersion; + mod.mcVersions.sort(); return mod; } @@ -189,6 +191,10 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) loaders.push_back(getModLoaderAsString(loader).toStdString()); } } + toml::array mcVersions; + for (auto version : mod.mcVersions) { + mcVersions.push_back(version.toStdString()); + } if (!index_file.open(QIODevice::ReadWrite)) { qCritical() << QString("Could not open file %1!").arg(normalized_fname); @@ -202,6 +208,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) { "filename", mod.filename.toStdString() }, { "side", sideToString(mod.side).toStdString() }, { "loaders", loaders }, + { "mcVersions", mcVersions }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -293,6 +300,17 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod } } } + if (auto versions = table["mcVersions"]; versions && versions.is_array()) { + for (auto&& version : *versions.as_array()) { + if (version.is_string()) { + auto ver = QString::fromStdString(version.as_string()->value_or("")); + if (!ver.isEmpty()) { + mod.mcVersions << ver; + } + } + } + mod.mcVersions.sort(); + } } { // [download] info diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index a61bb0503..5cf99e74a 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -42,6 +42,7 @@ class V1 { QString filename{}; Side side{ Side::UniversalSide }; ModPlatform::ModLoaderTypes loaders; + QStringList mcVersions; // [download] QString mode{}; From fea4c4eba8bd6d919e1fd2cb96a25b2bc0968894 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 18 Oct 2023 09:00:45 +0300 Subject: [PATCH 072/755] Updated filters Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 1 + launcher/modplatform/ModIndex.cpp | 3 +- .../ui/dialogs/ResourceDownloadDialog.cpp | 1 + launcher/ui/pages/modplatform/ModModel.cpp | 10 +- launcher/ui/pages/modplatform/ModPage.cpp | 9 +- launcher/ui/pages/modplatform/ModPage.h | 3 +- .../modplatform/flame/FlameResourceModels.cpp | 1 + launcher/ui/widgets/ModFilterWidget.cpp | 250 ++++++++++-------- launcher/ui/widgets/ModFilterWidget.h | 55 ++-- launcher/ui/widgets/ModFilterWidget.ui | 156 +++++++++-- 10 files changed, 311 insertions(+), 178 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index df8c690af..deec0db7c 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -23,6 +23,7 @@ #include #include "Json.h" #include "QObjectPtr.h" +#include "minecraft/PackProfile.h" #include "minecraft/mod/MetadataHandler.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 2cd7710e3..d2113320a 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -94,10 +94,9 @@ auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString t { QCryptographicHash::Algorithm algo = QCryptographicHash::Sha1; switch (p) { - case ResourceProvider::MODRINTH: { + case ResourceProvider::MODRINTH: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Sha512; break; - } case ResourceProvider::FLAME: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Md5; break; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index dc7cfff06..f21502365 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -27,6 +27,7 @@ #include "Application.h" #include "ResourceDownloadTask.h" +#include "minecraft/PackProfile.h" #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index c628f74ac..3b53d1348 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -25,15 +25,18 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() Q_ASSERT(m_filter); std::optional> versions{}; + auto loaders = profile->getSupportedModLoaders(); { // Version filter if (!m_filter->versions.empty()) versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; } auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) @@ -45,10 +48,13 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en Q_ASSERT(m_filter); std::optional> versions{}; + auto loaders = profile->getSupportedModLoaders(); if (!m_filter->versions.empty()) versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; - return { pack, versions, profile->getSupportedModLoaders() }; + return { pack, versions, loaders }; } ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index d6cc1fdcc..838cf21c8 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -71,7 +71,6 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount()); - m_filter_widget->setInstance(&static_cast(m_base_instance)); m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, @@ -89,13 +88,14 @@ void ModPage::filterMods() void ModPage::triggerSearch() { + auto changed = m_filter_widget->changed(); m_filter = m_filter_widget->getFilter(); m_ui->packView->clearSelection(); m_ui->packDescription->clear(); m_ui->versionSelectionBox->clear(); updateSelectionButton(); - static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), m_filter_widget->changed()); + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), changed); m_fetch_progress.watch(m_model->activeSearchJob().get()); } @@ -116,6 +116,9 @@ void ModPage::updateVersionList() auto packProfile = (dynamic_cast(m_base_instance)).getPackProfile(); QString mcVersion = packProfile->getComponentVersion("net.minecraft"); + auto loaders = packProfile->getSupportedModLoaders(); + if (m_filter->loaders) + loaders = m_filter->loaders; auto current_pack = getCurrentPack(); if (!current_pack) @@ -124,7 +127,7 @@ void ModPage::updateVersionList() auto version = current_pack->versions[i]; bool valid = false; for (auto& mcVer : m_filter->versions) { - if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) { + if (validateVersion(version, mcVer.toString(), loaders)) { valid = true; break; } diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 5a43e49a6..6efb318ea 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -31,8 +31,7 @@ class ModPage : public ResourcePage { auto page = new T(dialog, instance); auto model = static_cast(page->getModel()); - auto filter_widget = - ModFilterWidget::create(static_cast(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); + auto filter_widget = ModFilterWidget::create(&static_cast(instance), page); page->setFilterWidget(filter_widget); model->setFilter(page->getFilter()); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 7d18e72a6..39a2a0d6a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -6,6 +6,7 @@ #include "Json.h" +#include "minecraft/PackProfile.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index c2c099eeb..62a8eb154 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,13 +1,70 @@ #include "ModFilterWidget.h" +#include +#include +#include "BaseVersionList.h" +#include "meta/Index.h" +#include "modplatform/ModIndex.h" #include "ui_ModFilterWidget.h" #include "Application.h" +#include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(Version default_version, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, QWidget* parent) { - auto filter_widget = new ModFilterWidget(default_version, parent); + return unique_qobject_ptr(new ModFilterWidget(instance, parent)); +} - if (!filter_widget->versionList()->isLoaded()) { +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) + : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) +{ + ui->setupUi(this); + + m_versions_proxy = new VersionProxyModel(this); + + ui->versionsCb->setModel(m_versions_proxy); + + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + + ui->versionsCb->setStyleSheet("combobox-popup: 0;"); + connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); + connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + + connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabricCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quiltCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->liteLoaderCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->cauldronCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + + ui->liteLoaderCb->hide(); + ui->cauldronCb->hide(); + + connect(ui->serverEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + connect(ui->clientEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + + connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); + + setHidden(true); + loadVersionList(); + prepareBasicFilter(); +} + +auto ModFilterWidget::getFilter() -> std::shared_ptr +{ + m_filter_changed = false; + emit filterUnchanged(); + return m_filter; +} + +ModFilterWidget::~ModFilterWidget() +{ + delete ui; +} + +void ModFilterWidget::loadVersionList() +{ + m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); + if (!m_version_list->isLoaded()) { QEventLoop load_version_list_loop; QTimer time_limit_for_list_load; @@ -16,10 +73,12 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit); time_limit_for_list_load.start(4000); - auto task = filter_widget->versionList()->getLoadTask(); + auto task = m_version_list->getLoadTask(); - connect(task.get(), &Task::failed, - [filter_widget] { filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); }); + connect(task.get(), &Task::failed, [this] { + ui->versionsCb->setEnabled(false); + ui->snapshotsCb->setEnabled(false); + }); connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); if (!task->isRunning()) @@ -29,128 +88,93 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers if (time_limit_for_list_load.isActive()) time_limit_for_list_load.stop(); } - - return unique_qobject_ptr(filter_widget); + m_versions_proxy->setSourceModel(m_version_list.get()); } -ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget) +void ModFilterWidget::prepareBasicFilter() { - ui->setupUi(this); - - m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict); - ui->strictVersionButton->click(); - m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major); - m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All); - // m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between); - - connect(&m_mcVersion_buttons, SIGNAL(idClicked(int)), this, SLOT(onVersionFilterChanged(int))); - - m_filter->versions.push_front(def); - - m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); - setHidden(true); + m_filter->hideInstalled = false; + m_filter->side = ""; // or "both"t + auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + ui->neoForgeCb->setChecked(loaders & ModPlatform::NeoForge); + ui->forgeCb->setChecked(loaders & ModPlatform::Forge); + ui->fabricCb->setChecked(loaders & ModPlatform::Fabric); + ui->quiltCb->setChecked(loaders & ModPlatform::Quilt); + ui->liteLoaderCb->setChecked(loaders & ModPlatform::LiteLoader); + ui->cauldronCb->setChecked(loaders & ModPlatform::Cauldron); + m_filter->loaders = loaders; + auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); + m_filter->versions.push_front(Version{ def }); + m_versions_proxy->setCurrentVersion(def); + ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row()); } -void ModFilterWidget::setInstance(MinecraftInstance* instance) +void ModFilterWidget::onIncludeSnapshotsChanged() { - m_instance = instance; - - ui->strictVersionButton->setText(tr("Strict match (= %1)").arg(mcVersionStr())); - - // we can't do this for snapshots sadly - if (mcVersionStr().contains('.')) { - auto mcVersionSplit = mcVersionStr().split("."); - ui->majorVersionButton->setText(tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1])); - } else { - ui->majorVersionButton->setText(tr("Major version match (unsupported)")); - disableVersionButton(Major); - } - ui->allVersionsButton->setText(tr("Any version")); - // ui->betweenVersionsButton->setText( - // tr("Between two versions")); + QString filter = "(release)"; + if (ui->snapshotsCb->isChecked()) + filter += "|(snapshot)"; + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); } -auto ModFilterWidget::getFilter() -> std::shared_ptr +void ModFilterWidget::onVersionFilterChanged() { - m_last_version_id = m_version_id; - emit filterUnchanged(); - return m_filter; -} - -void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason) -{ - QAbstractButton* btn = nullptr; - - switch (id) { - case (VersionButtonID::Strict): - btn = ui->strictVersionButton; - break; - case (VersionButtonID::Major): - btn = ui->majorVersionButton; - break; - case (VersionButtonID::All): - btn = ui->allVersionsButton; - break; - case (VersionButtonID::Between): - default: - break; - } - - if (btn) { - btn->setEnabled(false); - if (!reason.isEmpty()) - btn->setText(btn->text() + QString(" (%1)").arg(reason)); - } -} - -void ModFilterWidget::onVersionFilterChanged(int id) -{ - // ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between); - // ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between); - - int index = 1; - - auto cast_id = (VersionButtonID)id; - if (cast_id != m_version_id) { - m_version_id = cast_id; - } else { - return; - } - + auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); m_filter->versions.clear(); + m_filter->versions.push_front(version); + m_filter_changed = true; + emit filterChanged(); +} - switch (cast_id) { - case (VersionButtonID::Strict): - m_filter->versions.push_front(mcVersion()); - break; - case (VersionButtonID::Major): { - auto versionSplit = mcVersionStr().split("."); - - auto major_version = QString("%1.%2").arg(versionSplit[0], versionSplit[1]); - QString version_str = major_version; - - while (m_version_list->hasVersion(version_str)) { - m_filter->versions.emplace_back(version_str); - version_str = QString("%1.%2").arg(major_version, QString::number(index++)); - } - - break; - } - case (VersionButtonID::All): - // Empty list to avoid enumerating all versions :P - break; - case (VersionButtonID::Between): - // TODO - break; - } - - if (changed()) +void ModFilterWidget::onLoadersFilterChanged() +{ + ModPlatform::ModLoaderTypes loaders; + if (ui->neoForgeCb->isChecked()) + loaders |= ModPlatform::NeoForge; + if (ui->forgeCb->isChecked()) + loaders |= ModPlatform::Forge; + if (ui->fabricCb->isChecked()) + loaders |= ModPlatform::Fabric; + if (ui->quiltCb->isChecked()) + loaders |= ModPlatform::Quilt; + if (ui->cauldronCb->isChecked()) + loaders |= ModPlatform::Cauldron; + if (ui->liteLoaderCb->isChecked()) + loaders |= ModPlatform::LiteLoader; + m_filter_changed = loaders != m_filter->loaders; + m_filter->loaders = loaders; + if (m_filter_changed) emit filterChanged(); else emit filterUnchanged(); } -ModFilterWidget::~ModFilterWidget() +void ModFilterWidget::onSideFilterChanged() { - delete ui; + QString side; + if (ui->serverEnv->isChecked()) + side = "server"; + if (ui->clientEnv->isChecked()) { + if (side.isEmpty()) + side = "client"; + else + side = ""; // or both + } + m_filter_changed = side != m_filter->side; + m_filter->side = side; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); } + +void ModFilterWidget::onHideInstalledFilterChanged() +{ + auto hide = ui->hide_installed->isChecked(); + m_filter_changed = hide != m_filter->hideInstalled; + m_filter->hideInstalled = hide; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); +} \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index ed6cd0ea7..b92437a4f 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -5,11 +5,11 @@ #include "Version.h" -#include "meta/Index.h" +#include "VersionProxyModel.h" #include "meta/VersionList.h" #include "minecraft/MinecraftInstance.h" -#include "minecraft/PackProfile.h" +#include "modplatform/ModIndex.h" class MinecraftInstance; @@ -20,42 +20,37 @@ class ModFilterWidget; class ModFilterWidget : public QTabWidget { Q_OBJECT public: - enum VersionButtonID { Strict = 0, Major = 1, All = 2, Between = 3 }; - struct Filter { std::list versions; + ModPlatform::ModLoaderTypes loaders; + QString side; + bool hideInstalled; - bool operator==(const Filter& other) const { return versions == other.versions; } + bool operator==(const Filter& other) const + { + return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions; + } bool operator!=(const Filter& other) const { return !(*this == other); } }; - std::shared_ptr m_filter; - - public: - static unique_qobject_ptr create(Version default_version, QWidget* parent = nullptr); - ~ModFilterWidget(); - - void setInstance(MinecraftInstance* instance); - - /// By default all buttons are enabled - void disableVersionButton(VersionButtonID, QString reason = {}); + static unique_qobject_ptr create(MinecraftInstance* instance, QWidget* parent = nullptr); + virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; - auto changed() const -> bool { return m_last_version_id != m_version_id; } - - Meta::VersionList::Ptr versionList() { return m_version_list; } + auto changed() const -> bool { return m_filter_changed; } private: - ModFilterWidget(Version def, QWidget* parent = nullptr); + ModFilterWidget(MinecraftInstance* instance, QWidget* parent = nullptr); - inline auto mcVersionStr() const -> QString - { - return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; - } - inline auto mcVersion() const -> Version { return { mcVersionStr() }; } + void loadVersionList(); + void prepareBasicFilter(); private slots: - void onVersionFilterChanged(int id); + void onVersionFilterChanged(); + void onLoadersFilterChanged(); + void onSideFilterChanged(); + void onHideInstalledFilterChanged(); + void onIncludeSnapshotsChanged(); public: signals: @@ -66,13 +61,9 @@ class ModFilterWidget : public QTabWidget { Ui::ModFilterWidget* ui; MinecraftInstance* m_instance = nullptr; - - /* Version stuff */ - QButtonGroup m_mcVersion_buttons; + std::shared_ptr m_filter; + bool m_filter_changed = false; Meta::VersionList::Ptr m_version_list; - - /* Used to tell if the filter was changed since the last getFilter() call */ - VersionButtonID m_last_version_id = VersionButtonID::Strict; - VersionButtonID m_version_id = VersionButtonID::Strict; + VersionProxyModel* m_versions_proxy = nullptr; }; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index ebe5d2be1..74e27e5f7 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -7,7 +7,7 @@ 0 0 400 - 300 + 127 @@ -16,35 +16,143 @@ 0 + + 0 + Minecraft versions + + + + + 0 + 0 + + + + 5 + + + QComboBox::AdjustToContentsOnFirstShow + + + + + + + Include Snapshots + + + + + + + + Loaders + + + + QLayout::SetMinimumSize + + + + + false + + + LiteLoader + + + + + + + false + + + Cauldron + + + - - - - - allVersions - - - - - - - strictVersion - - - - - - - majorVersion - - - - + + + NeoForge + + + + + + + Forge + + + + + + + Quilt + + + + + + + Fabric + + + + + + + + Others + + + + + + Environments + + + + QLayout::SetDefaultConstraint + + + + + Client + + + + + + + Server + + + + + + + + + + Instaled status + + + + + + Hide already installed + + + + + From 03a27d5b5a43116bafbd08aa8a2f5b7a592710b8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 19 Oct 2023 19:47:48 +0300 Subject: [PATCH 073/755] Updated Managed Packs Signed-off-by: Trial97 --- .../flame/FlameInstanceCreationTask.cpp | 3 +++ .../modrinth/ModrinthInstanceCreationTask.cpp | 5 +++- .../ui/pages/instance/ManagedPackPage.cpp | 23 +++++++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 2a26ce944..c8df8b627 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -427,6 +427,9 @@ bool FlameCreationTask::createInstance() // Don't add managed info to packs without an ID (most likely imported from ZIP) if (!m_managed_id.isEmpty()) instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version); + else + instance.setManagedPack("flame", "", name(), "", ""); + instance.setName(name()); m_mod_id_resolver.reset(new Flame::FileResolvingTask(APPLICATION->network(), m_pack)); diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index e732ad39c..96d9c84d2 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -226,6 +226,9 @@ bool ModrinthCreationTask::createInstance() // Don't add managed info to packs without an ID (most likely imported from ZIP) if (!m_managed_id.isEmpty()) instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version()); + else + instance.setManagedPack("modrinth", "", name(), "", ""); + instance.setName(name()); instance.saveNow(); @@ -289,7 +292,7 @@ bool ModrinthCreationTask::createInstance() // Only change the name if it didn't use a custom name, so that the previous custom name // is preserved, but if we're using the original one, we update the version string. // NOTE: This needs to come before the copyManagedPack call! - if (inst->name().contains(inst->getManagedPackVersionName())) { + if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance.name()) { if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange) inst->setName(instance.name()); } diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 26a8302db..c419066a0 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -131,6 +131,22 @@ ManagedPackPage::~ManagedPackPage() void ManagedPackPage::openedImpl() { + if (m_inst->getManagedPackID().isEmpty()) { + ui->packVersion->hide(); + ui->packVersionLabel->hide(); + ui->packOrigin->hide(); + ui->packOriginLabel->hide(); + ui->versionsComboBox->hide(); + ui->updateButton->hide(); + ui->updateToVersionLabel->hide(); + ui->updateFromFileButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + ui->packName->setText(m_inst->name()); + ui->changelogTextBrowser->setText(tr("This is a local modpack.\n" + "This can be updated only using a file in %1 format\n") + .arg(displayName())); + return; + } ui->packName->setText(m_inst->getManagedPackName()); ui->packVersion->setText(m_inst->getManagedPackVersionName()); ui->packOrigin->setText(tr("Website: %2 | Pack ID: %3 | Version ID: %4") @@ -355,6 +371,8 @@ void ModrinthManagedPackPage::update() void ModrinthManagedPackPage::updateFromFile() { auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "Modrinth pack (*.mrpack *.zip)"); + if (output.isEmpty()) + return; QMap extra_info; extra_info.insert("pack_id", m_inst->getManagedPackID()); extra_info.insert("pack_version_id", QString()); @@ -471,8 +489,7 @@ void FlameManagedPackPage::parseManagedPack() QString FlameManagedPackPage::url() const { - // FIXME: We should display the websiteUrl field, but this requires doing the API request first :( - return {}; + return "https://www.curseforge.com/projects/" + m_inst->getManagedPackID(); } void FlameManagedPackPage::suggestVersion() @@ -519,6 +536,8 @@ void FlameManagedPackPage::update() void FlameManagedPackPage::updateFromFile() { auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), "CurseForge pack (*.zip)"); + if (output.isEmpty()) + return; QMap extra_info; extra_info.insert("pack_id", m_inst->getManagedPackID()); From 1ac9757a86884d3c8299b8bf3749a82c9e7339ff Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 19 Oct 2023 21:51:51 +0300 Subject: [PATCH 074/755] Added back the processEvents Signed-off-by: Trial97 --- launcher/tasks/ConcurrentTask.cpp | 3 +++ launcher/tasks/MultipleOptionsTask.cpp | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 77d9ab29d..0183b5124 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -35,6 +35,7 @@ */ #include "ConcurrentTask.h" +#include #include #include "tasks/Task.h" @@ -132,6 +133,8 @@ void ConcurrentTask::executeNextSubTask() } startSubTask(m_queue.dequeue()); + + QCoreApplication::processEvents(); } void ConcurrentTask::startSubTask(Task::Ptr next) diff --git a/launcher/tasks/MultipleOptionsTask.cpp b/launcher/tasks/MultipleOptionsTask.cpp index b95fc3d5e..5afe03964 100644 --- a/launcher/tasks/MultipleOptionsTask.cpp +++ b/launcher/tasks/MultipleOptionsTask.cpp @@ -34,7 +34,6 @@ */ #include "MultipleOptionsTask.h" -#include #include MultipleOptionsTask::MultipleOptionsTask(QObject* parent, const QString& task_name) : ConcurrentTask(parent, task_name, 1) {} @@ -53,10 +52,6 @@ void MultipleOptionsTask::executeNextSubTask() } ConcurrentTask::executeNextSubTask(); - // not sure why this is needed here but tests fail without it - // as the MultipleOptionsTask is yet to be used not sure if - // it works correcly - QCoreApplication::processEvents(); } void MultipleOptionsTask::updateState() From 55946c8923bf0e1b804944934aec2a42478f84a4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 19 Oct 2023 23:53:26 +0300 Subject: [PATCH 075/755] first attempt at a combobox Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 2 + launcher/ui/widgets/CheckComboBox.cpp | 205 ++++++++++++++++++++++++ launcher/ui/widgets/CheckComboBox.h | 61 +++++++ launcher/ui/widgets/ModFilterWidget.cpp | 8 +- launcher/ui/widgets/ModFilterWidget.ui | 9 +- 5 files changed, 280 insertions(+), 5 deletions(-) create mode 100644 launcher/ui/widgets/CheckComboBox.cpp create mode 100644 launcher/ui/widgets/CheckComboBox.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d15dc85de..a6c3a3e08 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1035,6 +1035,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/InstallLoaderDialog.h # GUI - widgets + ui/widgets/CheckComboBox.cpp + ui/widgets/CheckComboBox.h ui/widgets/Common.cpp ui/widgets/Common.h ui/widgets/CustomCommands.cpp diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp new file mode 100644 index 000000000..9c9006c74 --- /dev/null +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "CheckComboBox.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "BaseVersionList.h" + +class CheckComboModel : public QIdentityProxyModel { + Q_OBJECT + + public: + explicit CheckComboModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {} + + virtual Qt::ItemFlags flags(const QModelIndex& index) const { return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable; } + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const + { + if (role == Qt::CheckStateRole) { + auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + return checked.contains(txt) ? Qt::Checked : Qt::Unchecked; + } + if (role == Qt::DisplayRole) + return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole); + return {}; + } + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) + { + if (role == Qt::CheckStateRole) { + auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + if (checked.contains(txt)) { + checked.removeOne(txt); + } else { + checked.push_back(txt); + } + emit dataChanged(index, index); + emit checkStateChanged(); + return true; + } + return QIdentityProxyModel::setData(index, value, role); + } + QStringList getChecked() { return checked; } + + signals: + void checkStateChanged(); + + private: + QStringList checked; +}; + +CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",") +{ + // read-only contents + // QLineEdit* lineEdit = new QLineEdit(this); + // lineEdit->setReadOnly(false); + // setLineEdit(lineEdit); + // lineEdit->disconnect(this); + setInsertPolicy(QComboBox::NoInsert); + + view()->installEventFilter(this); + view()->window()->installEventFilter(this); + view()->viewport()->installEventFilter(this); + this->installEventFilter(this); +} + +void CheckComboBox::setModel(QAbstractItemModel* new_model) +{ + auto proxy = new CheckComboModel(this); + proxy->setSourceModel(new_model); + model()->disconnect(this); + QComboBox::setModel(proxy); + connect(this, QOverload::of(&QComboBox::activated), this, &CheckComboBox::toggleCheckState); + connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::updateCheckedItems); + connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::updateCheckedItems); + connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::updateCheckedItems); +} + +void CheckComboBox::hidePopup() +{ + if (containerMousePress) + QComboBox::hidePopup(); +} + +void CheckComboBox::updateCheckedItems() +{ + QStringList items = checkedItems(); + if (items.isEmpty()) + setEditText(defaultText()); + else + setEditText(items.join(separator())); + + emit checkedItemsChanged(items); +} + +QString CheckComboBox::defaultText() const +{ + return m_default_text; +} + +void CheckComboBox::setDefaultText(const QString& text) +{ + if (m_default_text != text) { + m_default_text = text; + updateCheckedItems(); + } +} + +QString CheckComboBox::separator() const +{ + return m_separator; +} + +void CheckComboBox::setSeparator(const QString& separator) +{ + if (m_separator != separator) { + m_separator = separator; + updateCheckedItems(); + } +} + +bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) +{ + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { + QKeyEvent* keyEvent = static_cast(event); + if (receiver == this && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { + showPopup(); + return true; + } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Escape) { + // it is important to call QComboBox implementation + QComboBox::hidePopup(); + return (keyEvent->key() != Qt::Key_Escape); + } + } + case QEvent::MouseButtonPress: + containerMousePress = (receiver == view()->window()); + break; + case QEvent::MouseButtonRelease: + containerMousePress = false; + break; + default: + break; + } + return false; +} + +void CheckComboBox::toggleCheckState(int index) +{ + QVariant value = itemData(index, Qt::CheckStateRole); + if (value.isValid()) { + Qt::CheckState state = static_cast(value.toInt()); + setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole); + } + updateCheckedItems(); +} + +Qt::CheckState CheckComboBox::itemCheckState(int index) const +{ + return static_cast(itemData(index, Qt::CheckStateRole).toInt()); +} + +void CheckComboBox::setItemCheckState(int index, Qt::CheckState state) +{ + setItemData(index, state, Qt::CheckStateRole); +} + +QStringList CheckComboBox::checkedItems() const +{ + if (model()) + return dynamic_cast(model())->getChecked(); + return {}; +} + +void CheckComboBox::setCheckedItems(const QStringList& items) +{ + foreach (auto text, items) { + auto index = findText(text); + setItemCheckState(index, index != -1 ? Qt::Checked : Qt::Unchecked); + } +} + +#include "CheckComboBox.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h new file mode 100644 index 000000000..277dd5fb8 --- /dev/null +++ b/launcher/ui/widgets/CheckComboBox.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +class CheckComboBox : public QComboBox { + Q_OBJECT + + public: + explicit CheckComboBox(QWidget* parent = nullptr); + virtual ~CheckComboBox() = default; + + virtual void hidePopup() override; + + QString defaultText() const; + void setDefaultText(const QString& text); + + Qt::CheckState itemCheckState(int index) const; + void setItemCheckState(int index, Qt::CheckState state); + + QString separator() const; + void setSeparator(const QString& separator); + + QStringList checkedItems() const; + + virtual void setModel(QAbstractItemModel* model) override; + + public slots: + void setCheckedItems(const QStringList& items); + + signals: + void checkedItemsChanged(const QStringList& items); + + private: + void updateCheckedItems(); + bool eventFilter(QObject* receiver, QEvent* event) override; + void toggleCheckState(int index); + + private: + QString m_default_text; + QString m_separator; + bool containerMousePress; +}; \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 62a8eb154..5ed2e34f6 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -105,8 +105,7 @@ void ModFilterWidget::prepareBasicFilter() m_filter->loaders = loaders; auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); m_filter->versions.push_front(Version{ def }); - m_versions_proxy->setCurrentVersion(def); - ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row()); + ui->versionsCb->setCheckedItems({ def }); } void ModFilterWidget::onIncludeSnapshotsChanged() @@ -119,9 +118,10 @@ void ModFilterWidget::onIncludeSnapshotsChanged() void ModFilterWidget::onVersionFilterChanged() { - auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); + auto versions = ui->versionsCb->checkedItems(); m_filter->versions.clear(); - m_filter->versions.push_front(version); + for (auto version : versions) + m_filter->versions.push_back(version); m_filter_changed = true; emit filterChanged(); } diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 74e27e5f7..e27de6f1a 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -25,7 +25,7 @@ - + 0 @@ -157,6 +157,13 @@ + + + CheckComboBox + QComboBox +
ui/widgets/CheckComboBox.h
+
+
From 6883c195795a8bb61a7ab50869d60265ddebc3d8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 00:44:36 +0300 Subject: [PATCH 076/755] Fixed the version combobox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 16 +++++++--------- launcher/ui/widgets/ModFilterWidget.cpp | 23 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 9c9006c74..f826c1afe 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -39,17 +39,17 @@ class CheckComboModel : public QIdentityProxyModel { virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const { if (role == Qt::CheckStateRole) { - auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString(); return checked.contains(txt) ? Qt::Checked : Qt::Unchecked; } if (role == Qt::DisplayRole) - return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole); + return QIdentityProxyModel::data(index, Qt::DisplayRole); return {}; } virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) { if (role == Qt::CheckStateRole) { - auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString(); if (checked.contains(txt)) { checked.removeOne(txt); } else { @@ -72,11 +72,10 @@ class CheckComboModel : public QIdentityProxyModel { CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",") { - // read-only contents - // QLineEdit* lineEdit = new QLineEdit(this); - // lineEdit->setReadOnly(false); - // setLineEdit(lineEdit); - // lineEdit->disconnect(this); + QLineEdit* lineEdit = new QLineEdit(this); + lineEdit->setReadOnly(false); + setLineEdit(lineEdit); + lineEdit->disconnect(this); setInsertPolicy(QComboBox::NoInsert); view()->installEventFilter(this); @@ -150,7 +149,6 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) showPopup(); return true; } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Escape) { - // it is important to call QComboBox implementation QComboBox::hidePopup(); return (keyEvent->key() != Qt::Key_Escape); } diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 5ed2e34f6..5a67757b5 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -14,6 +14,20 @@ unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* i return unique_qobject_ptr(new ModFilterWidget(instance, parent)); } +class VersionBasicModel : public QIdentityProxyModel { + Q_OBJECT + + public: + explicit VersionBasicModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {} + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override + { + if (role == Qt::DisplayRole) + return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole); + return {}; + } +}; + ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) { @@ -21,7 +35,10 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) m_versions_proxy = new VersionProxyModel(this); - ui->versionsCb->setModel(m_versions_proxy); + auto proxy = new VersionBasicModel(this); + proxy->setSourceModel(m_versions_proxy); + ui->versionsCb->setModel(proxy); + ui->versionsCb->setSeparator("| "); m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); @@ -177,4 +194,6 @@ void ModFilterWidget::onHideInstalledFilterChanged() emit filterChanged(); else emit filterUnchanged(); -} \ No newline at end of file +} + +#include "ModFilterWidget.moc" From 35a8ab2393e132481e7e0ee2a2eca338918d17f6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 10:32:45 +0300 Subject: [PATCH 077/755] Removed processEvents Signed-off-by: Trial97 --- launcher/tasks/ConcurrentTask.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 0183b5124..92b13cfa8 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -35,7 +35,6 @@ */ #include "ConcurrentTask.h" -#include #include #include "tasks/Task.h" @@ -69,7 +68,7 @@ void ConcurrentTask::addTask(Task::Ptr task) void ConcurrentTask::executeTask() { - for (auto i = 0; i <= m_total_max_size; i++) + for (auto i = 0; i < m_total_max_size; i++) executeNextSubTask(); } @@ -133,8 +132,6 @@ void ConcurrentTask::executeNextSubTask() } startSubTask(m_queue.dequeue()); - - QCoreApplication::processEvents(); } void ConcurrentTask::startSubTask(Task::Ptr next) From a9d7af909614404b6cc58ccc568d46d505f541cd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 16:44:37 +0300 Subject: [PATCH 078/755] removed some unused stuff Signed-off-by: Trial97 --- launcher/ui/dialogs/AboutDialog.h | 4 ---- launcher/ui/pages/instance/VersionPage.cpp | 7 ------- launcher/ui/pages/instance/VersionPage.h | 1 - 3 files changed, 12 deletions(-) diff --git a/launcher/ui/dialogs/AboutDialog.h b/launcher/ui/dialogs/AboutDialog.h index 356f005e0..5da686b5f 100644 --- a/launcher/ui/dialogs/AboutDialog.h +++ b/launcher/ui/dialogs/AboutDialog.h @@ -15,7 +15,6 @@ #pragma once -#include #include namespace Ui { @@ -31,7 +30,4 @@ class AboutDialog : public QDialog { private: Ui::AboutDialog* ui; - - NetJob::Ptr netJob; - QByteArray dataSink; }; diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index 2918261d2..e425269c8 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -295,13 +295,6 @@ void VersionPage::on_actionRemove_triggered() m_container->refreshContainer(); } -void VersionPage::on_actionInstall_mods_triggered() -{ - if (m_container) { - m_container->selectPage("mods"); - } -} - void VersionPage::on_actionAdd_to_Minecraft_jar_triggered() { auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h index 6915be883..951643743 100644 --- a/launcher/ui/pages/instance/VersionPage.h +++ b/launcher/ui/pages/instance/VersionPage.h @@ -80,7 +80,6 @@ class VersionPage : public QMainWindow, public BasePage { void on_actionAdd_Agents_triggered(); void on_actionRevert_triggered(); void on_actionEdit_triggered(); - void on_actionInstall_mods_triggered(); void on_actionCustomize_triggered(); void on_actionDownload_All_triggered(); From 4850434c67017622cf7b082728cf56a3b42d7920 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 12:16:02 +0300 Subject: [PATCH 079/755] added releaseType? Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.ui | 75 +++++++++++++++++++------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index e27de6f1a..3113f2df2 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -17,7 +17,7 @@ - 0 + 2 @@ -57,23 +57,10 @@ QLayout::SetMinimumSize - - - - false - + + - LiteLoader - - - - - - - false - - - Cauldron + Fabric @@ -91,6 +78,16 @@ + + + + false + + + Cauldron + + + @@ -98,10 +95,48 @@ - - + + + + false + - Fabric + LiteLoader + + + +
+ + + + Release type + + + + + + Release + + + + + + + Beta + + + + + + + Alpha + + + + + + + Unknown From 9e85297f7af495ecf8c34ea62a3559b712ff8e14 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 18:28:33 +0300 Subject: [PATCH 080/755] Connected filters Signed-off-by: Trial97 --- launcher/modplatform/ResourceAPI.h | 1 + launcher/modplatform/flame/FlameModIndex.cpp | 7 +-- launcher/modplatform/modrinth/ModrinthAPI.h | 18 +++++++ .../modrinth/ModrinthPackIndex.cpp | 7 +-- launcher/ui/pages/modplatform/ModModel.cpp | 53 +++++++++++++++--- launcher/ui/pages/modplatform/ModModel.h | 3 ++ launcher/ui/pages/modplatform/ModPage.cpp | 38 +------------ launcher/ui/pages/modplatform/ModPage.h | 9 +--- .../ui/pages/modplatform/ResourceModel.cpp | 15 ++++-- launcher/ui/pages/modplatform/ResourceModel.h | 11 ++++ .../ui/pages/modplatform/ResourcePage.cpp | 2 +- launcher/ui/pages/modplatform/ResourcePage.h | 7 --- .../modplatform/flame/FlameResourceModels.cpp | 25 +++++++++ .../modplatform/flame/FlameResourceModels.h | 8 +++ .../modplatform/flame/FlameResourcePages.cpp | 38 ++----------- .../modplatform/flame/FlameResourcePages.h | 12 +---- .../modrinth/ModrinthResourcePages.cpp | 11 ++-- .../modrinth/ModrinthResourcePages.h | 3 +- launcher/ui/widgets/CheckComboBox.cpp | 2 +- launcher/ui/widgets/CheckComboBox.h | 4 +- launcher/ui/widgets/ModFilterWidget.cpp | 54 ++++++++++++++++--- launcher/ui/widgets/ModFilterWidget.h | 19 ++++--- launcher/ui/widgets/ModFilterWidget.ui | 19 ++++--- 23 files changed, 214 insertions(+), 152 deletions(-) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 3b1959384..732c66621 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -73,6 +73,7 @@ class ResourceAPI { std::optional sorting; std::optional loaders; std::optional > versions; + std::optional side; }; struct SearchCallbacks { std::function on_succeed; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index d56e46b23..f95b17683 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -79,10 +79,6 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, const BaseInstance* inst) { QVector unsortedVersions; - auto profile = (dynamic_cast(inst))->getPackProfile(); - QString mcVersion = profile->getComponentVersion("net.minecraft"); - auto loaders = profile->getSupportedModLoaders(); - for (auto versionIter : arr) { auto obj = versionIter.toObject(); @@ -90,8 +86,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, if (!file.addonId.isValid()) file.addonId = pack.addonId; - if (file.fileId.isValid() && - (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + if (file.fileId.isValid()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 857719902..8e0f9fe22 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -56,6 +56,18 @@ class ModrinthAPI : public NetworkResourceAPI { return l.join(','); } + static auto getSideFilters(QString side) -> const QString + { + if (side.isEmpty() || side == "both") { + return {}; + } + if (side == "client") + return QString("\"client_side:required\",\"client_side:optional\""); + if (side == "server") + return QString("\"server_side:required\",\"server_side:optional\""); + return {}; + } + private: [[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type) { @@ -73,6 +85,7 @@ class ModrinthAPI : public NetworkResourceAPI { return ""; } + [[nodiscard]] QString createFacets(SearchArgs const& args) const { QStringList facets_list; @@ -81,6 +94,11 @@ class ModrinthAPI : public NetworkResourceAPI { facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value()))); if (args.versions.has_value()) facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value()))); + if (args.side.has_value()) { + auto side = getSideFilters(args.side.value()); + if (!side.isEmpty()) + facets_list.append(QString("[%1]").arg(side)); + } facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type))); return QString("[%1]").arg(facets_list.join(',')); diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 23770b7fb..5de645220 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -112,16 +112,11 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst) { QVector unsortedVersions; - auto profile = (dynamic_cast(inst))->getPackProfile(); - QString mcVersion = profile->getComponentVersion("net.minecraft"); - auto loaders = profile->getSupportedModLoaders(); - for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj); - if (file.fileId.isValid() && - (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + if (file.fileId.isValid()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 3b53d1348..64ae7de36 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -27,16 +27,16 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() std::optional> versions{}; auto loaders = profile->getSupportedModLoaders(); - { // Version filter - if (!m_filter->versions.empty()) - versions = m_filter->versions; - if (m_filter->loaders) - loaders = m_filter->loaders; - } + // Version filter + if (!m_filter->versions.empty()) + versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; + auto side = m_filter->side; auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) @@ -85,4 +85,43 @@ bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const }); } +bool checkSide(QString filter, QString value) +{ + return filter.isEmpty() || value.isEmpty() || filter == "both" || value == "both" || filter == value; +} + +bool checkMcVersions(std::list filter, QStringList value) +{ + bool valid = false; + for (auto mcVersion : filter) { + if (value.contains(mcVersion.toString())) { + valid = true; + break; + } + } + return filter.empty() || valid; +} + +bool ModModel::checkFilters(ModPlatform::IndexedPack::Ptr pack) +{ + if (!m_filter) + return true; + return !(m_filter->hideInstalled && isPackInstalled(pack)) && checkSide(m_filter->side, pack->side); +} + +bool ModModel::checkVersionFilters(const ModPlatform::IndexedVersion& v) +{ + if (!m_filter) + return true; + auto loaders = static_cast(m_base_instance).getPackProfile()->getSupportedModLoaders(); + if (m_filter->loaders) + loaders = m_filter->loaders; + return (!optedOut(v) && // is opted out(aka curseforge download link) + (!loaders.has_value() || !v.loaders || loaders.value() & v.loaders) && // loaders + checkSide(m_filter->side, v.side) && // side + (m_filter->releases.empty() || // releases + std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.version_type) != m_filter->releases.cend()) && + checkMcVersions(m_filter->versions, v.mcVersion)); // mcVersions +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index dd187aa8d..9101e07ba 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -45,6 +45,9 @@ class ModModel : public ResourceModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0; virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override; + virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) override; + virtual bool checkVersionFilters(const ModPlatform::IndexedVersion&) override; + protected: BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 838cf21c8..e6106a0de 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -73,6 +73,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_filter = m_filter_widget->getFilter(); + connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ResourcePage::updateVersionList); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, @@ -110,43 +111,6 @@ QMap ModPage::urlHandlers() const /******** Make changes to the UI ********/ -void ModPage::updateVersionList() -{ - m_ui->versionSelectionBox->clear(); - auto packProfile = (dynamic_cast(m_base_instance)).getPackProfile(); - - QString mcVersion = packProfile->getComponentVersion("net.minecraft"); - auto loaders = packProfile->getSupportedModLoaders(); - if (m_filter->loaders) - loaders = m_filter->loaders; - - auto current_pack = getCurrentPack(); - if (!current_pack) - return; - for (int i = 0; i < current_pack->versions.size(); i++) { - auto version = current_pack->versions[i]; - bool valid = false; - for (auto& mcVer : m_filter->versions) { - if (validateVersion(version, mcVer.toString(), loaders)) { - valid = true; - break; - } - } - - // 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)) { - 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)); - m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :(")); - } - - updateSelectionButton(); -} - void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version, const std::shared_ptr base_model) diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 6efb318ea..c878bc004 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -31,7 +31,7 @@ class ModPage : public ResourcePage { auto page = new T(dialog, instance); auto model = static_cast(page->getModel()); - auto filter_widget = ModFilterWidget::create(&static_cast(instance), page); + auto filter_widget = page->createFilterWidget(); page->setFilterWidget(filter_widget); model->setFilter(page->getFilter()); @@ -52,17 +52,12 @@ class ModPage : public ResourcePage { ModPlatform::IndexedVersion&, const std::shared_ptr) override; - virtual auto validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders = {}) const -> bool = 0; + virtual unique_qobject_ptr createFilterWidget() = 0; [[nodiscard]] bool supportsFiltering() const override { return true; }; auto getFilter() const -> const std::shared_ptr { return m_filter; } void setFilterWidget(unique_qobject_ptr&); - public slots: - void updateVersionList() override; - protected: ModPage(ModDownloadDialog* dialog, BaseInstance& instance); diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 48e66efca..776765d64 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -399,12 +399,17 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) m_search_state = SearchState::CanFetchMore; } + QList filteredNewList; + for (auto p : newList) + if (checkFilters(p)) + filteredNewList << p; + // When you have a Qt build with assertions turned on, proceeding here will abort the application - if (newList.size() == 0) + if (filteredNewList.size() == 0) return; - beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + newList.size() - 1); - m_packs.append(newList); + beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + filteredNewList.size() - 1); + m_packs.append(filteredNewList); endInsertRows(); } @@ -547,4 +552,8 @@ void ResourceModel::removePack(const QString& rem) ver.is_currently_selected = false; } +bool ResourceModel::checkVersionFilters(const ModPlatform::IndexedVersion& v) +{ + return (!optedOut(v)); +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index ecf4f8f79..1956e54ac 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -11,6 +11,7 @@ #include "QObjectPtr.h" #include "ResourceDownloadTask.h" +#include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "tasks/ConcurrentTask.h" @@ -55,6 +56,16 @@ class ResourceModel : public QAbstractListModel { [[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); } + /** Whether the version is opted out or not. Currently only makes sense in CF. */ + virtual bool optedOut(const ModPlatform::IndexedVersion& ver) const + { + Q_UNUSED(ver); + return false; + }; + + virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) { return true; } + virtual bool checkVersionFilters(const ModPlatform::IndexedVersion&); + public slots: void fetchMore(const QModelIndex& parent) override; // NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12 diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 44a91003d..158b13c96 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -263,7 +263,7 @@ void ResourcePage::updateVersionList() if (current_pack) for (int i = 0; i < current_pack->versions.size(); i++) { auto& version = current_pack->versions[i]; - if (optedOut(version)) + if (!m_model->checkVersionFilters(version)) continue; auto release_type = current_pack->versions[i].version_type.isValid() diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 7bec0a375..84b37f50b 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -96,13 +96,6 @@ class ResourcePage : public QWidget, public BasePage { virtual QMap urlHandlers() const = 0; virtual void openUrl(const QUrl&); - /** Whether the version is opted out or not. Currently only makes sense in CF. */ - virtual bool optedOut(ModPlatform::IndexedVersion& ver) const - { - Q_UNUSED(ver); - return false; - }; - public: BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 39a2a0d6a..ae4562be4 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -12,6 +12,11 @@ namespace ResourceDownload { +static bool isOptedOut(const ModPlatform::IndexedVersion& ver) +{ + return ver.downloadUrl.isEmpty(); +} + FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {} void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) @@ -35,6 +40,11 @@ auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJs return FlameMod::loadDependencyVersions(m, arr, &m_base_instance); } +bool FlameModModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); @@ -58,6 +68,11 @@ void FlameResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); } +bool FlameResourcePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); @@ -117,6 +132,11 @@ ResourceAPI::VersionSearchArgs FlameTexturePackModel::createVersionsArguments(QM return args; } +bool FlameTexturePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); @@ -140,6 +160,11 @@ void FlameShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); } +bool FlameShaderPackModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 76dbd7b3d..458fd85d0 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -17,6 +17,8 @@ class FlameModModel : public ModModel { FlameModModel(BaseInstance&); ~FlameModModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } @@ -36,6 +38,8 @@ class FlameResourcePackModel : public ResourcePackResourceModel { FlameResourcePackModel(const BaseInstance&); ~FlameResourcePackModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } @@ -54,6 +58,8 @@ class FlameTexturePackModel : public TexturePackResourceModel { FlameTexturePackModel(const BaseInstance&); ~FlameTexturePackModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } @@ -75,6 +81,8 @@ class FlameShaderPackModel : public ShaderPackResourceModel { FlameShaderPackModel(const BaseInstance&); ~FlameShaderPackModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 23373ec9d..66a51decd 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -44,11 +44,6 @@ namespace ResourceDownload { -static bool isOptedOut(ModPlatform::IndexedVersion const& ver) -{ - return ver.downloadUrl.isEmpty(); -} - FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { m_model = new FlameModModel(instance); @@ -66,19 +61,6 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : m_ui->packDescription->setMetaEntry(metaEntryBase()); } -auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders) const -> bool -{ - return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() && - (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders); -} - -bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameModPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -113,11 +95,6 @@ FlameResourcePackPage::FlameResourcePackPage(ResourcePackDownloadDialog* dialog, m_ui->packDescription->setMetaEntry(metaEntryBase()); } -bool FlameResourcePackPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameResourcePackPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -152,11 +129,6 @@ FlameTexturePackPage::FlameTexturePackPage(TexturePackDownloadDialog* dialog, Ba m_ui->packDescription->setMetaEntry(metaEntryBase()); } -bool FlameTexturePackPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameTexturePackPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -191,11 +163,6 @@ FlameShaderPackPage::FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseI m_ui->packDescription->setMetaEntry(metaEntryBase()); } -bool FlameShaderPackPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameShaderPackPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -232,4 +199,9 @@ auto FlameShaderPackPage::shouldDisplay() const -> bool return true; } +unique_qobject_ptr FlameModPage::createFilterWidget() +{ + return ModFilterWidget::create(&static_cast(m_base_instance), false, this); +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index f2f5cecad..88ef404fb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -94,12 +94,8 @@ class FlameModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - bool validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders = {}) const override; - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; + unique_qobject_ptr createFilterWidget() override; }; class FlameResourcePackPage : public ResourcePackResourcePage { @@ -124,8 +120,6 @@ class FlameResourcePackPage : public ResourcePackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; }; @@ -151,8 +145,6 @@ class FlameTexturePackPage : public TexturePackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; }; @@ -178,8 +170,6 @@ class FlameShaderPackPage : public ShaderPackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index a4197b225..ab015ff0e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -63,13 +63,6 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan m_ui->packDescription->setMetaEntry(metaEntryBase()); } -auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders) const -> bool -{ - return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders); -} - ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) : ResourcePackResourcePage(dialog, instance) { @@ -144,4 +137,8 @@ auto ModrinthShaderPackPage::shouldDisplay() const -> bool return true; } +unique_qobject_ptr ModrinthModPage::createFilterWidget() +{ + return ModFilterWidget::create(&static_cast(m_base_instance), true, this); +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 311bcfe32..0a715d747 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -93,8 +93,7 @@ class ModrinthModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const - -> bool override; + unique_qobject_ptr createFilterWidget() override; }; class ModrinthResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index f826c1afe..af4594335 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -84,7 +84,7 @@ CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(" this->installEventFilter(this); } -void CheckComboBox::setModel(QAbstractItemModel* new_model) +void CheckComboBox::setSourceModel(QAbstractItemModel* new_model) { auto proxy = new CheckComboModel(this); proxy->setSourceModel(new_model); diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h index 277dd5fb8..f1d9771cc 100644 --- a/launcher/ui/widgets/CheckComboBox.h +++ b/launcher/ui/widgets/CheckComboBox.h @@ -28,7 +28,7 @@ class CheckComboBox : public QComboBox { explicit CheckComboBox(QWidget* parent = nullptr); virtual ~CheckComboBox() = default; - virtual void hidePopup() override; + void hidePopup() override; QString defaultText() const; void setDefaultText(const QString& text); @@ -41,7 +41,7 @@ class CheckComboBox : public QComboBox { QStringList checkedItems() const; - virtual void setModel(QAbstractItemModel* model) override; + void setSourceModel(QAbstractItemModel* model); public slots: void setCheckedItems(const QStringList& items); diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 5a67757b5..dde5d7502 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -9,9 +9,9 @@ #include "Application.h" #include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) { - return unique_qobject_ptr(new ModFilterWidget(instance, parent)); + return unique_qobject_ptr(new ModFilterWidget(instance, extendedSupport, parent)); } class VersionBasicModel : public QIdentityProxyModel { @@ -28,23 +28,33 @@ class VersionBasicModel : public QIdentityProxyModel { } }; -ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) { ui->setupUi(this); m_versions_proxy = new VersionProxyModel(this); + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); auto proxy = new VersionBasicModel(this); proxy->setSourceModel(m_versions_proxy); - ui->versionsCb->setModel(proxy); - ui->versionsCb->setSeparator("| "); - m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + if (!extendedSupport) { + ui->versionsSimpleCb->setModel(proxy); + ui->versionsCb->hide(); + ui->snapshotsCb->hide(); + ui->envBox->hide(); + } else { + ui->versionsCb->setSourceModel(proxy); + ui->versionsCb->setSeparator("| "); + ui->versionsSimpleCb->hide(); + } ui->versionsCb->setStyleSheet("combobox-popup: 0;"); + ui->versionsSimpleCb->setStyleSheet("combobox-popup: 0;"); connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->versionsSimpleCb, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); @@ -61,6 +71,11 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); + connect(ui->releaseCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->betaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + setHidden(true); loadVersionList(); prepareBasicFilter(); @@ -196,4 +211,31 @@ void ModFilterWidget::onHideInstalledFilterChanged() emit filterUnchanged(); } +void ModFilterWidget::onReleaseFilterChanged() +{ + std::list releases; + if (ui->releaseCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Release)); + if (ui->betaCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Beta)); + if (ui->alphaCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Alpha)); + if (ui->unknownCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Unknown)); + m_filter_changed = releases != m_filter->releases; + m_filter->releases = releases; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); +} + +void ModFilterWidget::onVersionFilterTextChanged(QString version) +{ + m_filter->versions.clear(); + m_filter->versions.push_front(version); + m_filter_changed = true; + emit filterChanged(); +} + #include "ModFilterWidget.moc" diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index b92437a4f..50e6b3762 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -22,41 +22,44 @@ class ModFilterWidget : public QTabWidget { public: struct Filter { std::list versions; + std::list releases; ModPlatform::ModLoaderTypes loaders; QString side; bool hideInstalled; bool operator==(const Filter& other) const { - return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions; + return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions && + releases == other.releases; } bool operator!=(const Filter& other) const { return !(*this == other); } }; - static unique_qobject_ptr create(MinecraftInstance* instance, QWidget* parent = nullptr); + static unique_qobject_ptr create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; auto changed() const -> bool { return m_filter_changed; } + signals: + void filterChanged(); + void filterUnchanged(); + private: - ModFilterWidget(MinecraftInstance* instance, QWidget* parent = nullptr); + ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); void loadVersionList(); void prepareBasicFilter(); private slots: void onVersionFilterChanged(); + void onVersionFilterTextChanged(QString version); + void onReleaseFilterChanged(); void onLoadersFilterChanged(); void onSideFilterChanged(); void onHideInstalledFilterChanged(); void onIncludeSnapshotsChanged(); - public: - signals: - void filterChanged(); - void filterUnchanged(); - private: Ui::ModFilterWidget* ui; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 3113f2df2..d962ea44a 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -6,7 +6,7 @@ 0 0 - 400 + 460 127 @@ -17,13 +17,20 @@ - 2 + 0 Minecraft versions + + + + Include Snapshots + + + @@ -40,12 +47,8 @@ - - - - Include Snapshots - - + + From 9aac8e389f4e5daaf05826691b6bdb21bfb06e09 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 18:48:55 +0300 Subject: [PATCH 081/755] made release type visible Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 15 +++++++++++++++ launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 21 ++++++++++++++------- launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/minecraft/mod/Resource.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 3 +++ launcher/modplatform/packwiz/Packwiz.h | 1 + launcher/ui/widgets/ModFilterWidget.cpp | 2 +- launcher/ui/widgets/ModFilterWidget.h | 2 +- 9 files changed, 38 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 22e652319..f301cc949 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -47,6 +47,7 @@ #include "minecraft/mod/ModDetails.h" #include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" +#include "modplatform/ModIndex.h" static ModPlatform::ProviderCapabilities ProviderCaps; @@ -132,6 +133,13 @@ std::pair Mod::compare(const Resource& other, SortType type) const return { compare_result, type == SortType::MC_VERSIONS }; break; } + case SortType::RELEASE_TYPE: { + if (releaseType() > cast_other->releaseType()) + return { 1, type == SortType::RELEASE_TYPE }; + else if (releaseType() < cast_other->releaseType()) + return { -1, type == SortType::RELEASE_TYPE }; + break; + } } return { 0, false }; } @@ -262,6 +270,13 @@ auto Mod::side() const -> Metadata::ModSide return Metadata::ModSide::UniversalSide; } +auto Mod::releaseType() const -> ModPlatform::IndexedVersionType +{ + if (metadata()) + return metadata()->releaseType; + return ModPlatform::IndexedVersionType::VersionType::Unknown; +} + auto Mod::loaders() const -> ModPlatform::ModLoaderTypes { if (metadata()) diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 92de65889..bf503bc80 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -74,6 +74,7 @@ class Mod : public Resource { auto side() const -> Metadata::ModSide; auto loaders() const -> ModPlatform::ModLoaderTypes; auto mcVersions() const -> QStringList; + auto releaseType() const -> ModPlatform::IndexedVersionType; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index ef4ab957c..fb3688d5d 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -64,16 +64,17 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = - QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders", "Miecraft Versions" }); + m_column_names = QStringList( + { "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders", "Miecraft Versions", "Release Type" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), - tr("Side"), tr("Loaders"), tr("Miecraft Versions") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, - SortType::PROVIDER, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS }; + tr("Side"), tr("Loaders"), tr("Miecraft Versions"), tr("Release Type") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, + SortType::PROVIDER, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS, SortType::RELEASE_TYPE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -128,6 +129,9 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const case McVersionsColumn: { return at(row)->mcVersions().join(", "); } + case ReleaseTypeColumn: { + return at(row)->releaseType().toString(); + } default: return QVariant(); } @@ -180,6 +184,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case SideColumn: case LoadersColumn: case McVersionsColumn: + case ReleaseTypeColumn: return columnNames().at(section); default: return QVariant(); @@ -203,6 +208,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The mod loader."); case McVersionsColumn: return tr("The supported minecraft versions."); + case ReleaseTypeColumn: + return tr("The release type."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index f9a87a741..a243a3c92 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -71,6 +71,7 @@ class ModFolderModel : public ResourceFolderModel { SideColumn, LoadersColumn, McVersionsColumn, + ReleaseTypeColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c46980837..101f65271 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS, MC_VERSIONS }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS, MC_VERSIONS, RELEASE_TYPE }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 0692a627d..1672cdf3d 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -117,6 +117,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.loaders = mod_version.loaders; mod.mcVersions = mod_version.mcVersion; mod.mcVersions.sort(); + mod.releaseType = mod_version.version_type; return mod; } @@ -209,6 +210,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) { "side", sideToString(mod.side).toStdString() }, { "loaders", loaders }, { "mcVersions", mcVersions }, + { "releaseType", mod.releaseType.toString().toStdString() }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -293,6 +295,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); mod.side = stringToSide(stringEntry(table, "side")); + mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "releaseType")); if (auto loaders = table["loaders"]; loaders && loaders.is_array()) { for (auto&& loader : *loaders.as_array()) { if (loader.is_string()) { diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 5cf99e74a..50cce9c66 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -43,6 +43,7 @@ class V1 { Side side{ Side::UniversalSide }; ModPlatform::ModLoaderTypes loaders; QStringList mcVersions; + ModPlatform::IndexedVersionType releaseType; // [download] QString mode{}; diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index dde5d7502..d702173f1 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -148,7 +148,7 @@ void ModFilterWidget::onIncludeSnapshotsChanged() m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); } -void ModFilterWidget::onVersionFilterChanged() +void ModFilterWidget::onVersionFilterChanged(int) { auto versions = ui->versionsCb->checkedItems(); m_filter->versions.clear(); diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 50e6b3762..b6edcb21e 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -52,7 +52,7 @@ class ModFilterWidget : public QTabWidget { void prepareBasicFilter(); private slots: - void onVersionFilterChanged(); + void onVersionFilterChanged(int); void onVersionFilterTextChanged(QString version); void onReleaseFilterChanged(); void onLoadersFilterChanged(); From 35d62cc5f2d79e499791a20a3997bf91137aadc0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 19:05:31 +0300 Subject: [PATCH 082/755] Updated dependencies Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 15 ++++++++++++--- launcher/ui/widgets/ModFilterWidget.cpp | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index deec0db7c..625ab23bb 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -45,6 +45,14 @@ static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value(); } +static bool checkDependencies(std::shared_ptr sel, + Version mcVersion, + ModPlatform::ModLoaderTypes loaders) +{ + return (sel->pack->versions.isEmpty() || sel->version.mcVersion.contains(mcVersion.toString())) && + (!loaders || !sel->version.loaders || sel->version.loaders & loaders); +} + GetModDependenciesTask::GetModDependenciesTask(QObject* parent, BaseInstance* instance, ModFolderModel* folder, @@ -67,9 +75,10 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent, void GetModDependenciesTask::prepare() { for (auto sel : m_selected) { - for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) { - addTask(prepareDependencyTask(dep, sel->pack->provider, 20)); - } + if (checkDependencies(sel, m_version, m_loaderType)) + for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) { + addTask(prepareDependencyTask(dep, sel->pack->provider, 20)); + } } } diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index d702173f1..0dff97510 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -53,7 +53,7 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSuppo ui->versionsCb->setStyleSheet("combobox-popup: 0;"); ui->versionsSimpleCb->setStyleSheet("combobox-popup: 0;"); connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); - connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->versionsCb, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); connect(ui->versionsSimpleCb, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); From 41c9ca4f8ae0d18c53a5523b81e31d5d4e42072f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 22:15:07 +0300 Subject: [PATCH 083/755] Added categories filter Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.h | 5 +++ launcher/modplatform/ResourceAPI.h | 1 + launcher/modplatform/flame/FlameAPI.cpp | 41 +++++++++++++++++++ launcher/modplatform/flame/FlameAPI.h | 7 ++++ launcher/modplatform/modrinth/ModrinthAPI.cpp | 38 +++++++++++++++++ launcher/modplatform/modrinth/ModrinthAPI.h | 15 +++++++ launcher/ui/pages/modplatform/ModModel.cpp | 5 ++- launcher/ui/pages/modplatform/ModPage.cpp | 1 + launcher/ui/pages/modplatform/ModPage.h | 2 + .../modplatform/flame/FlameResourcePages.cpp | 14 +++++++ .../modplatform/flame/FlameResourcePages.h | 3 ++ .../modrinth/ModrinthResourcePages.cpp | 11 +++++ .../modrinth/ModrinthResourcePages.h | 3 ++ launcher/ui/widgets/ModFilterWidget.cpp | 39 ++++++++++++++++-- launcher/ui/widgets/ModFilterWidget.h | 11 ++++- launcher/ui/widgets/ModFilterWidget.ui | 40 +++++++++++++++++- 16 files changed, 230 insertions(+), 6 deletions(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 7c472ec9b..a8ec6e7b3 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -191,6 +191,11 @@ constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept return x && !(x & (x - 1)); } +struct Category { + QString name; + QString id; +}; + } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 732c66621..97a38ca69 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -74,6 +74,7 @@ class ResourceAPI { std::optional loaders; std::optional > versions; std::optional side; + std::optional categoryIds; }; struct SearchCallbacks { std::function on_succeed; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index e99ce3a56..cc3f31e32 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -3,10 +3,12 @@ // SPDX-License-Identifier: GPL-3.0-only #include "FlameAPI.h" +#include #include "FlameModIndex.h" #include "Application.h" #include "Json.h" +#include "modplatform/ModIndex.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" #include "net/NetJob.h" @@ -222,3 +224,42 @@ QList FlameAPI::getSortingMethods() const { return s_sorts; } + +Task::Ptr FlameAPI::getModCategories(std::shared_ptr response) +{ + auto netJob = makeShared(QString("Flame::GetCategories"), APPLICATION->network()); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl("https://api.curseforge.com/v1/categories?gameId=432&classId=6"), response)); + QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; }); + return netJob; +} + +QList FlameAPI::loadModCategories(std::shared_ptr response) +{ + QList categories; + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from categories at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return categories; + } + + try { + auto obj = Json::requireObject(doc); + auto arr = Json::requireArray(obj, "data"); + + for (auto val : arr) { + auto cat = Json::requireObject(val); + auto id = Json::requireInteger(cat, "id"); + auto name = Json::requireString(cat, "name"); + categories.push_back({ name, QString::number(id) }); + } + + } catch (Json::JsonException& e) { + qCritical() << "Failed to parse response from a version request."; + qCritical() << e.what(); + qDebug() << doc; + } + return categories; +}; \ No newline at end of file diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index e22d8f0d8..dfe76f9d5 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include "modplatform/ModIndex.h" @@ -22,6 +23,9 @@ class FlameAPI : public NetworkResourceAPI { Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr response) const; Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr response) const; + static Task::Ptr getModCategories(std::shared_ptr response); + static QList loadModCategories(std::shared_ptr response); + [[nodiscard]] auto getSortingMethods() const -> QList override; static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool @@ -96,6 +100,9 @@ class FlameAPI : public NetworkResourceAPI { get_arguments.append("sortOrder=desc"); if (args.loaders.has_value()) get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value()))); + if (args.categoryIds.has_value() && !args.categoryIds->empty()) + get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(","))); + get_arguments.append(gameVersionStr); return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&'); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index f453f5cb9..d3f006e6f 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -122,3 +122,41 @@ QList ModrinthAPI::getSortingMethods() const { return s_sorts; } + +Task::Ptr ModrinthAPI::getModCategories(std::shared_ptr response) +{ + auto netJob = makeShared(QString("Modrinth::GetCategories"), APPLICATION->network()); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(BuildConfig.MODRINTH_PROD_URL + "/tag/category"), response)); + QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Modrinth failed to get categories:" << msg; }); + return netJob; +} + +QList ModrinthAPI::loadModCategories(std::shared_ptr response) +{ + QList categories; + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from categories at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return categories; + } + + try { + auto arr = Json::requireArray(doc); + + for (auto val : arr) { + auto cat = Json::requireObject(val); + auto name = Json::requireString(cat, "name"); + if (Json::ensureString(cat, "project_type", "") == "mod") + categories.push_back({ name, name }); + } + + } catch (Json::JsonException& e) { + qCritical() << "Failed to parse response from a version request."; + qCritical() << e.what(); + qDebug() << doc; + } + return categories; +}; \ No newline at end of file diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 8e0f9fe22..d1f8f712a 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -30,6 +30,9 @@ class ModrinthAPI : public NetworkResourceAPI { Task::Ptr getProjects(QStringList addonIds, std::shared_ptr response) const override; + static Task::Ptr getModCategories(std::shared_ptr response); + static QList loadModCategories(std::shared_ptr response); + public: [[nodiscard]] auto getSortingMethods() const -> QList override; @@ -56,6 +59,15 @@ class ModrinthAPI : public NetworkResourceAPI { return l.join(','); } + static auto getCategoriesFilters(QStringList categories) -> const QString + { + QStringList l; + for (auto cat : categories) { + l << QString("\"categories:%1\"").arg(cat); + } + return l.join(','); + } + static auto getSideFilters(QString side) -> const QString { if (side.isEmpty() || side == "both") { @@ -99,6 +111,9 @@ class ModrinthAPI : public NetworkResourceAPI { if (!side.isEmpty()) facets_list.append(QString("[%1]").arg(side)); } + if (args.categoryIds.has_value() && !args.categoryIds->empty()) + facets_list.append(QString("[%1]").arg(getCategoriesFilters(args.categoryIds.value()))); + facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type))); return QString("[%1]").arg(facets_list.join(',')); diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 64ae7de36..61a01ddd2 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -25,6 +25,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() Q_ASSERT(m_filter); std::optional> versions{}; + std::optional categories{}; auto loaders = profile->getSupportedModLoaders(); // Version filter @@ -32,11 +33,13 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() versions = m_filter->versions; if (m_filter->loaders) loaders = m_filter->loaders; + if (!m_filter->categoryIds.empty()) + categories = m_filter->categoryIds; auto side = m_filter->side; auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side, categories }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index e6106a0de..e4da1a962 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -78,6 +78,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, [&] { m_ui->searchButton->setStyleSheet("text-decoration: none"); }); + prepareProviderCategories(); } /******** Callbacks to events in the UI (set up in the derived classes) ********/ diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index c878bc004..d6a7d64e4 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -61,6 +61,8 @@ class ModPage : public ResourcePage { protected: ModPage(ModDownloadDialog* dialog, BaseInstance& instance); + virtual void prepareProviderCategories(){}; + protected slots: virtual void filterMods(); void triggerSearch() override; diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 66a51decd..7a6046824 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -37,6 +37,10 @@ */ #include "FlameResourcePages.h" +#include +#include +#include "modplatform/ModIndex.h" +#include "modplatform/flame/FlameAPI.h" #include "ui_ResourcePage.h" #include "FlameResourceModels.h" @@ -204,4 +208,14 @@ unique_qobject_ptr FlameModPage::createFilterWidget() return ModFilterWidget::create(&static_cast(m_base_instance), false, this); } +void FlameModPage::prepareProviderCategories() +{ + auto response = std::make_shared(); + auto task = FlameAPI::getModCategories(response); + QObject::connect(task.get(), &Task::succeeded, [this, response]() { + auto categories = FlameAPI::loadModCategories(response); + m_filter_widget->setCategories(categories); + }); + task->start(); +}; } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 88ef404fb..0288023ca 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -96,6 +96,9 @@ class FlameModPage : public ModPage { void openUrl(const QUrl& url) override; unique_qobject_ptr createFilterWidget() override; + + protected: + virtual void prepareProviderCategories() override; }; class FlameResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index ab015ff0e..5b5099863 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -141,4 +141,15 @@ unique_qobject_ptr ModrinthModPage::createFilterWidget() { return ModFilterWidget::create(&static_cast(m_base_instance), true, this); } + +void ModrinthModPage::prepareProviderCategories() +{ + auto response = std::make_shared(); + auto task = ModrinthAPI::getModCategories(response); + QObject::connect(task.get(), &Task::succeeded, [this, response]() { + auto categories = ModrinthAPI::loadModCategories(response); + m_filter_widget->setCategories(categories); + }); + task->start(); +}; } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 0a715d747..a8dcd1de5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -94,6 +94,9 @@ class ModrinthModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } unique_qobject_ptr createFilterWidget() override; + + protected: + virtual void prepareProviderCategories() override; }; class ModrinthResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 0dff97510..a3b0a0cc0 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,6 +1,8 @@ #include "ModFilterWidget.h" -#include -#include +#include +#include +#include +#include #include "BaseVersionList.h" #include "meta/Index.h" #include "modplatform/ModIndex.h" @@ -76,6 +78,8 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSuppo connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->categoriesList, &QListWidget::itemClicked, this, &ModFilterWidget::onCategoryClicked); + setHidden(true); loadVersionList(); prepareBasicFilter(); @@ -238,4 +242,33 @@ void ModFilterWidget::onVersionFilterTextChanged(QString version) emit filterChanged(); } -#include "ModFilterWidget.moc" +void ModFilterWidget::setCategories(QList categories) +{ + ui->categoriesList->clear(); + m_categories = categories; + for (auto cat : categories) { + auto item = new QListWidgetItem(cat.name, ui->categoriesList); + item->setFlags(item->flags() & (~Qt::ItemIsUserCheckable)); + item->setCheckState(Qt::Unchecked); + ui->categoriesList->addItem(item); + } +} + +void ModFilterWidget::onCategoryClicked(QListWidgetItem* item) +{ + if (item) + item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); + m_filter->categoryIds.clear(); + for (auto i = 0; i < ui->categoriesList->count(); i++) { + auto item = ui->categoriesList->item(i); + if (item->checkState() == Qt::Checked) { + auto c = std::find_if(m_categories.cbegin(), m_categories.cend(), [item](auto v) { return v.name == item->text(); }); + if (c != m_categories.cend()) + m_filter->categoryIds << c->id; + } + } + m_filter_changed = true; + emit filterChanged(); +}; + +#include "ModFilterWidget.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index b6edcb21e..402e11ae4 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include "Version.h" @@ -26,11 +28,12 @@ class ModFilterWidget : public QTabWidget { ModPlatform::ModLoaderTypes loaders; QString side; bool hideInstalled; + QStringList categoryIds; bool operator==(const Filter& other) const { return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions && - releases == other.releases; + releases == other.releases && categoryIds == other.categoryIds; } bool operator!=(const Filter& other) const { return !(*this == other); } }; @@ -45,6 +48,9 @@ class ModFilterWidget : public QTabWidget { void filterChanged(); void filterUnchanged(); + public slots: + void setCategories(QList); + private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); @@ -59,6 +65,7 @@ class ModFilterWidget : public QTabWidget { void onSideFilterChanged(); void onHideInstalledFilterChanged(); void onIncludeSnapshotsChanged(); + void onCategoryClicked(QListWidgetItem* item); private: Ui::ModFilterWidget* ui; @@ -69,4 +76,6 @@ class ModFilterWidget : public QTabWidget { Meta::VersionList::Ptr m_version_list; VersionProxyModel* m_versions_proxy = nullptr; + + QList m_categories; }; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index d962ea44a..56cd33fa6 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -7,7 +7,7 @@ 0 0 460 - 127 + 132 @@ -145,6 +145,44 @@
+ + + Categories + + + + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerItem + + + QListView::LeftToRight + + + true + + + QListView::ListMode + + + 0 + + + + + Others From d151e97e3a24667063ad4fe9cd6f5aca6cdf7b21 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 22:40:37 +0300 Subject: [PATCH 084/755] Fixed some headers Signed-off-by: Trial97 --- launcher/minecraft/mod/MetadataHandler.h | 1 + launcher/minecraft/mod/Mod.cpp | 1 + launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 1 + launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/minecraft/mod/Resource.h | 35 +++++++++++++++++++ launcher/modplatform/ModIndex.cpp | 1 + launcher/modplatform/ResourceAPI.h | 1 + .../modrinth/ModrinthPackIndex.cpp | 1 + launcher/modplatform/packwiz/Packwiz.cpp | 1 + launcher/modplatform/packwiz/Packwiz.h | 1 + launcher/ui/pages/modplatform/ModPage.cpp | 1 + .../ui/pages/modplatform/ResourcePage.cpp | 1 + .../modplatform/flame/FlameResourcePages.cpp | 1 + .../modplatform/flame/FlameResourcePages.h | 1 + .../modrinth/ModrinthResourcePages.cpp | 1 + .../modrinth/ModrinthResourcePages.h | 1 + launcher/ui/widgets/CheckComboBox.cpp | 6 ++-- launcher/ui/widgets/CheckComboBox.h | 2 +- launcher/ui/widgets/ModFilterWidget.cpp | 35 +++++++++++++++++++ launcher/ui/widgets/ModFilterWidget.h | 35 +++++++++++++++++++ 21 files changed, 124 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index ccdd7d559..fb3a10133 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index f301cc949..e89131034 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index bf503bc80..e9baeed58 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index fb3688d5d..8e31fa34b 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index a243a3c92..31c739e5b 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 101f65271..0710a400b 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index d2113320a..20bd3f827 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 97a38ca69..a306dcc52 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -4,6 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 5de645220..a2bda2ec5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 1672cdf3d..c8899345e 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 50cce9c66..95362bbfe 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index e4da1a962..8fd42b427 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 158b13c96..35c4afc0d 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 7a6046824..d82c76c3a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 0288023ca..6eef3e435 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 5b5099863..26fe46a54 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -4,6 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index a8dcd1de5..eaf6129a5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -4,6 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index af4594335..8117baa58 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -17,17 +17,15 @@ */ #include "CheckComboBox.h" -#include -#include -#include #include +#include #include #include #include #include +#include #include -#include "BaseVersionList.h" class CheckComboModel : public QIdentityProxyModel { Q_OBJECT diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h index f1d9771cc..4d6daf5a4 100644 --- a/launcher/ui/widgets/CheckComboBox.h +++ b/launcher/ui/widgets/CheckComboBox.h @@ -18,8 +18,8 @@ #pragma once -#include #include +#include class CheckComboBox : public QComboBox { Q_OBJECT diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index a3b0a0cc0..b7da6878a 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "ModFilterWidget.h" #include #include diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 402e11ae4..dd98d9afa 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include From 10ae808da90c819df03c9ea78a3248ddb66179d1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 22 Oct 2023 00:06:08 +0300 Subject: [PATCH 085/755] fixed codeql Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index b7da6878a..341823244 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -289,10 +289,10 @@ void ModFilterWidget::setCategories(QList categories) } } -void ModFilterWidget::onCategoryClicked(QListWidgetItem* item) +void ModFilterWidget::onCategoryClicked(QListWidgetItem* itm) { - if (item) - item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); + if (itm) + itm->setCheckState(itm->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); m_filter->categoryIds.clear(); for (auto i = 0; i < ui->categoriesList->count(); i++) { auto item = ui->categoriesList->item(i); From 0ca3873f1c492ad78257eb9ca2c1568a90c353c0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 22 Oct 2023 17:48:19 +0300 Subject: [PATCH 086/755] small fix Signed-off-by: Trial97 --- launcher/tasks/ConcurrentTask.cpp | 4 ++-- launcher/tasks/SequentialTask.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index 92b13cfa8..c042894b4 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -69,7 +69,7 @@ void ConcurrentTask::addTask(Task::Ptr task) void ConcurrentTask::executeTask() { for (auto i = 0; i < m_total_max_size; i++) - executeNextSubTask(); + QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection); } bool ConcurrentTask::abort() @@ -127,7 +127,7 @@ void ConcurrentTask::executeNextSubTask() } return; } - if (m_doing.count() > m_total_max_size) { + if (m_doing.count() >= m_total_max_size) { return; } diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp index 180430c8c..509d91cf7 100644 --- a/launcher/tasks/SequentialTask.cpp +++ b/launcher/tasks/SequentialTask.cpp @@ -43,7 +43,7 @@ SequentialTask::SequentialTask(QObject* parent, QString task_name) : ConcurrentT void SequentialTask::subTaskFailed(Task::Ptr task, const QString& msg) { emitFailed(msg); - qWarning() << m_failed.constBegin()->get()->failReason(); + qWarning() << msg; ConcurrentTask::subTaskFailed(task, msg); } From 2aeb8291767e1832f81f3a4e3f8e3736613175ed Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 22 Oct 2023 19:58:10 +0300 Subject: [PATCH 087/755] removed the concurent task destructor behaivior Signed-off-by: Trial97 --- launcher/modplatform/EnsureMetadataTask.cpp | 1 - launcher/tasks/ConcurrentTask.cpp | 14 +++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index a9ed30119..ce53ee62d 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -160,7 +160,6 @@ void EnsureMetadataTask::executeTask() if (m_current_task) m_current_task.reset(); }); - connect(version_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed); if (m_mods.size() > 1) setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider))); diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index c042894b4..6f4a94e7f 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -46,13 +46,9 @@ ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concu ConcurrentTask::~ConcurrentTask() { - for (auto task : m_queue) { + for (auto task : m_doing) { if (task) - task->deleteLater(); - } - for (auto task : m_done) { - if (task) - task->deleteLater(); + task->disconnect(this); } } @@ -118,6 +114,9 @@ void ConcurrentTask::executeNextSubTask() if (!isRunning()) { return; } + if (m_doing.count() >= m_total_max_size) { + return; + } if (m_queue.isEmpty()) { if (m_doing.isEmpty()) { if (m_failed.isEmpty()) @@ -127,9 +126,6 @@ void ConcurrentTask::executeNextSubTask() } return; } - if (m_doing.count() >= m_total_max_size) { - return; - } startSubTask(m_queue.dequeue()); } From 6a19f2dae82ecda7790e8fcf6cd8cf16f3bcdaf0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 24 Oct 2023 11:19:19 +0300 Subject: [PATCH 088/755] fixed icon import Signed-off-by: Trial97 --- launcher/InstanceImportTask.cpp | 4 +++- launcher/icons/IconUtils.cpp | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 713787902..21ad834b4 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -334,13 +334,15 @@ void InstanceImportTask::processMultiMC() m_instIcon = instance.iconKey(); auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon); + if (importIconPath.isNull() || !QFile::exists(importIconPath)) + importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), "icon.png"); if (!importIconPath.isNull() && QFile::exists(importIconPath)) { // import icon auto iconList = APPLICATION->icons(); if (iconList->iconFileExists(m_instIcon)) { iconList->deleteIcon(m_instIcon); } - iconList->installIcons({ importIconPath }); + iconList->installIcon(importIconPath, m_instIcon); } } emitSucceeded(); diff --git a/launcher/icons/IconUtils.cpp b/launcher/icons/IconUtils.cpp index 99c38f47a..6825dd6da 100644 --- a/launcher/icons/IconUtils.cpp +++ b/launcher/icons/IconUtils.cpp @@ -52,8 +52,7 @@ QString findBestIconIn(const QString& folder, const QString& iconKey) while (it.hasNext()) { it.next(); auto fileInfo = it.fileInfo(); - - if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix())) + if ((fileInfo.completeBaseName() == iconKey || fileInfo.fileName() == iconKey) && isIconSuffix(fileInfo.suffix())) return fileInfo.absoluteFilePath(); } return {}; From 263dc5af67b21719a690c3577c3e9d028fc1106a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 25 Oct 2023 19:56:26 +0300 Subject: [PATCH 089/755] Fixed remane and delete of selected skin Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index b3a593454..1b046a781 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -359,7 +359,7 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) int row = idx.row(); if (row < 0 || row >= m_skin_list.size()) return false; - auto skin = m_skin_list[row]; + auto& skin = m_skin_list[row]; auto newName = value.toString(); if (skin.name() != newName) { skin.rename(newName); diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 24197baeb..5419d3eed 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -229,8 +229,10 @@ void SkinManageDialog::on_steveBtn_toggled(bool checked) void SkinManageDialog::accept() { auto skin = m_list.skin(m_selected_skin); - if (!skin) + if (!skin) { reject(); + return; + } auto path = skin->getPath(); ProgressDialog prog(this); @@ -315,7 +317,7 @@ void SkinManageDialog::on_action_Delete_Skin_triggered(bool checked) return; if (m_list.getSkinIndex(m_selected_skin) == m_list.getSelectedAccountSkin()) { - CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning); + CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning)->exec(); return; } From 44cdf3f6979969c2de7953b58fd08836049fe5d5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 13:33:00 +0300 Subject: [PATCH 090/755] Fixed skin variant Signed-off-by: Trial97 --- launcher/minecraft/auth/Parsers.cpp | 2 +- launcher/minecraft/skins/SkinList.cpp | 4 ++-- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index f6179a93e..a2b002c78 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -347,7 +347,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) Skin skinOut; // fill in default skin info ourselves, as this endpoint doesn't provide it bool steve = isDefaultModelSteve(output.id); - skinOut.variant = steve ? "classic" : "slim"; + skinOut.variant = steve ? "CLASSIC" : "SLIM"; skinOut.url = steve ? SKIN_URL_STEVE : SKIN_URL_ALEX; // sadly we can't figure this out, but I don't think it really matters... skinOut.id = "00000000-0000-0000-0000-000000000000"; diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index 1b046a781..0505e4ced 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -106,7 +106,7 @@ bool SkinList::update() auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); - s.setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; @@ -114,7 +114,7 @@ bool SkinList::update() } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); - nskin->setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + nskin->setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); } } diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 5419d3eed..5c69be0ba 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -460,7 +460,7 @@ void SkinManageDialog::on_userBtn_clicked() dlg.execWithTask(job.get()); SkinModel s(path); - s.setModel(mcProfile.skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(mcProfile.skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setURL(mcProfile.skin.url); if (m_capes.contains(mcProfile.currentCape)) { s.setCapeId(mcProfile.currentCape); From 8b8ea2d27057dd9cebe20586f2ab5fc8426d1c99 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 13:37:25 +0300 Subject: [PATCH 091/755] ensured that the variant is allways uppercase Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 4 ++-- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index 0505e4ced..fd883ad52 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -106,7 +106,7 @@ bool SkinList::update() auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); - s.setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; @@ -114,7 +114,7 @@ bool SkinList::update() } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); - nskin->setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); + nskin->setModel(skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); } } diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 5c69be0ba..9320101c2 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -460,7 +460,7 @@ void SkinManageDialog::on_userBtn_clicked() dlg.execWithTask(job.get()); SkinModel s(path); - s.setModel(mcProfile.skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(mcProfile.skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setURL(mcProfile.skin.url); if (m_capes.contains(mcProfile.currentCape)) { s.setCapeId(mcProfile.currentCape); From dba54332fb7785400ffc6d9eac1f3a0b9d3412cd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 18:31:52 +0300 Subject: [PATCH 092/755] fixed some asan stuf Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp | 2 +- launcher/screenshots/ImgurUpload.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index e56fc3d1d..e78061f27 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -72,7 +72,7 @@ void ModrinthCheckUpdate::executeTask() auto response = std::make_shared(); auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); - connect(job.get(), &Task::succeeded, this, [this, response, &mappings, best_hash_type, job] { + connect(job.get(), &Task::succeeded, this, [this, response, mappings, best_hash_type, job] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index 7ed672eb7..15fb043e4 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -58,14 +58,14 @@ void ImgurUpload::init() QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request) { - auto file = new QFile(m_fileInfo.absoluteFilePath()); + auto file = new QFile(m_fileInfo.absoluteFilePath(), this); if (!file->open(QFile::ReadOnly)) { emitFailed(); return nullptr; } - QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); file->setParent(multipart); QHttpPart filePart; filePart.setBodyDevice(file); From 321bbf1fa8bbdf37a5b81aa8c69512e1b04028eb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 19:06:38 +0300 Subject: [PATCH 093/755] fixed asan stuff Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinUpload.cpp | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index 4a88faedf..dc1bc0bf9 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -49,7 +49,7 @@ SkinUpload::SkinUpload(QString token, QString path, QString variant) : NetReques QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { - QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); QHttpPart skin; skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 9320101c2..8028a719c 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -74,7 +74,7 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct) contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); contentsWidget->installEventFilter(this); - contentsWidget->setItemDelegate(new ListViewDelegate()); + contentsWidget->setItemDelegate(new ListViewDelegate(this)); contentsWidget->setAcceptDrops(true); contentsWidget->setDropIndicatorShown(true); From 3d84635b24e52c35b70e044bed67060adb1dcfd6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 20:09:12 +0300 Subject: [PATCH 094/755] Remove file on mod duplication Signed-off-by: Trial97 --- launcher/InstanceList.cpp | 12 ++++++------ launcher/InstanceList.h | 2 +- launcher/minecraft/mod/Resource.cpp | 10 +++++----- launcher/minecraft/mod/ResourceFolderModel.cpp | 3 --- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 756ff93dd..e245fc2c6 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -371,13 +371,13 @@ void InstanceList::undoTrashInstance() auto top = m_trashHistory.pop(); - while (QDir(top.polyPath).exists()) { + while (QDir(top.rootPath).exists()) { top.id += "1"; - top.polyPath += "1"; + top.rootPath += "1"; } - qDebug() << "Moving" << top.trashPath << "back to" << top.polyPath; - QFile(top.trashPath).rename(top.polyPath); + qDebug() << "Moving" << top.trashPath << "back to" << top.rootPath; + QFile(top.trashPath).rename(top.rootPath); m_instanceGroupIndex[top.id] = top.groupName; increaseGroupCount(top.groupName); @@ -634,8 +634,8 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id) QString inst_type = instanceSettings->get("InstanceType").toString(); - // NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix - // instance + // NOTE: Some PrismLauncher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a + // OneSix instance if (inst_type == "OneSix" || inst_type.isEmpty()) { inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot)); } else { diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 6b0bcd810..1a70b57b9 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -58,7 +58,7 @@ enum class GroupsState { NotLoaded, Steady, Dirty }; struct TrashHistoryItem { QString id; - QString polyPath; + QString rootPath; QString trashPath; QString groupName; }; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index da806f0f4..05940fcb4 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -130,15 +130,15 @@ bool Resource::enable(EnableAction action) if (!path.endsWith(".disabled")) return false; path.chop(9); - - if (!file.rename(path)) - return false; } else { path += ".disabled"; - - if (!file.rename(path)) + } + if (QFileInfo::exists(path)) { // the path exists so just remove the file at path + if (!QFile::remove(path)) return false; } + if (!file.rename(path)) + return false; setFile(QFileInfo(path)); diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 0503b660b..8f2c87747 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -213,9 +213,6 @@ bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, Ena } auto new_id = resource->internal_id(); - if (m_resources_index.contains(new_id)) { - // FIXME: https://github.com/PolyMC/PolyMC/issues/550 - } m_resources_index.remove(old_id); m_resources_index[new_id] = row; From 60a7628dbbf21c27cdc8021b01278b173f56dedb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 20:43:26 +0300 Subject: [PATCH 095/755] delete duplicate mods Signed-off-by: Trial97 --- launcher/minecraft/mod/tasks/BasicFolderLoadTask.h | 10 ++++++++-- launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h index 23a2b649a..6e8b8ed43 100644 --- a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h @@ -49,10 +49,16 @@ class BasicFolderLoadTask : public Task { connect(this, &Task::finished, this->thread(), &QThread::quit); m_dir.refresh(); + QStringList names; for (auto entry : m_dir.entryInfoList()) { auto resource = m_create_func(entry); - resource->moveToThread(m_thread_to_spawn_into); - m_result->resources.insert(resource->internal_id(), resource); + if (names.contains(resource->name())) { + resource->destroy(); + } else { + names << resource->name(); + resource->moveToThread(m_thread_to_spawn_into); + m_result->resources.insert(resource->internal_id(), resource); + } } if (m_aborted) diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 2094df4fc..c11759dd2 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -62,9 +62,15 @@ void ModFolderLoadTask::executeTask() // Read JAR files that don't have metadata m_mods_dir.refresh(); + QStringList names; for (auto entry : m_mods_dir.entryInfoList()) { Mod* mod(new Mod(entry)); + if (names.contains(mod->name())) { + mod->destroy(m_index_dir, true); + continue; + } + names << mod->name(); if (mod->enabled()) { if (m_result->mods.contains(mod->internal_id())) { m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); From 33cf7066b403e1438fd3c9dd36e33bf5e84c5f29 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 27 Oct 2023 21:51:35 +0300 Subject: [PATCH 096/755] formated the code Signed-off-by: Trial97 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 3c0352b13..63d7aa57c 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -40,7 +40,6 @@ JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent) connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited); connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked); connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaDownloadBtn_clicked); - } void JavaSettingsWidget::setupUi() @@ -274,7 +273,6 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() void JavaSettingsWidget::on_javaDownloadBtn_clicked() { JavaDownloader::showPrompts(this); - } void JavaSettingsWidget::on_javaStatusBtn_clicked() { From 1490193402096381c81a940a9926da18bba2ce5a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 30 Oct 2023 22:14:26 +0200 Subject: [PATCH 097/755] added button to disable dependencies on the mod review Signed-off-by: Trial97 --- launcher/ui/dialogs/ModUpdateDialog.cpp | 3 +++ launcher/ui/dialogs/ReviewMessageBox.cpp | 10 ++++++++++ launcher/ui/dialogs/ReviewMessageBox.h | 7 +++++++ launcher/ui/dialogs/ReviewMessageBox.ui | 11 ++++++++--- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 1a70ea59a..762f3ad50 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -435,6 +435,9 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri reqItem->insertChildren(i++, { reqItem }); } } + + ui->toggleDepsButton->show(); + m_deps << item_top; } auto changelog_item = new QTreeWidgetItem(item_top); diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index aa668f8c2..41b832e03 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -13,6 +13,7 @@ ReviewMessageBox::ReviewMessageBox(QWidget* parent, [[maybe_unused]] QString con auto back_button = ui->buttonBox->button(QDialogButtonBox::Cancel); back_button->setText(tr("Back")); + ui->toggleDepsButton->hide(); ui->modTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->modTreeWidget->header()->setStretchLastSection(false); ui->modTreeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); @@ -75,6 +76,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info) } itemTop->insertChildren(childIndx++, { requiredByItem }); + ui->toggleDepsButton->show(); + m_deps << itemTop; } auto versionTypeItem = new QTreeWidgetItem(itemTop); @@ -108,3 +111,10 @@ void ReviewMessageBox::retranslateUi(QString resources_name) ui->explainLabel->setText(tr("You're about to download the following %1:").arg(resources_name)); ui->onlyCheckedLabel->setText(tr("Only %1 with a check will be downloaded!").arg(resources_name)); } +void ReviewMessageBox::on_toggleDepsButton_clicked() +{ + m_deps_checked = !m_deps_checked; + auto state = m_deps_checked ? Qt::Checked : Qt::Unchecked; + for (auto dep : m_deps) + dep->setCheckState(0, state); +}; \ No newline at end of file diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index 596f39c8e..7dd2732a0 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Ui { class ReviewMessageBox; @@ -28,8 +29,14 @@ class ReviewMessageBox : public QDialog { ~ReviewMessageBox() override; + protected slots: + void on_toggleDepsButton_clicked(); + protected: ReviewMessageBox(QWidget* parent, const QString& title, const QString& icon); Ui::ReviewMessageBox* ui; + + QList m_deps; + bool m_deps_checked = true; }; diff --git a/launcher/ui/dialogs/ReviewMessageBox.ui b/launcher/ui/dialogs/ReviewMessageBox.ui index bf53ae80b..dbe351019 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.ui +++ b/launcher/ui/dialogs/ReviewMessageBox.ui @@ -44,15 +44,20 @@ - - + - + + + Toggle Dependencies + + + + From e42a2404a9451b822c5d034f40c79ed006e708e9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 30 Oct 2023 22:55:23 +0200 Subject: [PATCH 098/755] added more microsoft error codes Signed-off-by: Trial97 --- .../auth/steps/XboxAuthorizationStep.cpp | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index c33d7e629..36a76b870 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -126,7 +126,34 @@ bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, Q emit finished( AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.") - .arg("help.minecraft.net")); + .arg("help.minecraft.net")); + return true; + } + case 2148916236: { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("This Microsoft account requires proof of age to play. Please login to %1 to provide proof of age.") + .arg("login.live.com")); + return true; + } + case 2148916237: + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has reached the its limit for playtime. This " + "Microsoft account has been blocked from logging in.")); + return true; + case 2148916227: { + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account was banned by Xbox for violating one or more " + "Community Standards for Xbox and is unable to be used.")); + return true; + } + case 2148916229: { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("This Microsoft account is currently restricted and your guardian has not given you permission to play " + "online. Login to %1 and have your guardian change your permissions.") + .arg("account.microsoft.com")); + return true; + } + case 2148916234: { + emit finished(AccountTaskState::STATE_FAILED_SOFT, + tr("This Microsoft account has not accepted Xbox's Terms of Service. Please login and accept them.")); return true; } default: { From 7166d68736e17573b2bd04d78d5a8a61433f5948 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 31 Oct 2023 00:45:18 +0200 Subject: [PATCH 099/755] fixed account refresh after sleep Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 41 ++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 9fb385777..8844ae105 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -36,12 +36,14 @@ #include "LaunchController.h" #include "Application.h" +#include "minecraft/auth/AccountData.h" #include "minecraft/auth/AccountList.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/EditAccountDialog.h" +#include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/ProfileSelectDialog.h" #include "ui/dialogs/ProfileSetupDialog.h" #include "ui/dialogs/ProgressDialog.h" @@ -154,6 +156,12 @@ void LaunchController::login() return; } } + if (!m_accountToUse->isOffline() && m_accountToUse->accountState() == AccountState::Offline && tries == 0) { + // Force account refresh on the account used to launch the instance updating the AccountState + // only on first try and if it is not meant to be offline + auto accounts = APPLICATION->accounts(); + accounts->requestRefresh(m_accountToUse->internalId()); + } tries++; m_session = std::make_shared(); m_session->wants_online = m_online; @@ -249,17 +257,30 @@ void LaunchController::login() progDialog.execWithTask(task.get()); continue; } - // FIXME: this is missing - the meaning is that the account is queued for refresh and we should wait for that - /* - case AccountState::Queued: { - return; - } - */ case AccountState::Expired: { - auto errorString = tr("The account has expired and needs to be logged into manually again."); - QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok, - QMessageBox::StandardButton::Ok); - emitFailed(errorString); + auto errorString = tr("The account has expired and needs to be logged into manually. Press OK to log in again."); + auto button = QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, + QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel, + QMessageBox::StandardButton::Ok); + if (button == QMessageBox::StandardButton::Ok) { + auto accounts = APPLICATION->accounts(); + bool isDefault = accounts->defaultAccount() == m_accountToUse; + accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(m_accountToUse->profileId()))); + if (m_accountToUse->isMSA()) { + auto newAccount = MSALoginDialog::newAccount( + m_parentWidget, tr("Please enter your Mojang account email and password to add your account.")); + accounts->addAccount(newAccount); + if (isDefault) { + accounts->setDefaultAccount(newAccount); + } + m_accountToUse = nullptr; + decideAccount(); + continue; + } + emitFailed(tr("Account expired and re-login attempt failed")); + } else { + emitFailed(errorString); + } return; } case AccountState::Disabled: { From 60fd7f0ae69c65538c22f9ac9f3ecbc13679804e Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Tue, 31 Oct 2023 08:43:26 +0200 Subject: [PATCH 100/755] Update launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp Co-authored-by: Tayou Signed-off-by: Alexandru Ionut Tripon --- launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index 36a76b870..820e03ec9 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -136,7 +136,7 @@ bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, Q return true; } case 2148916237: - emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has reached the its limit for playtime. This " + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has reached its limit for playtime. This " "Microsoft account has been blocked from logging in.")); return true; case 2148916227: { From 2863a691ef4687eefa47e8fbfd4e5f6e06cce877 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 31 Oct 2023 08:45:08 +0200 Subject: [PATCH 101/755] moved refresh out of the loop Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 8844ae105..2c76fee4f 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -145,6 +145,12 @@ void LaunchController::login() bool tryagain = true; unsigned int tries = 0; + if (!m_accountToUse->isOffline() && m_accountToUse->accountState() == AccountState::Offline) { + // Force account refresh on the account used to launch the instance updating the AccountState + // only on first try and if it is not meant to be offline + auto accounts = APPLICATION->accounts(); + accounts->requestRefresh(m_accountToUse->internalId()); + } while (tryagain) { if (tries > 0 && tries % 3 == 0) { auto result = @@ -156,12 +162,6 @@ void LaunchController::login() return; } } - if (!m_accountToUse->isOffline() && m_accountToUse->accountState() == AccountState::Offline && tries == 0) { - // Force account refresh on the account used to launch the instance updating the AccountState - // only on first try and if it is not meant to be offline - auto accounts = APPLICATION->accounts(); - accounts->requestRefresh(m_accountToUse->internalId()); - } tries++; m_session = std::make_shared(); m_session->wants_online = m_online; From 3cbc63bb9f5265a55294e546f3812b52d4ef7346 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 2 Nov 2023 12:18:24 +0200 Subject: [PATCH 102/755] added size column Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 4 ++- launcher/minecraft/mod/ModFolderModel.cpp | 24 +++++++++++----- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/minecraft/mod/Resource.cpp | 7 +++++ launcher/minecraft/mod/Resource.h | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 28 +++++++++++-------- launcher/minecraft/mod/ResourceFolderModel.h | 12 ++++---- .../minecraft/mod/ResourcePackFolderModel.cpp | 19 +++++++++---- .../minecraft/mod/ResourcePackFolderModel.h | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 16 +++++++---- .../minecraft/mod/TexturePackFolderModel.h | 2 +- 11 files changed, 78 insertions(+), 40 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 310946379..374e60d56 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -45,6 +45,7 @@ #include "MetadataHandler.h" #include "Version.h" #include "minecraft/mod/ModDetails.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" static ModPlatform::ProviderCapabilities ProviderCaps; @@ -87,7 +88,8 @@ std::pair Mod::compare(const Resource& other, SortType type) const default: case SortType::ENABLED: case SortType::NAME: - case SortType::DATE: { + case SortType::DATE: + case SortType::SIZE: { auto res = Resource::compare(other, type); if (res.first != 0) return res; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index a5f1489dd..5c6cdb00e 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,6 +52,8 @@ #include "Application.h" #include "Json.h" +#include "StringUtils.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" @@ -62,12 +64,15 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; + m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size" }); + m_column_names_translated = + QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, + SortType::DATE, SortType::PROVIDER, SortType::SIZE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -105,12 +110,14 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return QVariant(); } case Qt::ToolTipRole: - if (column == NAME_COLUMN) { + if (column == NameColumn) { if (at(row)->isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." @@ -124,7 +131,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const } return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); @@ -154,6 +161,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case DateColumn: case ProviderColumn: case ImageColumn: + case SizeColumn: return columnNames().at(section); default: return QVariant(); @@ -171,6 +179,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The date and time this mod was last changed (or added)."); case ProviderColumn: return tr("Where the mod was downloaded from."); + case SizeColumn: + return tr("The size of the resource."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 61d840f9b..e830556ab 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -61,7 +61,7 @@ class QFileSystemWatcher; class ModFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index da806f0f4..79e52a881 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -89,6 +89,13 @@ std::pair Resource::compare(const Resource& other, SortType type) con if (dateTimeChanged() < other.dateTimeChanged()) return { -1, type == SortType::DATE }; break; + case SortType::SIZE: { + if (fileinfo().size() > other.fileinfo().size()) + return { 1, type == SortType::SIZE }; + if (fileinfo().size() < other.fileinfo().size()) + return { -1, type == SortType::SIZE }; + break; + } } return { 0, false }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c1ed49461..d94e4b368 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 0503b660b..16ff01227 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -15,6 +15,7 @@ #include "FileSystem.h" #include "QVariantUtils.h" +#include "StringUtils.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "settings/Setting.h" @@ -410,15 +411,17 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: switch (column) { - case NAME_COLUMN: + case NameColumn: return m_resources[row]->name(); - case DATE_COLUMN: + case DateColumn: return m_resources[row]->dateTimeChanged(); + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return {}; } case Qt::ToolTipRole: - if (column == NAME_COLUMN) { + if (column == NameColumn) { if (at(row).isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." @@ -434,14 +437,14 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) + if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); return {}; } case Qt::CheckStateRole: switch (column) { - case ACTIVE_COLUMN: + case ActiveColumn: return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; default: return {}; @@ -480,24 +483,27 @@ QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orien switch (role) { case Qt::DisplayRole: switch (section) { - case ACTIVE_COLUMN: - case NAME_COLUMN: - case DATE_COLUMN: + case ActiveColumn: + case NameColumn: + case DateColumn: + case SizeColumn: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: { switch (section) { - case ACTIVE_COLUMN: + case ActiveColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("Is the resource enabled?"); - case NAME_COLUMN: + case NameColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The name of the resource."); - case DATE_COLUMN: + case DateColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The date and time this resource was last changed (or added)."); + case SizeColumn: + return tr("The size of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 60b8879c0..c282b8b80 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -96,7 +96,7 @@ class ResourceFolderModel : public QAbstractListModel { /* Qt behavior */ /* Basic columns */ - enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, NameColumn, DateColumn, SizeColumn, NUM_COLUMNS }; QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast(size()); } @@ -198,12 +198,12 @@ class ResourceFolderModel : public QAbstractListModel { protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! - QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; - QStringList m_column_names = { "Enable", "Name", "Last Modified" }; - QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; + QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::SIZE }; + QStringList m_column_names = { "Enable", "Name", "Last Modified", "Size" }; + QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Size") }; QList m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, - QHeaderView::ResizeToContents }; - QList m_columnsHideable = { false, false, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + QList m_columnsHideable = { false, false, true, true }; QDir m_dir; BaseInstance* m_instance; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f27431576..d94751629 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -42,19 +42,21 @@ #include #include "Application.h" +#include "StringUtils.h" #include "Version.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true }; + m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Size" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Size") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::SIZE }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const @@ -85,6 +87,8 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const } case DateColumn: return m_resources[row]->dateTimeChanged(); + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return {}; @@ -139,6 +143,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O case PackFormatColumn: case DateColumn: case ImageColumn: + case SizeColumn: return columnNames().at(section); default: return {}; @@ -155,6 +160,8 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); case DateColumn: return tr("The date and time this resource pack was last changed (or added)."); + case SizeColumn: + return tr("The size of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 29c2c5995..755b9c4c6 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -7,7 +7,7 @@ class ResourcePackFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, SizeColumn, NUM_COLUMNS }; explicit ResourcePackFolderModel(const QString& dir, BaseInstance* instance); diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 5c5f2b7c1..ef241351f 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -37,6 +37,7 @@ #include "Application.h" +#include "StringUtils.h" #include "TexturePackFolderModel.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" @@ -44,12 +45,12 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, + m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true }; + m_columnsHideable = { false, true, false, true, true }; } Task* TexturePackFolderModel::createUpdateTask() @@ -77,6 +78,8 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->name(); case DateColumn: return m_resources[row]->dateTimeChanged(); + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return {}; } @@ -123,6 +126,7 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or case NameColumn: case DateColumn: case ImageColumn: + case SizeColumn: return columnNames().at(section); default: return {}; @@ -138,6 +142,8 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or case DateColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The date and time this resource was last changed (or added)."); + case SizeColumn: + return tr("The size of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index b975d8641..de90f879f 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -44,7 +44,7 @@ class TexturePackFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, SizeColumn, NUM_COLUMNS }; explicit TexturePackFolderModel(const QString& dir, std::shared_ptr instance); From 4081c51573b76b59a1551045ded4256d6886d3ef Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 2 Nov 2023 23:52:12 +0200 Subject: [PATCH 103/755] made dependency check more lax Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 67 ++++++++++++++++++- .../mod/tasks/GetModDependenciesTask.h | 4 ++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index df8c690af..682f98240 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -57,9 +57,11 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent, , m_version(mcVersion(instance)) , m_loaderType(mcLoaders(instance)) { - for (auto mod : folder->allMods()) + for (auto mod : folder->allMods()) { + m_mods_file_names << mod->fileinfo().fileName(); if (auto meta = mod->metadata(); meta) m_mods.append(meta); + } prepare(); } @@ -225,8 +227,13 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen if (dep_.addonId != pDep->version.addonId) { removePack(pDep->version.addonId); addTask(prepareDependencyTask(dep_, provider.name, level)); - } else + } else { addTask(getProjectInfoTask(pDep)); + } + } + if (isLocalyInstalled(pDep)) { + removePack(pDep->version.addonId); + return; } for (auto dep_ : getDependenciesForVersion(pDep->version, provider.name)) { addTask(prepareDependencyTask(dep_, provider.name, level - 1)); @@ -279,4 +286,58 @@ QHash GetModDependenciesTask::getRequiredBy() rby[addonId.toString()] = req; } return rby; -} \ No newline at end of file +} + +// super lax compare (but not fuzzy) +// convert to lowercase +// convert all speratores to whitespace +// simplify sequence of internal whitespace to a single space +// efectivly compare two strings ignoring all separators and case +auto laxCompare = [](QString fsfilename, QString metadataFilename, bool excludeDigits = false) { + // allowed character seperators + QList allowedSeperators = { '-', '+', '.', '_' }; + if (excludeDigits) + allowedSeperators.append({ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }); + + // copy in lowercase + auto fsName = fsfilename.toLower(); + auto metaName = metadataFilename.toLower(); + + // replace all potential allowed seperatores with whitespace + for (auto sep : allowedSeperators) { + fsName = fsName.replace(sep, ' '); + metaName = metaName.replace(sep, ' '); + } + + // remove extraneous whitespace + fsName = fsName.simplified(); + metaName = metaName.simplified(); + + return fsName.compare(metaName) == 0; +}; + +bool GetModDependenciesTask::isLocalyInstalled(std::shared_ptr pDep) +{ + return pDep->version.fileName.isEmpty() || + + std::find_if(m_selected.begin(), m_selected.end(), + [pDep](std::shared_ptr i) { + return !i->version.fileName.isEmpty() && laxCompare(i->version.fileName, pDep->version.fileName); + }) != m_selected.end() || // check the selected versions + + std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), + [pDep](QString i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName); }) != + m_mods_file_names.end() || // check the existing mods + + std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(), [pDep](std::shared_ptr i) { + return pDep->pack->addonId != i->pack->addonId && !i->version.fileName.isEmpty() && + laxCompare(pDep->version.fileName, i->version.fileName); + }) != m_pack_dependencies.end(); // check loaded dependencies +} + +bool GetModDependenciesTask::isLaxInstalled(std::shared_ptr pDep) +{ + return std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), [pDep](QString i) { + return !i.isEmpty() && laxCompare(i, pDep->version.fileName, true); + }) != m_mods_file_names.end(); // check the existing mods +} diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h index 2580f8077..6ec373525 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h @@ -73,10 +73,14 @@ class GetModDependenciesTask : public SequentialTask { ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName); void removePack(const QVariant addonId); + bool isLocalyInstalled(std::shared_ptr pDep); + bool isLaxInstalled(std::shared_ptr pDep); + private: QList> m_pack_dependencies; QList> m_mods; QList> m_selected; + QStringList m_mods_file_names; Provider m_flame_provider; Provider m_modrinth_provider; From cbb453a0edf5bde883627247b8284f02d18c4493 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 3 Nov 2023 22:55:10 +0200 Subject: [PATCH 104/755] minimize the permisions for extracted files Signed-off-by: Trial97 --- launcher/MMCZip.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 6172fe911..c3acd8eb6 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #if defined(LAUNCHER_APPLICATION) @@ -327,9 +328,20 @@ std::optional extractSubDir(QuaZip* zip, const QString& subdir, con } extracted.append(target_file_path); - QFile::setPermissions(target_file_path, - QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + auto fileInfo = QFileInfo(target_file_path); + if (fileInfo.isFile()) { + auto permissions = fileInfo.permissions(); + auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser | + QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther; + auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; + auto newPermisions = (permissions & maxPermisions) | minPermisions; + if (newPermisions != permissions) { + if (!QFile::setPermissions(target_file_path, permissions)) { + qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path)); + } + } + } qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; } while (zip->goToNextFile()); @@ -582,8 +594,20 @@ auto ExtractZipTask::extractZip() -> ZipResult } extracted.append(target_file_path); - QFile::setPermissions(target_file_path, - QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + auto fileInfo = QFileInfo(target_file_path); + if (fileInfo.isFile()) { + auto permissions = fileInfo.permissions(); + auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser | + QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther; + auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; + + auto newPermisions = (permissions & maxPermisions) | minPermisions; + if (newPermisions != permissions) { + if (!QFile::setPermissions(target_file_path, permissions)) { + logWarning(tr("Could not fix permissions for %1").arg(target_file_path)); + } + } + } qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; } while (m_input->goToNextFile()); From 2b6d8b8a2d7aff89b7677a22980254115293dbd5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 3 Nov 2023 23:02:17 +0200 Subject: [PATCH 105/755] updated tooltip messages Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 5c6cdb00e..5ba1795fb 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -180,7 +180,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case ProviderColumn: return tr("Where the mod was downloaded from."); case SizeColumn: - return tr("The size of the resource."); + return tr("The size of the mod."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index d94751629..e9c3d3043 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -161,7 +161,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O case DateColumn: return tr("The date and time this resource pack was last changed (or added)."); case SizeColumn: - return tr("The size of the resource."); + return tr("The size of the resource pack."); default: return {}; } From a4978946d56f1c21df4d812d33df14897ebfae1b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 4 Nov 2023 00:07:51 +0200 Subject: [PATCH 106/755] disabled lax deps Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 8 +++---- .../mod/tasks/GetModDependenciesTask.h | 9 ++++++-- launcher/modplatform/CheckUpdateTask.h | 5 +++- launcher/ui/dialogs/ModUpdateDialog.cpp | 23 +++++++++++++------ .../ui/dialogs/ResourceDownloadDialog.cpp | 9 ++++---- launcher/ui/dialogs/ReviewMessageBox.cpp | 5 +++- launcher/ui/dialogs/ReviewMessageBox.h | 1 + 7 files changed, 41 insertions(+), 19 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 682f98240..e2563ebe8 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -259,9 +259,9 @@ void GetModDependenciesTask::removePack(const QVariant addonId) #endif } -QHash GetModDependenciesTask::getRequiredBy() +auto GetModDependenciesTask::getExtraInfo() -> QHash { - QHash rby; + QHash rby; auto fullList = m_selected + m_pack_dependencies; for (auto& mod : fullList) { auto addonId = mod->pack->addonId; @@ -283,7 +283,7 @@ QHash GetModDependenciesTask::getRequiredBy() req.append(smod->pack->name); } } - rby[addonId.toString()] = req; + rby[addonId.toString()] = { maybeInstalled(mod), req }; } return rby; } @@ -335,7 +335,7 @@ bool GetModDependenciesTask::isLocalyInstalled(std::shared_ptr p }) != m_pack_dependencies.end(); // check loaded dependencies } -bool GetModDependenciesTask::isLaxInstalled(std::shared_ptr pDep) +bool GetModDependenciesTask::maybeInstalled(std::shared_ptr pDep) { return std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), [pDep](QString i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName, true); diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h index 6ec373525..4b2608045 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h @@ -50,6 +50,11 @@ class GetModDependenciesTask : public SequentialTask { } }; + struct PackDependencyExtraInfo { + bool maybe_installed; + QStringList required_by; + }; + struct Provider { ModPlatform::ResourceProvider name; std::shared_ptr mod; @@ -62,7 +67,7 @@ class GetModDependenciesTask : public SequentialTask { QList> selected); auto getDependecies() const -> QList> { return m_pack_dependencies; } - QHash getRequiredBy(); + QHash getExtraInfo(); protected slots: Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int); @@ -74,7 +79,7 @@ class GetModDependenciesTask : public SequentialTask { void removePack(const QVariant addonId); bool isLocalyInstalled(std::shared_ptr pDep); - bool isLaxInstalled(std::shared_ptr pDep); + bool maybeInstalled(std::shared_ptr pDep); private: QList> m_pack_dependencies; diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 8bd83d988..b19b25484 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -28,6 +28,7 @@ class CheckUpdateTask : public Task { QString changelog; ModPlatform::ResourceProvider provider; shared_qobject_ptr download; + bool enabled = true; public: UpdatableMod(QString name, @@ -37,7 +38,8 @@ class CheckUpdateTask : public Task { std::optional new_v_type, QString changelog, ModPlatform::ResourceProvider p, - shared_qobject_ptr t) + shared_qobject_ptr t, + bool enabled = true) : name(name) , old_hash(old_h) , old_version(old_v) @@ -46,6 +48,7 @@ class CheckUpdateTask : public Task { , changelog(changelog) , provider(p) , download(t) + , enabled(enabled) {} }; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 1a70ea59a..b7e8eea14 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -212,19 +212,25 @@ void ModUpdateDialog::checkCandidates() } static FlameAPI api; - auto getRequiredBy = depTask->getRequiredBy(); + auto dependencyExtraInfo = depTask->getExtraInfo(); for (auto dep : depTask->getDependecies()) { auto changelog = dep->version.changelog; 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, dep->version.version_type, - changelog, dep->pack->provider, download_task - }; + auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString()); + CheckUpdateTask::UpdatableMod updatable = { dep->pack->name, + dep->version.hash, + "", + dep->version.version, + dep->version.version_type, + changelog, + dep->pack->provider, + download_task, + !extraInfo.maybe_installed }; - appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString())); + appendMod(updatable, extraInfo.required_by); m_tasks.insert(updatable.name, updatable.download); } } @@ -404,7 +410,10 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy) { auto item_top = new QTreeWidgetItem(ui->modTreeWidget); - item_top->setCheckState(0, Qt::CheckState::Checked); + item_top->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + if (!info.enabled) { + item_top->setToolTip(0, tr("Mod was disabled as it may be already instaled.")); + } item_top->setText(0, info.name); item_top->setExpanded(true); diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index dc7cfff06..cae4cb4dd 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -132,7 +132,7 @@ void ResourceDownloadDialog::confirm() auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); confirm_dialog->retranslateUi(resourcesString()); - QHash getRequiredBy; + QHash dependencyExtraInfo; if (auto task = getModDependenciesTask(); task) { connect(task.get(), &Task::failed, this, [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); @@ -157,7 +157,7 @@ void ResourceDownloadDialog::confirm() } else { for (auto dep : task->getDependecies()) addResource(dep->pack, dep->version); - getRequiredBy = task->getRequiredBy(); + dependencyExtraInfo = task->getExtraInfo(); } } @@ -166,9 +166,10 @@ void ResourceDownloadDialog::confirm() return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0; }); for (auto& task : selected) { + auto extraInfo = dependencyExtraInfo.value(task->getPack()->addonId.toString()); confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(), - ProviderCaps.name(task->getProvider()), getRequiredBy.value(task->getPack()->addonId.toString()), - task->getVersion().version_type.toString() }); + ProviderCaps.name(task->getProvider()), extraInfo.required_by, + task->getVersion().version_type.toString(), !extraInfo.maybe_installed }); } if (confirm_dialog->exec()) { diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index aa668f8c2..e25186755 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -34,8 +34,11 @@ auto ReviewMessageBox::create(QWidget* parent, QString&& title, QString&& icon) void ReviewMessageBox::appendResource(ResourceInformation&& info) { auto itemTop = new QTreeWidgetItem(ui->modTreeWidget); - itemTop->setCheckState(0, Qt::CheckState::Checked); + itemTop->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); itemTop->setText(0, info.name); + if (!info.enabled) { + itemTop->setToolTip(0, tr("Mod was disabled as it may be already instaled.")); + } auto filenameItem = new QTreeWidgetItem(itemTop); filenameItem->setText(0, tr("Filename: %1").arg(info.filename)); diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index 596f39c8e..d9b4b0b7a 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -19,6 +19,7 @@ class ReviewMessageBox : public QDialog { QString provider; QStringList required_by; QString version_type; + bool enabled = true; }; void appendResource(ResourceInformation&& info); From f828ea8929f9ba23f5f2d6a852c9634eebdf7786 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 4 Nov 2023 11:49:05 +0200 Subject: [PATCH 107/755] updated the texture pack tooltips Signed-off-by: Trial97 --- launcher/minecraft/mod/TexturePackFolderModel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index ef241351f..e3f369bb8 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -135,15 +135,15 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or switch (section) { case ActiveColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("Is the resource enabled?"); + return tr("Is the texture pack enabled?"); case NameColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("The name of the resource."); + return tr("The name of the texture pack."); case DateColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("The date and time this resource was last changed (or added)."); + return tr("The date and time this texture pack was last changed (or added)."); case SizeColumn: - return tr("The size of the resource."); + return tr("The size of the texture pack."); default: return {}; } From 5afe6600eea7d569c6f47dcd98226ca10c40aa62 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 4 Nov 2023 16:49:35 +0200 Subject: [PATCH 108/755] use fs::move instead of qt rename Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 3 +-- launcher/InstanceList.cpp | 3 +-- launcher/modplatform/flame/FlameInstanceCreationTask.cpp | 4 ++-- launcher/modplatform/legacy_ftb/PackInstallTask.cpp | 2 +- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 4 ++-- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index c7d5f85fa..794804985 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -652,8 +652,7 @@ bool move(const QString& source, const QString& dest) if (err) { qWarning() << "Failed to move file:" << QString::fromStdString(err.message()); - qDebug() << "Source file:" << source; - qDebug() << "Destination file:" << dest; + qDebug() << "Source file:" << source << ";Destination file:" << dest; } return err.value() == 0; diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 2b8f34293..8f0cbba79 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -953,7 +953,6 @@ bool InstanceList::commitStagedInstance(const QString& path, if (groupName.isEmpty() && !groupName.isNull()) groupName = QString(); - QDir dir; QString instID; InstancePtr inst; @@ -977,7 +976,7 @@ bool InstanceList::commitStagedInstance(const QString& path, return false; } } else { - if (!dir.rename(path, destination)) { + if (!FS::move(path, destination)) { qWarning() << "Failed to move" << path << "to" << destination; return false; } diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 2a26ce944..7e107f2d2 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -321,7 +321,7 @@ bool FlameCreationTask::createInstance() // Keep index file in case we need it some other time (like when changing versions) QString new_index_place(FS::PathCombine(parent_folder, "manifest.json")); FS::ensureFilePathExists(new_index_place); - QFile::rename(index_path, new_index_place); + FS::move(index_path, new_index_place); } catch (const JSONValidationError& e) { setError(tr("Could not understand pack manifest:\n") + e.cause()); @@ -335,7 +335,7 @@ bool FlameCreationTask::createInstance() Override::createOverrides("overrides", parent_folder, overridePath); QString mcPath = FS::PathCombine(m_stagingPath, "minecraft"); - if (!QFile::rename(overridePath, mcPath)) { + if (!FS::move(overridePath, mcPath)) { setError(tr("Could not rename the overrides folder:\n") + m_pack.overrides); return false; } diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 091296751..867773f58 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -137,7 +137,7 @@ void PackInstallTask::install() QDir unzipMcDir(m_stagingPath + "/unzip/minecraft"); if (unzipMcDir.exists()) { // ok, found minecraft dir, move contents to instance dir - if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) { + if (!FS::move(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) { emitFailed(tr("Failed to move unzipped Minecraft!")); return; } diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index e732ad39c..f01c52d21 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -171,7 +171,7 @@ bool ModrinthCreationTask::createInstance() // Keep index file in case we need it some other time (like when changing versions) QString new_index_place(FS::PathCombine(parent_folder, "modrinth.index.json")); FS::ensureFilePathExists(new_index_place); - QFile::rename(index_path, new_index_place); + FS::move(index_path, new_index_place); auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft"); @@ -181,7 +181,7 @@ bool ModrinthCreationTask::createInstance() Override::createOverrides("overrides", parent_folder, override_path); // Apply the overrides - if (!QFile::rename(override_path, mcPath)) { + if (!FS::move(override_path, mcPath)) { setError(tr("Could not rename the overrides folder:\n") + "overrides"); return false; } From 1d67fc66466566955719d4ab599d8b5482182f46 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 5 Nov 2023 16:47:33 +0200 Subject: [PATCH 109/755] added special case for windows Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 794804985..5bb10b069 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -643,6 +643,19 @@ void ExternalLinkFileProcess::runLinkFile() qDebug() << "Process exited"; } +bool moveByCopy(const QString& source, const QString& dest) +{ + if (!copy(source, dest)()) { // copy + qDebug() << "Copy of" << source << "to" << dest << "failed!"; + return false; + } + if (!deletePath(source)) { // remove original + qDebug() << "Deletion of" << source << "failed!"; + return false; + }; + return true; +} + bool move(const QString& source, const QString& dest) { std::error_code err; @@ -650,12 +663,14 @@ bool move(const QString& source, const QString& dest) ensureFilePathExists(dest); fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err); - if (err) { - qWarning() << "Failed to move file:" << QString::fromStdString(err.message()); - qDebug() << "Source file:" << source << ";Destination file:" << dest; + if (err.value() != 0) { + if (moveByCopy(source, dest)) + return true; + qDebug() << "Move of" << source << "to" << dest << "failed!"; + qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value()); + return false; } - - return err.value() == 0; + return true; } bool deletePath(QString path) From 7532a410433296eb5b84865e921857580f646c38 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 9 Nov 2023 21:38:38 +0200 Subject: [PATCH 110/755] Improvements to catpack Signed-off-by: Trial97 --- launcher/ui/instanceview/InstanceView.cpp | 2 +- launcher/ui/themes/CatPack.cpp | 23 +++++++++++++++++++++-- launcher/ui/themes/CatPack.h | 2 +- launcher/ui/themes/ThemeManager.cpp | 14 +++++++------- launcher/ui/themes/ThemeManager.h | 2 +- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 7530fdfba..4500f6d4d 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -465,7 +465,7 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) widWidth = m_catPixmap.width(); if (m_catPixmap.height() < widHeight) widHeight = m_catPixmap.height(); - auto pixmap = m_catPixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatio); + auto pixmap = m_catPixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); QRect rectOfPixmap = pixmap.rect(); rectOfPixmap.moveBottomRight(this->viewport()->rect().bottomRight()); painter.drawPixmap(rectOfPixmap.topLeft(), pixmap); diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp index bbcb58bc8..cd1a5772d 100644 --- a/launcher/ui/themes/CatPack.cpp +++ b/launcher/ui/themes/CatPack.cpp @@ -36,7 +36,10 @@ #include "ui/themes/CatPack.h" #include #include +#include #include +#include +#include #include "FileSystem.h" #include "Json.h" @@ -79,7 +82,7 @@ JsonCatPack::JsonCatPack(QFileInfo& manifestInfo) : BasicCatPack(manifestInfo.di auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "CatPack JSON file"); const auto root = doc.object(); m_name = Json::requireString(root, "name", "Catpack name"); - m_defaultPath = FS::PathCombine(path, Json::requireString(root, "default", "Default Cat")); + m_default_path = FS::PathCombine(path, Json::requireString(root, "default", "Default Cat")); auto variants = Json::ensureArray(root, "variants", QJsonArray(), "Catpack Variants"); for (auto v : variants) { auto variant = Json::ensureObject(v, QJsonObject(), "Cat variant"); @@ -117,5 +120,21 @@ QString JsonCatPack::path(QDate now) if (startDate <= now && now <= endDate) return var.path; } - return m_defaultPath; + auto dInfo = QFileInfo(m_default_path); + if (!dInfo.isDir()) + return m_default_path; + + QStringList supportedImageFormats; + for (auto format : QImageReader::supportedImageFormats()) { + supportedImageFormats.append("*." + format); + } + + auto files = QDir(m_default_path).entryInfoList(supportedImageFormats, QDir::Files, QDir::Name); + if (files.length() == 0) + return ""; + auto idx = now.dayOfYear() % files.length(); + auto isRandom = dInfo.fileName().compare("random", Qt::CaseInsensitive) == 0; + if (isRandom) + idx = QRandomGenerator::global()->bounded(0, files.length()); + return files[idx].absoluteFilePath(); } diff --git a/launcher/ui/themes/CatPack.h b/launcher/ui/themes/CatPack.h index 1d310e796..5a13d0cef 100644 --- a/launcher/ui/themes/CatPack.h +++ b/launcher/ui/themes/CatPack.h @@ -87,6 +87,6 @@ class JsonCatPack : public BasicCatPack { QString path(QDate now); private: - QString m_defaultPath; + QString m_default_path; QList m_variants; }; diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index 0bcac100c..a128fc3f5 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -178,8 +178,8 @@ QList ThemeManager::getValidApplicationThemes() QList ThemeManager::getValidCatPacks() { QList ret; - ret.reserve(m_catPacks.size()); - for (auto&& [id, theme] : m_catPacks) { + ret.reserve(m_cat_packs.size()); + for (auto&& [id, theme] : m_cat_packs) { ret.append(theme.get()); } return ret; @@ -244,8 +244,8 @@ void ThemeManager::applyCurrentlySelectedTheme(bool initial) QString ThemeManager::getCatPack(QString catName) { - auto catIter = m_catPacks.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); - if (catIter != m_catPacks.end()) { + auto catIter = m_cat_packs.find(!catName.isEmpty() ? catName : APPLICATION->settings()->get("BackgroundCat").toString()); + if (catIter != m_cat_packs.end()) { auto& catPack = catIter->second; themeDebugLog() << "applying catpack" << catPack->id(); return catPack->path(); @@ -253,14 +253,14 @@ QString ThemeManager::getCatPack(QString catName) themeWarningLog() << "Tried to get invalid catPack:" << catName; } - return m_catPacks.begin()->second->path(); + return m_cat_packs.begin()->second->path(); } QString ThemeManager::addCatPack(std::unique_ptr catPack) { QString id = catPack->id(); - if (m_catPacks.find(id) == m_catPacks.end()) - m_catPacks.emplace(id, std::move(catPack)); + if (m_cat_packs.find(id) == m_cat_packs.end()) + m_cat_packs.emplace(id, std::move(catPack)); else themeWarningLog() << "CatPack(" << id << ") not added to prevent id duplication"; return id; diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index b5c66677b..b77b5947a 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -61,7 +61,7 @@ class ThemeManager { QDir m_iconThemeFolder{ "iconthemes" }; QDir m_applicationThemeFolder{ "themes" }; QDir m_catPacksFolder{ "catpacks" }; - std::map> m_catPacks; + std::map> m_cat_packs; void initializeThemes(); void initializeCatPacks(); From b4bfc03e8b25d8eeea250560c1fc5e1c055677b2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 9 Nov 2023 21:48:46 +0200 Subject: [PATCH 111/755] Added button to refresh themes and catpacks Signed-off-by: Trial97 --- launcher/ui/themes/ThemeManager.cpp | 10 +++++++++ launcher/ui/themes/ThemeManager.h | 2 ++ .../ui/widgets/ThemeCustomizationWidget.cpp | 21 +++++++++++++++++++ .../ui/widgets/ThemeCustomizationWidget.h | 1 + .../ui/widgets/ThemeCustomizationWidget.ui | 7 +++++++ 5 files changed, 41 insertions(+) diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index 0bcac100c..cb90bc826 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -313,3 +313,13 @@ void ThemeManager::initializeCatPacks() } } } + +void ThemeManager::refresh() +{ + m_themes.clear(); + m_icons.clear(); + m_catPacks.clear(); + + initializeThemes(); + initializeCatPacks(); +}; \ No newline at end of file diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index b5c66677b..8ed587806 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -55,6 +55,8 @@ class ThemeManager { QString getCatPack(QString catName = ""); QList getValidCatPacks(); + void refresh(); + private: std::map> m_themes; std::map m_icons; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index 0de97441f..a0e682bb9 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -39,6 +39,8 @@ ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(pa [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); connect(ui->catPackFolder, &QPushButton::clicked, this, [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); }); + + connect(ui->refreshButton, &QPushButton::clicked, this, &ThemeCustomizationWidget::refresh); } ThemeCustomizationWidget::~ThemeCustomizationWidget() @@ -169,3 +171,22 @@ void ThemeCustomizationWidget::retranslate() { ui->retranslateUi(this); } + +void ThemeCustomizationWidget::refresh() +{ + applySettings(); + disconnect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); + disconnect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyWidgetTheme); + disconnect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyCatTheme); + APPLICATION->themeManager()->refresh(); + ui->iconsComboBox->clear(); + ui->widgetStyleComboBox->clear(); + ui->backgroundCatComboBox->clear(); + loadSettings(); + connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); + connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyWidgetTheme); + connect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); +}; \ No newline at end of file diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.h b/launcher/ui/widgets/ThemeCustomizationWidget.h index cef5fb6c6..6977b8495 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.h +++ b/launcher/ui/widgets/ThemeCustomizationWidget.h @@ -44,6 +44,7 @@ class ThemeCustomizationWidget : public QWidget { void applyIconTheme(int index); void applyWidgetTheme(int index); void applyCatTheme(int index); + void refresh(); signals: int currentIconThemeChanged(int index); diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui index 4503181c2..322f9d6a7 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -167,6 +167,13 @@ + + + + Refresh + + + From 5e2eb7bbbb6b84ab8b8b85fe03a65ea9579e9f37 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 9 Nov 2023 23:30:30 +0200 Subject: [PATCH 112/755] Added button to select the FTBApp instances directory Signed-off-by: Trial97 --- launcher/Application.cpp | 3 + .../modplatform/import_ftb/ImportFTBPage.cpp | 8 ++ .../modplatform/import_ftb/ImportFTBPage.ui | 73 +++++++++++++------ .../modplatform/import_ftb/ListModel.cpp | 21 +++++- .../pages/modplatform/import_ftb/ListModel.h | 3 +- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index be252f1c5..a280a6013 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -740,6 +740,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("ModrinthToken", ""); m_settings->registerSetting("UserAgentOverride", ""); + // FTBApp instances + m_settings->registerSetting("FTBAppInstancesPath", ""); + // Init page provider { m_globalSettingsProvider = std::make_shared(tr("Settings")); diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index d46f16b4d..ac06f4cdd 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -20,6 +20,7 @@ #include "ui/widgets/ProjectItem.h" #include "ui_ImportFTBPage.h" +#include #include #include "FileSystem.h" #include "ListModel.h" @@ -56,6 +57,13 @@ ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidg connect(ui->searchEdit, &QLineEdit::textChanged, this, &ImportFTBPage::triggerSearch); + connect(ui->browseButton, &QPushButton::clicked, this, [this] { + auto path = listModel->getPath(); + QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), path, QFileDialog::ShowDirsOnly); + if (!dir.isEmpty()) + listModel->setPath(dir); + }); + ui->modpackList->setItemDelegate(new ProjectItemDelegate(this)); ui->modpackList->selectionModel()->reset(); } diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui index 5e09fb6d1..6613a5939 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui @@ -11,7 +11,7 @@ - + @@ -21,28 +21,7 @@ - - - - - - Search and filter... - - - true - - - - - - - Search - - - - - - + @@ -69,6 +48,54 @@ + + + + + + Search and filter... + + + true + + + + + + + Search + + + + + + + Select FTBApp instances directory + + + + + + + .. + + + true + + + + + + + + + Note: If your FTB instances are not in the default location, select it using the button next to search. + + + Qt::AlignCenter + + + diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index 134bdc0c3..e058937a6 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -17,11 +17,13 @@ */ #include "ListModel.h" +#include #include #include #include #include #include +#include "Application.h" #include "FileSystem.h" #include "StringUtils.h" #include "modplatform/import_ftb/PackHelpers.h" @@ -29,7 +31,7 @@ namespace FTBImportAPP { -QString getPath() +QString getStaticPath() { QString partialPath; #if defined(Q_OS_OSX) @@ -42,14 +44,14 @@ QString getPath() return FS::PathCombine(partialPath, ".ftba"); } -const QString ListModel::FTB_APP_PATH = getPath(); +static const QString FTB_APP_PATH = FS::PathCombine(getStaticPath(), "instances"); void ListModel::update() { beginResetModel(); modpacks.clear(); - QString instancesPath = FS::PathCombine(FTB_APP_PATH, "instances"); + QString instancesPath = getPath(); if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks); @@ -168,4 +170,17 @@ FilterModel::Sorting FilterModel::getCurrentSorting() { return currentSorting; } +void ListModel::setPath(QString path) +{ + APPLICATION->settings()->set("FTBAppInstancesPath", path); + update(); +} + +QString ListModel::getPath() +{ + auto path = APPLICATION->settings()->get("FTBAppInstancesPath").toString(); + if (path.isEmpty() || !QFileInfo(path).exists()) + path = FTB_APP_PATH; + return path; +} } // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h index 111928276..ed33a88f3 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -60,7 +60,8 @@ class ListModel : public QAbstractListModel { void update(); - static const QString FTB_APP_PATH; + QString getPath(); + void setPath(QString path); private: ModpackList modpacks; From 463608b289eeffc3e20e2af396875ee084db11d1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 Nov 2023 19:37:11 +0200 Subject: [PATCH 113/755] fixed shader packs detection Signed-off-by: Trial97 --- .../minecraft/mod/ResourceFolderModel.cpp | 30 ++++++++++++++++++- launcher/minecraft/mod/ResourceFolderModel.h | 7 ++--- launcher/minecraft/mod/ShaderPack.cpp | 5 ---- launcher/minecraft/mod/ShaderPack.h | 1 - .../minecraft/mod/tasks/LocalModParseTask.cpp | 2 +- .../mod/tasks/LocalResourcePackParseTask.cpp | 4 ++- .../mod/tasks/LocalShaderPackParseTask.cpp | 4 ++- .../mod/tasks/LocalTexturePackParseTask.cpp | 4 ++- .../pages/instance/ExternalResourcesPage.cpp | 1 + 9 files changed, 42 insertions(+), 16 deletions(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 0503b660b..de638681e 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -283,7 +283,12 @@ void ResourceFolderModel::resolveResource(Resource* res) connect( task.get(), &Task::failed, this, [=] { onParseFailed(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection); connect( - task.get(), &Task::finished, this, [=] { m_active_parse_tasks.remove(ticket); }, Qt::ConnectionType::QueuedConnection); + task.get(), &Task::finished, this, + [=] { + m_active_parse_tasks.remove(ticket); + emit parseFinished(); + }, + Qt::ConnectionType::QueuedConnection); m_helper_thread_task.addTask(task); @@ -630,3 +635,26 @@ QString ResourceFolderModel::instDirPath() const { return QFileInfo(m_instance->instanceRoot()).absoluteFilePath(); } + +void ResourceFolderModel::onParseFailed(int ticket, QString resource_id) +{ + auto iter = m_active_parse_tasks.constFind(ticket); + if (iter == m_active_parse_tasks.constEnd()) + return; + + auto removed_index = m_resources_index[resource_id]; + auto removed_it = m_resources.begin() + removed_index; + Q_ASSERT(removed_it != m_resources.end()); + + beginRemoveRows(QModelIndex(), removed_index, removed_index); + m_resources.erase(removed_it); + + // update index + m_resources_index.clear(); + int idx = 0; + for (auto const& mod : qAsConst(m_resources)) { + m_resources_index[mod->internal_id()] = idx; + idx++; + } + endRemoveRows(); +} diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 60b8879c0..d6ac6e0df 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -143,6 +143,7 @@ class ResourceFolderModel : public QAbstractListModel { signals: void updateFinished(); + void parseFinished(); protected: /** This creates a new update task to be executed by update(). @@ -189,11 +190,7 @@ class ResourceFolderModel : public QAbstractListModel { * if the resource is complex and has more stuff to parse. */ virtual void onParseSucceeded(int ticket, QString resource_id); - virtual void onParseFailed(int ticket, QString resource_id) - { - Q_UNUSED(ticket); - Q_UNUSED(resource_id); - } + virtual void onParseFailed(int ticket, QString resource_id); protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. diff --git a/launcher/minecraft/mod/ShaderPack.cpp b/launcher/minecraft/mod/ShaderPack.cpp index 2c094f26a..ccb344cb5 100644 --- a/launcher/minecraft/mod/ShaderPack.cpp +++ b/launcher/minecraft/mod/ShaderPack.cpp @@ -35,8 +35,3 @@ bool ShaderPack::valid() const { return m_pack_format != ShaderPackFormat::INVALID; } - -bool ShaderPack::applyFilter(QRegularExpression filter) const -{ - return valid() && Resource::applyFilter(filter); -} diff --git a/launcher/minecraft/mod/ShaderPack.h b/launcher/minecraft/mod/ShaderPack.h index d07c124be..ec0f9404e 100644 --- a/launcher/minecraft/mod/ShaderPack.h +++ b/launcher/minecraft/mod/ShaderPack.h @@ -54,7 +54,6 @@ class ShaderPack : public Resource { void setPackFormat(ShaderPackFormat new_format); bool valid() const override; - [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; protected: mutable QMutex m_data_lock; diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index e9e12d86a..0594351cb 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -746,7 +746,7 @@ void LocalModParseTask::executeTask() m_result->details = mod.details(); if (m_aborted) - emit finished(); + emitAborted(); else emitSucceeded(); } diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 26bc07637..f495c9b8d 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -286,8 +286,10 @@ bool LocalResourcePackParseTask::abort() void LocalResourcePackParseTask::executeTask() { - if (!ResourcePackUtils::process(m_resource_pack)) + if (!ResourcePackUtils::process(m_resource_pack)) { + emitFailed("this is not a resource pack"); return; + } if (m_aborted) emitAborted(); diff --git a/launcher/minecraft/mod/tasks/LocalShaderPackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalShaderPackParseTask.cpp index a9949735b..4deebcd1d 100644 --- a/launcher/minecraft/mod/tasks/LocalShaderPackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalShaderPackParseTask.cpp @@ -103,8 +103,10 @@ bool LocalShaderPackParseTask::abort() void LocalShaderPackParseTask::executeTask() { - if (!ShaderPackUtils::process(m_shader_pack)) + if (!ShaderPackUtils::process(m_shader_pack)) { + emitFailed("this is not a shader pack"); return; + } if (m_aborted) emitAborted(); diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index d7e61ca90..00cc2def2 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -241,8 +241,10 @@ bool LocalTexturePackParseTask::abort() void LocalTexturePackParseTask::executeTask() { - if (!TexturePackUtils::process(m_texture_pack)) + if (!TexturePackUtils::process(m_texture_pack)) { + emitFailed("this is not a texture pack"); return; + } if (m_aborted) emitAborted(); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 1a8fafa9b..f46f8af5f 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -87,6 +87,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared }; connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra); connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra); + connect(model.get(), &ResourceFolderModel::parseFinished, this, updateExtra); connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); From 50a4d61ded300c47c727f1104d17b2960ff5d0c6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 Nov 2023 23:26:41 +0200 Subject: [PATCH 114/755] Fixed demo mode Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 87 +++++++++++++------- launcher/LaunchController.h | 2 + launcher/minecraft/auth/AuthSession.cpp | 11 ++- launcher/minecraft/auth/AuthSession.h | 2 +- launcher/minecraft/auth/MinecraftAccount.cpp | 2 + 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index f59cdeda8..6d5a9e93e 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -129,12 +129,63 @@ void LaunchController::decideAccount() } } +bool LaunchController::askPlayDemo() +{ + QMessageBox box(m_parentWidget); + box.setWindowTitle(tr("Play demo?")); + box.setText( + tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play " + "the demo?")); + box.setIcon(QMessageBox::Warning); + auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); + auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); + box.setDefaultButton(cancelButton); + + box.exec(); + return box.clickedButton() == demoButton; +} + +QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok) +{ + // we ask the user for a player name + QString message = tr("Choose your offline mode player name."); + if (demo) { + message = tr("Choose your demo mode player name."); + } + + QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); + QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName; + QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); + if (!ok) + return {}; + if (name.length()) { + usedname = name; + APPLICATION->settings()->set("LastOfflinePlayerName", usedname); + } + return usedname; +} + void LaunchController::login() { decideAccount(); - // if no account is selected, we bail if (!m_accountToUse) { + // if no account is selected, ask about demo + if (!m_demo) { + m_demo = askPlayDemo(); + } + if (m_demo) { + // we ask the user for a player name + bool ok = false; + auto name = askOfflineName("Player", m_demo, ok); + if (ok) { + m_session = std::make_shared(); + m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]"))); + launchInstance(); + return; + } + } + // if no account is selected, we bail emitFailed(tr("No account selected for launch.")); return; } @@ -175,24 +226,12 @@ void LaunchController::login() if (!m_session->wants_online) { // we ask the user for a player name bool ok = false; - - QString message = tr("Choose your offline mode player name."); - if (m_session->demo) { - message = tr("Choose your demo mode player name."); - } - - QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); - QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; - QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); + auto name = askOfflineName(m_session->player_name, m_session->demo, ok); if (!ok) { tryagain = false; break; } - if (name.length()) { - usedname = name; - APPLICATION->settings()->set("LastOfflinePlayerName", usedname); - } - m_session->MakeOffline(usedname); + m_session->MakeOffline(name); // offline flavored game from here :3 } if (m_accountToUse->ownsMinecraft()) { @@ -212,20 +251,10 @@ void LaunchController::login() return; } else { // play demo ? - QMessageBox box(m_parentWidget); - box.setWindowTitle(tr("Play demo?")); - box.setText( - tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play " - "the demo?")); - box.setIcon(QMessageBox::Warning); - auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); - auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); - box.setDefaultButton(cancelButton); - - box.exec(); - if (box.clickedButton() == demoButton) { - // play demo here - m_session->MakeDemo(); + if (!m_session->demo) { + m_session->demo = askPlayDemo(); + } + if (m_session->demo) { // play demo here launchInstance(); } else { emitFailed(tr("Launch cancelled - account does not own Minecraft.")); diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index f1c88afb7..02b1e0d33 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -74,6 +74,8 @@ class LaunchController : public Task { void login(); void launchInstance(); void decideAccount(); + bool askPlayDemo(); + QString askOfflineName(QString playerName, bool demo, bool& ok); private slots: void readyForLaunch(); diff --git a/launcher/minecraft/auth/AuthSession.cpp b/launcher/minecraft/auth/AuthSession.cpp index 37534f983..3657befec 100644 --- a/launcher/minecraft/auth/AuthSession.cpp +++ b/launcher/minecraft/auth/AuthSession.cpp @@ -30,8 +30,13 @@ bool AuthSession::MakeOffline(QString offline_playername) return true; } -void AuthSession::MakeDemo() +void AuthSession::MakeDemo(QString name, QString u) { - player_name = "Player"; + wants_online = false; demo = true; -} + uuid = u; + session = "-"; + access_token = "0"; + player_name = name; + status = PlayableOnline; // needs online to download the assets +}; \ No newline at end of file diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index cec238033..54e7d69e0 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -10,7 +10,7 @@ class QNetworkAccessManager; struct AuthSession { bool MakeOffline(QString offline_playername); - void MakeDemo(); + void MakeDemo(QString name, QString uuid); QString serializeUserProperties(); diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 545d06aed..c3c29f598 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -269,6 +269,8 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) session->player_name = data.profileName(); // profile ID session->uuid = data.profileId(); + if (session->uuid.isEmpty()) + session->uuid = uuidFromUsername(session->player_name).toString().remove(QRegularExpression("[{}-]")); // 'legacy' or 'mojang', depending on account type session->user_type = typeString(); if (!session->access_token.isEmpty()) { From 17916dbd2439f3af8a5b29f1fba2cf9703e6c8ad Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 12 Nov 2023 20:27:55 +0200 Subject: [PATCH 115/755] Fixed inconsistent Ok in instance import Signed-off-by: Trial97 --- launcher/ui/dialogs/NewInstanceDialog.cpp | 3 +++ launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 6f3f7f7ec..3524d43f8 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -97,6 +97,9 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, ui->verticalLayout->insertWidget(2, m_container); m_container->addButtons(m_buttons); + connect(m_container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* selected) { + m_buttons->button(QDialogButtonBox::Ok)->setEnabled(creationTask && !instName().isEmpty()); + }); // Bonk Qt over its stupid head and make sure it understands which button is the default one... // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 8e2b9a902..2da9a712d 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -103,6 +103,7 @@ void ModrinthPage::retranslate() void ModrinthPage::openedImpl() { BasePage::openedImpl(); + suggestCurrent(); triggerSearch(); } From 6804e2ba59a45f8ec504e392829bacb51a30c86d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 14 Nov 2023 18:40:45 +0200 Subject: [PATCH 116/755] moved export to list to the mods page Signed-off-by: Trial97 --- launcher/ui/MainWindow.cpp | 10 ---- launcher/ui/MainWindow.h | 1 - launcher/ui/MainWindow.ui | 9 ---- launcher/ui/dialogs/ExportToModListDialog.cpp | 48 ++++++++----------- launcher/ui/dialogs/ExportToModListDialog.h | 10 ++-- launcher/ui/pages/instance/ModFolderPage.cpp | 18 ++++++- launcher/ui/pages/instance/ModFolderPage.h | 1 + 7 files changed, 42 insertions(+), 55 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 1da982dad..3ae80ebb6 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -96,7 +96,6 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ExportInstanceDialog.h" #include "ui/dialogs/ExportPackDialog.h" -#include "ui/dialogs/ExportToModListDialog.h" #include "ui/dialogs/IconPickerDialog.h" #include "ui/dialogs/ImportResourceDialog.h" #include "ui/dialogs/NewInstanceDialog.h" @@ -208,7 +207,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi exportInstanceMenu->addAction(ui->actionExportInstanceZip); exportInstanceMenu->addAction(ui->actionExportInstanceMrPack); exportInstanceMenu->addAction(ui->actionExportInstanceFlamePack); - exportInstanceMenu->addAction(ui->actionExportInstanceToModList); ui->actionExportInstance->setMenu(exportInstanceMenu); } @@ -1390,14 +1388,6 @@ void MainWindow::on_actionExportInstanceMrPack_triggered() } } -void MainWindow::on_actionExportInstanceToModList_triggered() -{ - if (m_selectedInstance) { - ExportToModListDialog dlg(m_selectedInstance, this); - dlg.exec(); - } -} - void MainWindow::on_actionExportInstanceFlamePack_triggered() { if (m_selectedInstance) { diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 0b7287404..7935ddf56 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -156,7 +156,6 @@ class MainWindow : public QMainWindow { void on_actionExportInstanceZip_triggered(); void on_actionExportInstanceMrPack_triggered(); void on_actionExportInstanceFlamePack_triggered(); - void on_actionExportInstanceToModList_triggered(); void on_actionRenameInstance_triggered(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 91b2c2703..ee90eeba1 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -479,15 +479,6 @@ CurseForge (zip) - - - - .. - - - Mod List - - diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index a343f555a..f767727d7 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -22,8 +22,6 @@ #include #include "FileSystem.h" #include "Markdown.h" -#include "minecraft/MinecraftInstance.h" -#include "minecraft/mod/ModFolderModel.h" #include "modplatform/helpers/ExportToModList.h" #include "ui_ExportToModListDialog.h" @@ -41,21 +39,12 @@ const QHash ExportToModListDialog::exampleLin { ExportToModList::CSV, "{name},{url},{version},\"{authors}\"" }, }; -ExportToModListDialog::ExportToModListDialog(InstancePtr instance, QWidget* parent) - : QDialog(parent), m_template_changed(false), name(instance->name()), ui(new Ui::ExportToModListDialog) +ExportToModListDialog::ExportToModListDialog(QString name, QList mods, QWidget* parent) + : QDialog(parent), m_mods(mods), m_template_changed(false), m_name(name), ui(new Ui::ExportToModListDialog) { ui->setupUi(this); enableCustom(false); - MinecraftInstance* mcInstance = dynamic_cast(instance.get()); - if (mcInstance) { - mcInstance->loaderModList()->update(); - connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, [this, mcInstance]() { - m_allMods = mcInstance->loaderModList()->allMods(); - triggerImp(); - }); - } - connect(ui->formatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ExportToModListDialog::formatChanged); connect(ui->authorsCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->versionCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); @@ -64,7 +53,7 @@ ExportToModListDialog::ExportToModListDialog(InstancePtr instance, QWidget* pare connect(ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); }); connect(ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); }); connect(ui->templateText, &QTextEdit::textChanged, this, [this] { - if (ui->templateText->toPlainText() != exampleLines[format]) + if (ui->templateText->toPlainText() != exampleLines[m_format]) ui->formatComboBox->setCurrentIndex(5); else triggerImp(); @@ -73,6 +62,7 @@ ExportToModListDialog::ExportToModListDialog(InstancePtr instance, QWidget* pare this->ui->finalText->selectAll(); this->ui->finalText->copy(); }); + triggerImp(); } ExportToModListDialog::~ExportToModListDialog() @@ -86,38 +76,38 @@ void ExportToModListDialog::formatChanged(int index) case 0: { enableCustom(false); ui->resultText->show(); - format = ExportToModList::HTML; + m_format = ExportToModList::HTML; break; } case 1: { enableCustom(false); ui->resultText->show(); - format = ExportToModList::MARKDOWN; + m_format = ExportToModList::MARKDOWN; break; } case 2: { enableCustom(false); ui->resultText->hide(); - format = ExportToModList::PLAINTXT; + m_format = ExportToModList::PLAINTXT; break; } case 3: { enableCustom(false); ui->resultText->hide(); - format = ExportToModList::JSON; + m_format = ExportToModList::JSON; break; } case 4: { enableCustom(false); ui->resultText->hide(); - format = ExportToModList::CSV; + m_format = ExportToModList::CSV; break; } case 5: { m_template_changed = true; enableCustom(true); ui->resultText->hide(); - format = ExportToModList::CUSTOM; + m_format = ExportToModList::CUSTOM; break; } } @@ -126,8 +116,8 @@ void ExportToModListDialog::formatChanged(int index) void ExportToModListDialog::triggerImp() { - if (format == ExportToModList::CUSTOM) { - ui->finalText->setPlainText(ExportToModList::exportToModList(m_allMods, ui->templateText->toPlainText())); + if (m_format == ExportToModList::CUSTOM) { + ui->finalText->setPlainText(ExportToModList::exportToModList(m_mods, ui->templateText->toPlainText())); return; } auto opt = 0; @@ -137,9 +127,9 @@ void ExportToModListDialog::triggerImp() opt |= ExportToModList::Version; if (ui->urlCheckBox->isChecked()) opt |= ExportToModList::Url; - auto txt = ExportToModList::exportToModList(m_allMods, format, static_cast(opt)); + auto txt = ExportToModList::exportToModList(m_mods, m_format, static_cast(opt)); ui->finalText->setPlainText(txt); - switch (format) { + switch (m_format) { case ExportToModList::CUSTOM: return; case ExportToModList::HTML: @@ -155,7 +145,7 @@ void ExportToModListDialog::triggerImp() case ExportToModList::CSV: break; } - auto exampleLine = exampleLines[format]; + auto exampleLine = exampleLines[m_format]; if (!m_template_changed && ui->templateText->toPlainText() != exampleLine) ui->templateText->setPlainText(exampleLine); } @@ -163,9 +153,9 @@ void ExportToModListDialog::triggerImp() void ExportToModListDialog::done(int result) { if (result == Accepted) { - const QString filename = FS::RemoveInvalidFilenameChars(name); + const QString filename = FS::RemoveInvalidFilenameChars(m_name); const QString output = - QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + extension()), + QFileDialog::getSaveFileName(this, tr("Export %1").arg(m_name), FS::PathCombine(QDir::homePath(), filename + extension()), "File (*.txt *.html *.md *.json *.csv)", nullptr); if (output.isEmpty()) @@ -178,7 +168,7 @@ void ExportToModListDialog::done(int result) QString ExportToModListDialog::extension() { - switch (format) { + switch (m_format) { case ExportToModList::HTML: return ".html"; case ExportToModList::MARKDOWN: @@ -197,7 +187,7 @@ QString ExportToModListDialog::extension() void ExportToModListDialog::addExtra(ExportToModList::OptionalData option) { - if (format != ExportToModList::CUSTOM) + if (m_format != ExportToModList::CUSTOM) return; switch (option) { case ExportToModList::Authors: diff --git a/launcher/ui/dialogs/ExportToModListDialog.h b/launcher/ui/dialogs/ExportToModListDialog.h index 9886ae5a0..4ebe203f7 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.h +++ b/launcher/ui/dialogs/ExportToModListDialog.h @@ -20,7 +20,6 @@ #include #include -#include "BaseInstance.h" #include "minecraft/mod/Mod.h" #include "modplatform/helpers/ExportToModList.h" @@ -32,7 +31,7 @@ class ExportToModListDialog : public QDialog { Q_OBJECT public: - explicit ExportToModListDialog(InstancePtr instance, QWidget* parent = nullptr); + explicit ExportToModListDialog(QString name, QList mods, QWidget* parent = nullptr); ~ExportToModListDialog(); void done(int result) override; @@ -46,10 +45,11 @@ class ExportToModListDialog : public QDialog { private: QString extension(); void enableCustom(bool enabled); - QList m_allMods; + + QList m_mods; bool m_template_changed; - QString name; - ExportToModList::Formats format = ExportToModList::Formats::HTML; + QString m_name; + ExportToModList::Formats m_format = ExportToModList::Formats::HTML; Ui::ExportToModListDialog* ui; static const QHash exampleLines; }; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index a38e608f2..31fb49d0c 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -37,6 +37,7 @@ */ #include "ModFolderPage.h" +#include "ui/dialogs/ExportToModListDialog.h" #include "ui_ExternalResourcesPage.h" #include @@ -111,6 +112,10 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); actionRemoveItemMetadata->setEnabled(false); + auto actionExportMetadata = updateMenu->addAction(tr("Export metadata")); + actionExportMetadata->setToolTip(tr("Export mod's metadata to text")); + connect(actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata); + ui->actionUpdateItem->setMenu(updateMenu); ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); @@ -124,7 +129,7 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this, check_allow_update, actionRemoveItemMetadata] { + [this, check_allow_update, actionRemoveItemMetadata, actionExportMetadata] { ui->actionUpdateItem->setEnabled(check_allow_update()); auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); @@ -360,3 +365,14 @@ void ModFolderPage::deleteModMetadata() m_model->deleteModsMetadata(selection); } + +void ModFolderPage::exportModMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectedMods = m_model->selectedMods(selection); + if (selectedMods.length() == 0) + selectedMods = m_model->allMods(); + + ExportToModListDialog dlg(m_instance->name(), selectedMods, this); + dlg.exec(); +} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 4672350c6..928f5ca7e 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -62,6 +62,7 @@ class ModFolderPage : public ExternalResourcesPage { private slots: void removeItems(const QItemSelection& selection) override; void deleteModMetadata(); + void exportModMetadata(); void installMods(); void updateMods(bool includeDeps = false); From bedb4da8699f6fc8e23d8e9b5644f6dd92251b7b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 14 Nov 2023 19:37:23 +0200 Subject: [PATCH 117/755] Added filename to the modlist export Signed-off-by: Trial97 --- .../modplatform/helpers/ExportToModList.cpp | 32 ++++++++++++++++--- .../modplatform/helpers/ExportToModList.h | 6 +--- launcher/ui/dialogs/ExportToModListDialog.cpp | 13 ++++++-- launcher/ui/dialogs/ExportToModListDialog.ui | 14 ++++++++ 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/launcher/modplatform/helpers/ExportToModList.cpp b/launcher/modplatform/helpers/ExportToModList.cpp index 1f01c4a89..86d051c75 100644 --- a/launcher/modplatform/helpers/ExportToModList.cpp +++ b/launcher/modplatform/helpers/ExportToModList.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ #include "ExportToModList.h" +#include #include #include #include @@ -42,17 +43,28 @@ QString toHTML(QList mods, OptionalData extraData) } if (extraData & Authors && !mod->authors().isEmpty()) line += " by " + mod->authors().join(", ").toHtmlEscaped(); + if (extraData & FileName) + line += QString(" (%1)").arg(mod->fileinfo().fileName().toHtmlEscaped()); + lines.append(QString("
  • %1
  • ").arg(line)); } return QString("
      \n\t%1\n
    ").arg(lines.join("\n\t")); } +QString toMarkdownEscaped(QString src) +{ + for (auto ch : "\\`*_{}[]<>()#+-.!|") + src.replace(ch, QString("\\%1").arg(ch)); + return src; +} + QString toMarkdown(QList mods, OptionalData extraData) { QStringList lines; + for (auto mod : mods) { auto meta = mod->metadata(); - auto modName = mod->name(); + auto modName = toMarkdownEscaped(mod->name()); if (extraData & Url) { auto url = mod->metaurl(); if (!url.isEmpty()) @@ -60,14 +72,16 @@ QString toMarkdown(QList mods, OptionalData extraData) } auto line = modName; if (extraData & Version) { - auto ver = mod->version(); + auto ver = toMarkdownEscaped(mod->version()); if (ver.isEmpty() && meta != nullptr) - ver = meta->version().toString(); + ver = toMarkdownEscaped(meta->version().toString()); if (!ver.isEmpty()) line += QString(" [%1]").arg(ver); } if (extraData & Authors && !mod->authors().isEmpty()) - line += " by " + mod->authors().join(", "); + line += " by " + toMarkdownEscaped(mod->authors().join(", ")); + if (extraData & FileName) + line += QString(" (%1)").arg(toMarkdownEscaped(mod->fileinfo().fileName())); lines << "- " + line; } return lines.join("\n"); @@ -95,6 +109,8 @@ QString toPlainTXT(QList mods, OptionalData extraData) } if (extraData & Authors && !mod->authors().isEmpty()) line += " by " + mod->authors().join(", "); + if (extraData & FileName) + line += QString(" (%1)").arg(mod->fileinfo().fileName()); lines << line; } return lines.join("\n"); @@ -122,6 +138,8 @@ QString toJSON(QList mods, OptionalData extraData) } if (extraData & Authors && !mod->authors().isEmpty()) line["authors"] = QJsonArray::fromStringList(mod->authors()); + if (extraData & FileName) + line["filename"] = mod->fileinfo().fileName(); lines << line; } QJsonDocument doc; @@ -154,6 +172,8 @@ QString toCSV(QList mods, OptionalData extraData) authors = QString("\"%1\"").arg(mod->authors().join(",")); data << authors; } + if (extraData & FileName) + data << mod->fileinfo().fileName(); lines << data.join(","); } return lines.join("\n"); @@ -189,11 +209,13 @@ QString exportToModList(QList mods, QString lineTemplate) if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); auto authors = mod->authors().join(", "); + auto filename = mod->fileinfo().fileName(); lines << QString(lineTemplate) .replace("{name}", modName) .replace("{url}", url) .replace("{version}", ver) - .replace("{authors}", authors); + .replace("{authors}", authors) + .replace("{filename}", filename); } return lines.join("\n"); } diff --git a/launcher/modplatform/helpers/ExportToModList.h b/launcher/modplatform/helpers/ExportToModList.h index 7ea4ba9c2..ab7797fe6 100644 --- a/launcher/modplatform/helpers/ExportToModList.h +++ b/launcher/modplatform/helpers/ExportToModList.h @@ -23,11 +23,7 @@ namespace ExportToModList { enum Formats { HTML, MARKDOWN, PLAINTXT, JSON, CSV, CUSTOM }; -enum OptionalData { - Authors = 1 << 0, - Url = 1 << 1, - Version = 1 << 2, -}; +enum OptionalData { Authors = 1 << 0, Url = 1 << 1, Version = 1 << 2, FileName = 1 << 3 }; QString exportToModList(QList mods, Formats format, OptionalData extraData); QString exportToModList(QList mods, QString lineTemplate); } // namespace ExportToModList diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index f767727d7..95916421d 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -49,14 +49,15 @@ ExportToModListDialog::ExportToModListDialog(QString name, QList mods, QWi connect(ui->authorsCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->versionCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->urlCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); + connect(ui->filenameCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger); connect(ui->authorsButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Authors); }); connect(ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); }); connect(ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); }); + connect(ui->filenameButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::FileName); }); connect(ui->templateText, &QTextEdit::textChanged, this, [this] { if (ui->templateText->toPlainText() != exampleLines[m_format]) ui->formatComboBox->setCurrentIndex(5); - else - triggerImp(); + triggerImp(); }); connect(ui->copyButton, &QPushButton::clicked, this, [this](bool) { this->ui->finalText->selectAll(); @@ -127,6 +128,8 @@ void ExportToModListDialog::triggerImp() opt |= ExportToModList::Version; if (ui->urlCheckBox->isChecked()) opt |= ExportToModList::Url; + if (ui->filenameCheckBox->isChecked()) + opt |= ExportToModList::FileName; auto txt = ExportToModList::exportToModList(m_mods, m_format, static_cast(opt)); ui->finalText->setPlainText(txt); switch (m_format) { @@ -199,6 +202,9 @@ void ExportToModListDialog::addExtra(ExportToModList::OptionalData option) case ExportToModList::Version: ui->templateText->insertPlainText("{version}"); break; + case ExportToModList::FileName: + ui->templateText->insertPlainText("{filename}"); + break; } } void ExportToModListDialog::enableCustom(bool enabled) @@ -211,4 +217,7 @@ void ExportToModListDialog::enableCustom(bool enabled) ui->urlCheckBox->setHidden(enabled); ui->urlButton->setHidden(!enabled); + + ui->filenameCheckBox->setHidden(enabled); + ui->filenameButton->setHidden(!enabled); } diff --git a/launcher/ui/dialogs/ExportToModListDialog.ui b/launcher/ui/dialogs/ExportToModListDialog.ui index 4f8ab52b5..3afda2fa8 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.ui +++ b/launcher/ui/dialogs/ExportToModListDialog.ui @@ -117,6 +117,13 @@
    + + + + Filename + + + @@ -138,6 +145,13 @@ + + + + Filename + + + From 4d93f4adb144079449c05edf939226a6bb215921 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 Nov 2023 12:10:47 +0200 Subject: [PATCH 118/755] Fixed folder size Signed-off-by: Trial97 --- launcher/minecraft/World.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/Resource.cpp | 20 +++++++++++++++++-- launcher/minecraft/mod/Resource.h | 2 ++ .../minecraft/mod/ResourceFolderModel.cpp | 2 +- .../minecraft/mod/ResourcePackFolderModel.cpp | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 2 +- 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index 1a680ac56..1eba148a5 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -206,8 +206,8 @@ int64_t calculateWorldSize(const QFileInfo& file) QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); int64_t total = 0; while (it.hasNext()) { - total += it.fileInfo().size(); it.next(); + total += it.fileInfo().size(); } return total; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 5ba1795fb..8f79a4e00 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -111,7 +111,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return QVariant(); } diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 79e52a881..0115cf7ca 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -1,5 +1,6 @@ #include "Resource.h" +#include #include #include @@ -18,6 +19,20 @@ void Resource::setFile(QFileInfo file_info) parseFile(); } +qint64 calculateFileSize(const QFileInfo& file) +{ + if (file.isDir()) { + QDirIterator it(file.absoluteFilePath(), QDir::Files); + qint64 total = 0; + while (it.hasNext()) { + it.next(); + total += it.fileInfo().size(); + } + return total; + } + return file.size(); +} + void Resource::parseFile() { QString file_name{ m_file_info.fileName() }; @@ -26,6 +41,7 @@ void Resource::parseFile() m_internal_id = file_name; + m_size = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -90,9 +106,9 @@ std::pair Resource::compare(const Resource& other, SortType type) con return { -1, type == SortType::DATE }; break; case SortType::SIZE: { - if (fileinfo().size() > other.fileinfo().size()) + if (size() > other.size()) return { 1, type == SortType::SIZE }; - if (fileinfo().size() < other.fileinfo().size()) + if (size() < other.size()) return { -1, type == SortType::SIZE }; break; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index d94e4b368..61949f917 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -45,6 +45,7 @@ class Resource : public QObject { [[nodiscard]] auto internal_id() const -> QString { return m_internal_id; } [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } + [[nodiscard]] qint64 size() const { return m_size; } [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } @@ -117,4 +118,5 @@ class Resource : public QObject { bool m_is_resolving = false; bool m_is_resolved = false; int m_resolution_ticket = 0; + qint64 m_size = 0; }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 16ff01227..648bf84db 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -416,7 +416,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index e9c3d3043..cf2eb50df 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -88,7 +88,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return {}; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index e3f369bb8..63fb1f8b3 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -79,7 +79,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return {}; } From 215465e833e6faa17053bbaaf24b20ed99655d01 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 Nov 2023 14:11:34 +0200 Subject: [PATCH 119/755] added subdirectories to iteration Signed-off-by: Trial97 --- launcher/minecraft/mod/Resource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 0115cf7ca..72652cefb 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -22,7 +22,7 @@ void Resource::setFile(QFileInfo file_info) qint64 calculateFileSize(const QFileInfo& file) { if (file.isDir()) { - QDirIterator it(file.absoluteFilePath(), QDir::Files); + QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); qint64 total = 0; while (it.hasNext()) { it.next(); From a531f32b693cd621de5abfbe0e6cf97b344f51a4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 16 Nov 2023 13:26:29 +0200 Subject: [PATCH 120/755] fixed settings saving Signed-off-by: Trial97 --- launcher/InstanceCopyTask.cpp | 3 +-- launcher/settings/INIFile.cpp | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index 8abf30640..cdcd61ba6 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -142,9 +142,8 @@ void InstanceCopyTask::copyFinished() if (!m_keepPlaytime) { inst->resetTimePlayed(); } - if (m_useLinks) - inst->addLinkedInstanceId(m_origInstance->id()); if (m_useLinks) { + inst->addLinkedInstanceId(m_origInstance->id()); auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt")); QByteArray allowed_symlinks; diff --git a/launcher/settings/INIFile.cpp b/launcher/settings/INIFile.cpp index 4fb11ed35..e97741f20 100644 --- a/launcher/settings/INIFile.cpp +++ b/launcher/settings/INIFile.cpp @@ -54,6 +54,7 @@ bool INIFile::saveFile(QString fileName) insert("ConfigVersion", "1.2"); QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; _settings_obj.setFallbacksEnabled(false); + _settings_obj.clear(); for (Iterator iter = begin(); iter != end(); iter++) _settings_obj.setValue(iter.key(), iter.value()); From 4e3132e8b2b18057f905668d3177098157ba2dac Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 17 Nov 2023 22:40:36 +0200 Subject: [PATCH 121/755] reseted the setting Signed-off-by: Trial97 --- launcher/BaseInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index d0b0ad3bc..3b6d69a78 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -65,7 +65,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("totalTimePlayed", 0); if (m_settings->get("totalTimePlayed").toLongLong() < 0) - m_settings->set("totalTimePlayed", 0); + m_settings->reset("totalTimePlayed"); m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("linkedInstances", "[]"); From f7e478c7559549c1342a23f488f9df7b85d4ffbd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 19 Nov 2023 02:02:02 +0200 Subject: [PATCH 122/755] Added all supported files filter Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ImportPage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 3e3c36b7b..26cd25e5f 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -200,7 +200,9 @@ void ImportPage::setExtraInfo(const QMap& extra_info) void ImportPage::on_modpackBtn_clicked() { - auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString(); + auto filter = + tr("Supported files") + QString(" (%1 *.mrpack)").arg(QMimeDatabase().mimeTypeForName("application/zip").globPatterns().join(" ")); + filter += ";;" + QMimeDatabase().mimeTypeForName("application/zip").filterString(); //: Option for filtering for *.mrpack files when importing filter += ";;" + tr("Modrinth pack") + " (*.mrpack)"; const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), filter); From 76216335b1b81e51885d66eeac55359435705f7b Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 19 Nov 2023 15:26:47 +0200 Subject: [PATCH 123/755] Update launcher/ui/pages/modplatform/ImportPage.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/modplatform/ImportPage.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 26cd25e5f..d4f6339e1 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -200,9 +200,10 @@ void ImportPage::setExtraInfo(const QMap& extra_info) void ImportPage::on_modpackBtn_clicked() { - auto filter = - tr("Supported files") + QString(" (%1 *.mrpack)").arg(QMimeDatabase().mimeTypeForName("application/zip").globPatterns().join(" ")); - filter += ";;" + QMimeDatabase().mimeTypeForName("application/zip").filterString(); + const QMimeType zip = QMimeDatabase().mimeTypeForName("application/zip"); + const QString filter = + tr("Supported files") + QString(" (%1 *.mrpack)").arg(zip.globPatterns().join(" ")); + filter += ";;" + zip.filterString(); //: Option for filtering for *.mrpack files when importing filter += ";;" + tr("Modrinth pack") + " (*.mrpack)"; const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), filter); From 80c85aef99e47a767f45767106e72fbb10ef33d0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 19 Nov 2023 15:37:18 +0200 Subject: [PATCH 124/755] fixed const stuff Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ImportPage.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index d4f6339e1..ae4bd49d7 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -200,9 +200,8 @@ void ImportPage::setExtraInfo(const QMap& extra_info) void ImportPage::on_modpackBtn_clicked() { - const QMimeType zip = QMimeDatabase().mimeTypeForName("application/zip"); - const QString filter = - tr("Supported files") + QString(" (%1 *.mrpack)").arg(zip.globPatterns().join(" ")); + const QMimeType zip = QMimeDatabase().mimeTypeForName("application/zip"); + auto filter = tr("Supported files") + QString(" (%1 *.mrpack)").arg(zip.globPatterns().join(" ")); filter += ";;" + zip.filterString(); //: Option for filtering for *.mrpack files when importing filter += ";;" + tr("Modrinth pack") + " (*.mrpack)"; From 26931475aeb0ed4f0e6e3d935095d9b1fa69b651 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 19 Nov 2023 15:58:30 +0200 Subject: [PATCH 125/755] made folder size reflect the number of elements Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/Resource.cpp | 25 ++++++++++--------- launcher/minecraft/mod/Resource.h | 4 +-- .../minecraft/mod/ResourceFolderModel.cpp | 2 +- .../minecraft/mod/ResourcePackFolderModel.cpp | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 369ad3936..4e98d1520 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -110,7 +110,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return QVariant(); } diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 72652cefb..d8727db0a 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -5,6 +5,7 @@ #include #include "FileSystem.h" +#include "StringUtils.h" Resource::Resource(QObject* parent) : QObject(parent) {} @@ -19,18 +20,18 @@ void Resource::setFile(QFileInfo file_info) parseFile(); } -qint64 calculateFileSize(const QFileInfo& file) +QString calculateFileSize(const QFileInfo& file) { if (file.isDir()) { - QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); - qint64 total = 0; - while (it.hasNext()) { - it.next(); - total += it.fileInfo().size(); - } - return total; + auto dir = QDir(file.absoluteFilePath()); + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + auto count = dir.count(); + auto str = QObject::tr("item"); + if (count != 1) + str = QObject::tr("items"); + return QString("%1 %2").arg(QString::number(count), str); } - return file.size(); + return StringUtils::humanReadableFileSize(file.size(), true); } void Resource::parseFile() @@ -41,7 +42,7 @@ void Resource::parseFile() m_internal_id = file_name; - m_size = calculateFileSize(m_file_info); + m_size_str = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -106,9 +107,9 @@ std::pair Resource::compare(const Resource& other, SortType type) con return { -1, type == SortType::DATE }; break; case SortType::SIZE: { - if (size() > other.size()) + if (fileinfo().size() > other.fileinfo().size()) return { 1, type == SortType::SIZE }; - if (size() < other.size()) + if (fileinfo().size() < other.fileinfo().size()) return { -1, type == SortType::SIZE }; break; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 61949f917..029afbd5b 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -45,7 +45,7 @@ class Resource : public QObject { [[nodiscard]] auto internal_id() const -> QString { return m_internal_id; } [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } - [[nodiscard]] qint64 size() const { return m_size; } + [[nodiscard]] QString sizeStr() const { return m_size_str; } [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } @@ -118,5 +118,5 @@ class Resource : public QObject { bool m_is_resolving = false; bool m_is_resolved = false; int m_resolution_ticket = 0; - qint64 m_size = 0; + QString m_size_str; }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 6a9c5b7dd..8e14f4e80 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -417,7 +417,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 7a6138833..7ad7b3038 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -88,7 +88,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return {}; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 38db9d8ff..a042c9113 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -79,7 +79,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return {}; } From 85b4e1f24f444627f48fe2e13b6cb34c45aa7361 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 22 Nov 2023 19:23:24 +0200 Subject: [PATCH 126/755] Made column hidden by default Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 1 + launcher/minecraft/mod/ResourceFolderModel.cpp | 4 ++++ launcher/minecraft/mod/ResourceFolderModel.h | 1 + launcher/minecraft/mod/ResourcePackFolderModel.cpp | 1 + launcher/minecraft/mod/TexturePackFolderModel.cpp | 1 + 5 files changed, 8 insertions(+) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 98e21b9f0..6a948e2ec 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -75,6 +75,7 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true, true, true, true, true }; + m_columnsHiddenByDefault = { false, false, false, false, false, false, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 4a3ee9922..8a0797c58 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -528,6 +528,10 @@ void ResourceFolderModel::saveColumns(QTreeView* tree) void ResourceFolderModel::loadColumns(QTreeView* tree) { + for (auto i = 0; i < m_columnsHiddenByDefault.size(); ++i) { + tree->setColumnHidden(i, m_columnsHiddenByDefault[i]); + } + auto const setting_name = QString("UI/%1_Page/Columns").arg(id()); auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index b3f6d9a58..77bece636 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -203,6 +203,7 @@ class ResourceFolderModel : public QAbstractListModel { QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; QList m_columnsHideable = { false, false, true }; + QList m_columnsHiddenByDefault = { false, false, false }; QDir m_dir; BaseInstance* m_instance; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 693b8af05..3fcbca4ce 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -55,6 +55,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true }; + m_columnsHiddenByDefault = { false, false, false, false, false }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index f210501c7..1a35a3795 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -49,6 +49,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true }; + m_columnsHiddenByDefault = { false, false, false, false }; } Task* TexturePackFolderModel::createUpdateTask() From e3af4f9a7892d90f3da9432c686b2e4f211fd499 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 22 Nov 2023 20:34:12 +0200 Subject: [PATCH 127/755] Fixed playtime recording Signed-off-by: Trial97 --- launcher/BaseInstance.cpp | 11 +++++++---- launcher/BaseInstance.h | 1 + launcher/minecraft/launch/LauncherPartLaunch.cpp | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 33dc3f741..bb9e5223a 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -267,13 +267,18 @@ void BaseInstance::setRunning(bool running) m_isRunning = running; - if (!m_settings->get("RecordGameTime").toBool()) { - emit runningStatusChanged(running); + emit runningStatusChanged(running); +} + +void BaseInstance::setMinecraftRunning(bool running) +{ + if (!settings()->get("RecordGameTime").toBool()) { return; } if (running) { m_timeStarted = QDateTime::currentDateTime(); + setLastLaunch(m_timeStarted.toMSecsSinceEpoch()); } else { QDateTime timeEnded = QDateTime::currentDateTime(); @@ -283,8 +288,6 @@ void BaseInstance::setRunning(bool running) emit propertiesChanged(this); } - - emit runningStatusChanged(running); } int64_t BaseInstance::totalTimePlayed() const diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index f4ed9113c..499ec7866 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -104,6 +104,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_thisshowMainWindow(); m_parent->setPid(-1); + m_parent->instance()->setMinecraftRunning(false); // if the exit code wasn't 0, report this as a crash auto exitCode = m_process.exitCode(); if (exitCode != 0) { @@ -228,7 +229,6 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state) case LoggedProcess::Running: emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher); m_parent->setPid(m_process.processId()); - m_parent->instance()->setLastLaunch(); // send the launch script to the launcher part m_process.write(m_launchScript.toUtf8()); @@ -248,6 +248,7 @@ void LauncherPartLaunch::setWorkingDirectory(const QString& wd) void LauncherPartLaunch::proceed() { if (mayProceed) { + m_parent->instance()->setMinecraftRunning(true); QString launchString("launch\n"); m_process.write(launchString.toUtf8()); mayProceed = false; From 6169af70db9a58304e5517a8caeb05515a083aa6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 23 Nov 2023 11:59:35 +0200 Subject: [PATCH 128/755] Added back the FIXME comment Signed-off-by: Trial97 --- launcher/ui/pages/instance/ManagedPackPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index c419066a0..2210d0263 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -489,6 +489,7 @@ void FlameManagedPackPage::parseManagedPack() QString FlameManagedPackPage::url() const { + // FIXME: We should display the websiteUrl field, but this requires doing the API request first :( return "https://www.curseforge.com/projects/" + m_inst->getManagedPackID(); } From ceb88a170e4851baa722761235736fb9d025fb31 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 23 Nov 2023 12:22:31 +0200 Subject: [PATCH 129/755] Extracted reauthenticateCurrentAccount as a separate function Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 53 ++++++++++++++++++++--------------- launcher/LaunchController.h | 1 + 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 2c76fee4f..a72cc58b1 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -258,29 +258,8 @@ void LaunchController::login() continue; } case AccountState::Expired: { - auto errorString = tr("The account has expired and needs to be logged into manually. Press OK to log in again."); - auto button = QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, - QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel, - QMessageBox::StandardButton::Ok); - if (button == QMessageBox::StandardButton::Ok) { - auto accounts = APPLICATION->accounts(); - bool isDefault = accounts->defaultAccount() == m_accountToUse; - accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(m_accountToUse->profileId()))); - if (m_accountToUse->isMSA()) { - auto newAccount = MSALoginDialog::newAccount( - m_parentWidget, tr("Please enter your Mojang account email and password to add your account.")); - accounts->addAccount(newAccount); - if (isDefault) { - accounts->setDefaultAccount(newAccount); - } - m_accountToUse = nullptr; - decideAccount(); - continue; - } - emitFailed(tr("Account expired and re-login attempt failed")); - } else { - emitFailed(errorString); - } + if (reauthenticateCurrentAccount()) + continue; return; } case AccountState::Disabled: { @@ -304,6 +283,34 @@ void LaunchController::login() emitFailed(tr("Failed to launch.")); } +bool LaunchController::reauthenticateCurrentAccount() +{ + auto button = + QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), + tr("The account has expired and needs to be reauthenticated. Do you want to reauthenticate this account?"), + QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes); + if (button == QMessageBox::StandardButton::Yes) { + auto accounts = APPLICATION->accounts(); + bool isDefault = accounts->defaultAccount() == m_accountToUse; + accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(m_accountToUse->profileId()))); + if (m_accountToUse->isMSA()) { + auto newAccount = + MSALoginDialog::newAccount(m_parentWidget, tr("Please enter your Mojang account email and password to add your account.")); + accounts->addAccount(newAccount); + if (isDefault) { + accounts->setDefaultAccount(newAccount); + } + m_accountToUse = nullptr; + decideAccount(); + return true; + } + emitFailed(tr("Account expired and re-login attempt failed")); + } else { + emitFailed(tr("The account has expired and needs to be reauthenticated")); + } + return false; +} + void LaunchController::launchInstance() { Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index f1c88afb7..33aedb063 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -74,6 +74,7 @@ class LaunchController : public Task { void login(); void launchInstance(); void decideAccount(); + bool reauthenticateCurrentAccount(); private slots: void readyForLaunch(); From 93454163618f4500732a079b7943db2fbe22fc66 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 23 Nov 2023 12:29:42 +0200 Subject: [PATCH 130/755] Fixed localization stuff Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 5 ++--- launcher/minecraft/auth/AccountList.cpp | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 271297d5c..3f3cf2e7e 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -42,7 +42,6 @@ #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/EditAccountDialog.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/ProfileSelectDialog.h" #include "ui/dialogs/ProfileSetupDialog.h" @@ -145,7 +144,7 @@ void LaunchController::login() bool tryagain = true; unsigned int tries = 0; - if (!m_accountToUse->isOffline() && m_accountToUse->accountState() == AccountState::Offline) { + if (m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) { // Force account refresh on the account used to launch the instance updating the AccountState // only on first try and if it is not meant to be offline auto accounts = APPLICATION->accounts(); @@ -293,7 +292,7 @@ bool LaunchController::reauthenticateCurrentAccount() auto accounts = APPLICATION->accounts(); bool isDefault = accounts->defaultAccount() == m_accountToUse; accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(m_accountToUse->profileId()))); - if (m_accountToUse->isMSA()) { + if (m_accountToUse->accountType() == AccountType::MSA) { auto newAccount = MSALoginDialog::newAccount(m_parentWidget, tr("Please enter your Mojang account email and password to add your account.")); accounts->addAccount(newAccount); diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index c3c09003c..68ebe3626 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -52,8 +52,6 @@ #include #include -#include - enum AccountListVersion { MojangMSA = 3 }; AccountList::AccountList(QObject* parent) : QAbstractListModel(parent) From ac223a29ef61643a2313914d180801ce6dcae139 Mon Sep 17 00:00:00 2001 From: deadmeu Date: Sun, 23 Apr 2023 00:29:49 +1000 Subject: [PATCH 131/755] refactor: shorten desktop entry comment field Signed-off-by: deadmeu --- program_info/org.prismlauncher.PrismLauncher.desktop.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in index f08f2ba43..0995c7c2a 100644 --- a/program_info/org.prismlauncher.PrismLauncher.desktop.in +++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in @@ -1,7 +1,7 @@ [Desktop Entry] Version=1.0 Name=Prism Launcher -Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. +Comment=Discover, manage, and play Minecraft instances Type=Application Terminal=false Exec=@Launcher_APP_BINARY_NAME@ From 1a0fd5f9932574c95d0591d4d6d1d64093613f83 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 25 Nov 2023 00:18:10 +0200 Subject: [PATCH 132/755] Added mod reinstall dialog Signed-off-by: Trial97 --- .../ui/dialogs/ResourceDownloadDialog.cpp | 15 +++++ launcher/ui/dialogs/ResourceDownloadDialog.h | 2 + launcher/ui/pages/instance/ModFolderPage.cpp | 63 ++++++++++++++++++- launcher/ui/pages/instance/ModFolderPage.h | 1 + launcher/ui/pages/modplatform/ModModel.cpp | 10 +++ launcher/ui/pages/modplatform/ModModel.h | 1 + launcher/ui/pages/modplatform/ModPage.cpp | 12 +++- launcher/ui/pages/modplatform/ResourceModel.h | 2 + .../ui/pages/modplatform/ResourcePage.cpp | 60 ++++++++++++++++-- launcher/ui/pages/modplatform/ResourcePage.h | 4 ++ launcher/ui/widgets/PageContainer.h | 3 + 11 files changed, 165 insertions(+), 8 deletions(-) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 1431ea92c..5d49c0dfa 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -356,4 +356,19 @@ QList ShaderPackDownloadDialog::getPages() return pages; } +void ModDownloadDialog::setModMetadata(std::shared_ptr meta) +{ + switch (meta->provider) { + case ModPlatform::ResourceProvider::MODRINTH: + selectPage(Modrinth::id()); + break; + case ModPlatform::ResourceProvider::FLAME: + selectPage(Flame::id()); + break; + } + m_container->hidePageList(); + m_buttons.hide(); + auto page = selectedPage(); + page->openProject(meta->project_id); +} } // namespace ResourceDownload diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index a6efca138..7a0d6e895 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -107,6 +107,8 @@ class ModDownloadDialog final : public ResourceDownloadDialog { QList getPages() override; GetModDependenciesTask::Ptr getModDependenciesTask() override; + void setModMetadata(std::shared_ptr); + private: BaseInstance* m_instance; }; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index ce1cc3a90..e33bcfc36 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -40,6 +40,7 @@ #include "ui_ExternalResourcesPage.h" #include +#include #include #include #include @@ -121,10 +122,16 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr ui->actionsToolbar->addAction(ui->actionVisitItemPage); connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); + auto changeVersion = new QAction(tr("Reinstall")); + changeVersion->setToolTip(tr("Reinstall mod")); + changeVersion->setEnabled(false); + ui->actionsToolbar->insertActionAfter(ui->actionVisitItemPage, changeVersion); + connect(changeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion); + auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this, check_allow_update, actionRemoveItemMetadata] { + [this, check_allow_update, actionRemoveItemMetadata, changeVersion] { ui->actionUpdateItem->setEnabled(check_allow_update()); auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); @@ -134,11 +141,12 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr if (selected <= 1) { ui->actionVisitItemPage->setText(tr("Visit mod's page")); ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); - } else { ui->actionVisitItemPage->setText(tr("Visit mods' pages")); ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); } + + changeVersion->setEnabled(mods_list.length() == 1 && mods_list[0]->metadata() != nullptr); ui->actionVisitItemPage->setEnabled(selected != 0); actionRemoveItemMetadata->setEnabled(selected != 0); }); @@ -372,3 +380,54 @@ void ModFolderPage::deleteModMetadata() m_model->deleteModsMetadata(selection); } + +void ModFolderPage::changeModVersion() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (!profile->getModLoaders().has_value()) { + QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!")); + return; + } + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!")); + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto mods_list = m_model->selectedMods(selection); + if (mods_list.length() != 1 || mods_list[0]->metadata() == nullptr) + return; + + ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance); + mdownload.setModMetadata((*mods_list.begin())->metadata()); + if (mdownload.exec()) { + auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + + tasks->deleteLater(); + }); + + for (auto& task : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 4672350c6..9aa2fdbb1 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -66,6 +66,7 @@ class ModFolderPage : public ExternalResourcesPage { void installMods(); void updateMods(bool includeDeps = false); void visitModPages(); + void changeModVersion(); protected: std::shared_ptr m_model; diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index c628f74ac..e7012c095 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -79,4 +79,14 @@ bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const }); } +QVariant ModModel:: getInstalledPackVersion(ModPlatform::IndexedPack::Ptr pack) const +{ + auto allMods = static_cast(m_base_instance).loaderModList()->allMods(); + for (auto mod : allMods) { + if (auto meta = mod->metadata(); meta && meta->provider == pack->provider && meta->project_id == pack->addonId) { + return meta->version(); + } + } + return {}; +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index dd187aa8d..36540bb03 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -35,6 +35,7 @@ class ModModel : public ResourceModel { virtual ModPlatform::IndexedVersion loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) = 0; void setFilter(std::shared_ptr filter) { m_filter = filter; } + virtual QVariant getInstalledPackVersion(ModPlatform::IndexedPack::Ptr) const override; public slots: ResourceAPI::SearchArgs createSearchArguments() override; diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index d6cc1fdcc..0ac08d4dd 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -120,6 +120,8 @@ void ModPage::updateVersionList() auto current_pack = getCurrentPack(); if (!current_pack) return; + auto installedVersion = m_model->getInstalledPackVersion(current_pack); + auto installedIndex = -1; for (int i = 0; i < current_pack->versions.size(); i++) { auto version = current_pack->versions[i]; bool valid = false; @@ -129,13 +131,19 @@ void ModPage::updateVersionList() break; } } - + QString flag; + if (installedIndex == -1 && installedVersion.isValid() && installedVersion == version.fileId) { + flag = tr("[installed]"); + installedIndex = i; + } // 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)) { 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)); + m_ui->versionSelectionBox->addItem(QString("%1%2%3").arg(version.version, release_type, flag), QVariant(i)); } } + if (installedIndex != -1) + m_ui->versionSelectionBox->setCurrentIndex(installedIndex); if (m_ui->versionSelectionBox->count() == 0) { m_ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :(")); diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index 12db49080..97f9e36b4 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -55,6 +55,8 @@ class ResourceModel : public QAbstractListModel { [[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); } + virtual QVariant getInstalledPackVersion(ModPlatform::IndexedPack::Ptr) const { return {}; } + public slots: void fetchMore(const QModelIndex& parent) override; // NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12 diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 44a91003d..00bd7548a 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -260,7 +260,9 @@ void ResourcePage::updateVersionList() m_ui->versionSelectionBox->clear(); m_ui->versionSelectionBox->blockSignals(false); - if (current_pack) + if (current_pack) { + auto installedVersion = m_model->getInstalledPackVersion(current_pack); + for (int i = 0; i < current_pack->versions.size(); i++) { auto& version = current_pack->versions[i]; if (optedOut(version)) @@ -269,9 +271,10 @@ void ResourcePage::updateVersionList() 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)); - } + 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)); m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :(")); @@ -388,7 +391,7 @@ void ResourcePage::openUrl(const QUrl& url) } } - if (!page.isNull()) { + if (!page.isNull() && !m_do_not_jump_to_mod) { const QString slug = match.captured(1); // ensure the user isn't opening the same mod @@ -432,4 +435,53 @@ void ResourcePage::openUrl(const QUrl& url) QDesktopServices::openUrl(url); } +void ResourcePage::openProject(QVariant projectID) +{ + m_ui->sortByBox->hide(); + m_ui->searchButton->hide(); + m_ui->searchEdit->hide(); + m_ui->resourceFilterButton->hide(); + m_ui->packView->hide(); + m_ui->resourceSelectionButton->hide(); + m_do_not_jump_to_mod = true; + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + + auto okBtn = buttonBox->button(QDialogButtonBox::Ok); + okBtn->setDefault(true); + okBtn->setAutoDefault(true); + okBtn->setText(tr("Reinstall")); + okBtn->setShortcut(tr("Ctrl+Return")); + okBtn->setEnabled(false); + + auto cancelBtn = buttonBox->button(QDialogButtonBox::Cancel); + cancelBtn->setDefault(false); + cancelBtn->setAutoDefault(false); + + connect(okBtn, &QPushButton::clicked, this, [this] { + onResourceSelected(); + m_parent_dialog->accept(); + }); + + connect(cancelBtn, &QPushButton::clicked, m_parent_dialog, &ResourceDownloadDialog::reject); + m_ui->gridLayout_4->addWidget(buttonBox, 1, 2); + + auto jump = [this, okBtn] { + for (int row = 0; row < m_model->rowCount({}); row++) { + const QModelIndex index = m_model->index(row); + m_ui->packView->setCurrentIndex(index); + okBtn->setEnabled(true); + return; + } + m_ui->packDescription->setText(tr("The resource was not found")); + }; + + m_ui->searchEdit->setText("#" + projectID.toString()); + triggerSearch(); + + if (m_model->hasActiveSearchJob()) + connect(m_model->activeSearchJob().get(), &Task::finished, jump); + else + jump(); +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 235b44412..212c83ea5 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -83,6 +83,8 @@ class ResourcePage : public QWidget, public BasePage { QList selectedPacks() { return m_model->selectedPacks(); } bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); } + virtual void openProject(QVariant projectID); + protected slots: virtual void triggerSearch() {} @@ -118,6 +120,8 @@ class ResourcePage : public QWidget, public BasePage { // Used to do instant searching with a delay to cache quick changes QTimer m_search_timer; + + bool m_do_not_jump_to_mod = false; }; } // namespace ResourceDownload diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h index 05be1c3a5..ab4444c99 100644 --- a/launcher/ui/widgets/PageContainer.h +++ b/launcher/ui/widgets/PageContainer.h @@ -36,6 +36,7 @@ #pragma once +#include #include #include @@ -86,6 +87,8 @@ class PageContainer : public QWidget, public BasePageContainer { void changeEvent(QEvent*) override; + void hidePageList() { m_pageList->hide(); } + private: void createUI(); void retranslate(); From f4e098f7f542186c907aae2f9e634e36a1aae333 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 25 Nov 2023 09:36:55 +0200 Subject: [PATCH 133/755] Formated file Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ModModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index e7012c095..02fc8a3dd 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -79,7 +79,7 @@ bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const }); } -QVariant ModModel:: getInstalledPackVersion(ModPlatform::IndexedPack::Ptr pack) const +QVariant ModModel::getInstalledPackVersion(ModPlatform::IndexedPack::Ptr pack) const { auto allMods = static_cast(m_base_instance).loaderModList()->allMods(); for (auto mod : allMods) { From 2d91bd09d413177c9382e7b101cc4cd8f7d78b45 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 26 Nov 2023 23:49:56 +0200 Subject: [PATCH 134/755] Fixed crash on abort function not initialized Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ResourceModel.cpp | 6 ++++++ launcher/ui/pages/modplatform/flame/FlameModel.cpp | 4 ++++ launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index c83e844c3..6c02150bd 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -234,6 +234,12 @@ void ResourceModel::loadEntry(QModelIndex& entry) return; QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason)); }; + if (!callbacks.on_abort) + callbacks.on_abort = [this] { + if (!s_running_models.constFind(this).value()) + return; + qCritical() << tr("The request was abborted for an unknown reason"); + }; if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) runInfoJob(job); diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 8875a9452..3b266bcef 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -170,6 +170,10 @@ void ListModel::performPaginatedSearch() callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_abort = [this] { + qCritical() << "Search task aborted by an unknown reason!"; + searchRequestFailed("Abborted"); + }; static const FlameAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { jobPtr = job; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index f691a185d..227ed646b 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -140,6 +140,10 @@ void ModpackListModel::performPaginatedSearch() callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_abort = [this] { + qCritical() << "Search task aborted by an unknown reason!"; + searchRequestFailed("Abborted"); + }; static const ModrinthAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { jobPtr = job; From 6f6b6312a55d970c6d7d809305151e874a910f79 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 27 Nov 2023 09:39:31 +0200 Subject: [PATCH 135/755] Moved the button up Signed-off-by: Trial97 --- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 1 + launcher/ui/pages/instance/ModFolderPage.cpp | 6 +++--- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 5d49c0dfa..47b58a6ed 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -366,6 +366,7 @@ void ModDownloadDialog::setModMetadata(std::shared_ptr meta selectPage(Flame::id()); break; } + setWindowTitle(tr("Change %1 version").arg(meta->name)); m_container->hidePageList(); m_buttons.hide(); auto page = selectedPage(); diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index e33bcfc36..d46bc5652 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -122,10 +122,10 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr ui->actionsToolbar->addAction(ui->actionVisitItemPage); connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); - auto changeVersion = new QAction(tr("Reinstall")); - changeVersion->setToolTip(tr("Reinstall mod")); + auto changeVersion = new QAction(tr("Change Version")); + changeVersion->setToolTip(tr("Change mod version")); changeVersion->setEnabled(false); - ui->actionsToolbar->insertActionAfter(ui->actionVisitItemPage, changeVersion); + ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, changeVersion); connect(changeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion); auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 0ac08d4dd..ccbe5b377 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -133,7 +133,7 @@ void ModPage::updateVersionList() } QString flag; if (installedIndex == -1 && installedVersion.isValid() && installedVersion == version.fileId) { - flag = tr("[installed]"); + flag = QString(" [%1]").arg(tr("installed", "Mod version select box")); installedIndex = i; } // Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out From 463ff4c6ae9362c70e4f70eea0805ce7b6931145 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 27 Nov 2023 11:30:07 +0200 Subject: [PATCH 136/755] Removed assert permanantly Signed-off-by: Trial97 --- launcher/minecraft/mod/ResourceFolderModel.h | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index d764280b6..90e3aac2d 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -306,7 +306,6 @@ void ResourceFolderModel::applyUpdates(QSet& current_set, QSet auto removed_it = m_resources.begin() + removed_index; Q_ASSERT(removed_it != m_resources.end()); - Q_ASSERT(removed_set.contains(removed_it->get()->internal_id())); if ((*removed_it)->isResolving()) { auto ticket = (*removed_it)->resolutionTicket(); From eb011e6729fe49da867e6ae1ab2524424eedac46 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:42:24 +0100 Subject: [PATCH 137/755] chore: update qt to qt 6.6.1 Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4fb2d6794..24e7194ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.6.0' + qt_version: '6.6.1' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -73,7 +73,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.6.0' + qt_version: '6.6.1' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -83,7 +83,7 @@ jobs: qt_ver: 6 qt_host: mac qt_arch: '' - qt_version: '6.6.0' + qt_version: '6.6.1' qt_modules: 'qt5compat qtimageformats' qt_tools: '' From 8b4b0f2f018b84d545e4fba1071cf4b0456cec87 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 3 Dec 2023 00:19:28 +0000 Subject: [PATCH 138/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/8c9fa2545007b49a5db5f650ae91f227672c3877' (2023-11-01) → 'github:hercules-ci/flake-parts/34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5' (2023-12-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:NixOS/nixpkgs/0cbe9f69c234a7700596e943bfae7ef27a31b735?dir=lib' (2023-10-29) → 'github:NixOS/nixpkgs/e92039b55bcd58469325ded85d4f58dd5a4eaf58?dir=lib' (2023-11-29) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/0bd59c54ef06bc34eca01e37d689f5e46b3fe2f1' (2023-11-24) → 'github:nixos/nixpkgs/f5c27c6136db4d76c30e533c20517df6864c46ee' (2023-11-30) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 18ae9c463..efaa524f6 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1698882062, - "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "lastModified": 1701473968, + "narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1700856099, - "narHash": "sha256-RnEA7iJ36Ay9jI0WwP+/y4zjEhmeN6Cjs9VOFBH7eVQ=", + "lastModified": 1701336116, + "narHash": "sha256-kEmpezCR/FpITc6yMbAh4WrOCiT2zg5pSjnKrq51h5Y=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0bd59c54ef06bc34eca01e37d689f5e46b3fe2f1", + "rev": "f5c27c6136db4d76c30e533c20517df6864c46ee", "type": "github" }, "original": { @@ -123,11 +123,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1698611440, - "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "lastModified": 1701253981, + "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", "type": "github" }, "original": { From 577c737d686df43ee777a3b0382b788bb34ec170 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 3 Dec 2023 20:00:50 +0200 Subject: [PATCH 139/755] Fixed askIfShouldUpdate dialog on mac Signed-off-by: Trial97 --- launcher/InstanceTask.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/launcher/InstanceTask.cpp b/launcher/InstanceTask.cpp index 5b2398268..53476897c 100644 --- a/launcher/InstanceTask.cpp +++ b/launcher/InstanceTask.cpp @@ -2,6 +2,8 @@ #include "ui/dialogs/CustomMessageBox.h" +#include + InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name) { auto dialog = @@ -27,16 +29,15 @@ ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name) "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") .arg(original_version_name), - QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort); - info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance")); - info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance")); - info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel")); + QMessageBox::Information, QMessageBox::Cancel); + QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole); + QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole); info->exec(); - if (info->clickedButton() == info->button(QMessageBox::Ok)) + if (info->clickedButton() == update) return ShouldUpdate::Update; - if (info->clickedButton() == info->button(QMessageBox::Abort)) + if (info->clickedButton() == skip) return ShouldUpdate::SkipUpdating; return ShouldUpdate::Cancel; } From 657416fe30c96a7bfb3978e8f84c5d0c7951ba27 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 3 Dec 2023 21:29:02 +0200 Subject: [PATCH 140/755] Removed header Signed-off-by: Trial97 --- launcher/modplatform/helpers/ExportToModList.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/modplatform/helpers/ExportToModList.cpp b/launcher/modplatform/helpers/ExportToModList.cpp index 86d051c75..aea16ab50 100644 --- a/launcher/modplatform/helpers/ExportToModList.cpp +++ b/launcher/modplatform/helpers/ExportToModList.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ #include "ExportToModList.h" -#include #include #include #include From 132344df1cff26f2dbc16dd3a7dfacc53091b975 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:06:39 +0000 Subject: [PATCH 141/755] chore(deps): update korthout/backport-action action to v2.2.0 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index e5443439d..12850d697 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.1.1 + uses: korthout/backport-action@v2.2.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From faedd0fd65123d328f4f767649d6529424642299 Mon Sep 17 00:00:00 2001 From: Archy <59789660+IceCryptonym@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:40:46 +1000 Subject: [PATCH 142/755] Add togglable status bar Signed-off-by: Archy <59789660+IceCryptonym@users.noreply.github.com> --- launcher/Application.cpp | 2 ++ launcher/ui/MainWindow.cpp | 15 +++++++++++++++ launcher/ui/MainWindow.h | 2 ++ launcher/ui/MainWindow.ui | 9 +++++++++ 4 files changed, 28 insertions(+) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index cfede9dc1..ff5eb2a66 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -668,6 +668,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // The cat m_settings->registerSetting("TheCat", false); + m_settings->registerSetting("StatusBarVisible", true); + m_settings->registerSetting("ToolbarsLocked", false); m_settings->registerSetting("InstSortMode", "Name"); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 7b4d1c8a5..42de6c2c0 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -186,6 +186,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi ui->instanceToolBar->addContextMenuAction(ui->newsToolBar->toggleViewAction()); ui->instanceToolBar->addContextMenuAction(ui->instanceToolBar->toggleViewAction()); + ui->instanceToolBar->addContextMenuAction(ui->actionToggleStatusBar); ui->instanceToolBar->addContextMenuAction(ui->actionLockToolbars); } @@ -319,6 +320,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi setCatBackground(cat_enable); } + // Togglable status bar + { + bool statusBarVisible = APPLICATION->settings()->get("StatusBarVisible").toBool(); + ui->actionToggleStatusBar->setChecked(statusBarVisible); + connect(ui->actionToggleStatusBar, &QAction::toggled, this, &MainWindow::setStatusBarVisibility); + setStatusBarVisibility(statusBarVisible); + } + // Lock toolbars { bool toolbarsLocked = APPLICATION->settings()->get("ToolbarsLocked").toBool(); @@ -451,10 +460,16 @@ QMenu* MainWindow::createPopupMenu() QMenu* filteredMenu = QMainWindow::createPopupMenu(); filteredMenu->removeAction(ui->mainToolBar->toggleViewAction()); + filteredMenu->addAction(ui->actionToggleStatusBar); filteredMenu->addAction(ui->actionLockToolbars); return filteredMenu; } +void MainWindow::setStatusBarVisibility(bool state) +{ + statusBar()->setVisible(state); + APPLICATION->settings()->set("StatusBarVisible", state); +} void MainWindow::lockToolbars(bool state) { ui->mainToolBar->setMovable(!state); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index d2e154643..07a6e1eba 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -205,6 +205,8 @@ class MainWindow : public QMainWindow { void globalSettingsClosed(); + void setStatusBarVisibility(bool); + void lockToolbars(bool); #ifndef Q_OS_MAC diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 1ee3a5632..889012105 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -176,6 +176,7 @@ + @@ -257,6 +258,14 @@ It's a fluffy kitty :3
    + + + true + + + Status Bar + + true From 877eb4172a737c44505b3c784e84ab710b291e5e Mon Sep 17 00:00:00 2001 From: Tayou Date: Thu, 7 Dec 2023 17:26:47 +0100 Subject: [PATCH 143/755] add line back to group separator Signed-off-by: Tayou --- launcher/ui/instanceview/VisualGroup.cpp | 28 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/launcher/ui/instanceview/VisualGroup.cpp b/launcher/ui/instanceview/VisualGroup.cpp index aaf31941d..7bff727fe 100644 --- a/launcher/ui/instanceview/VisualGroup.cpp +++ b/launcher/ui/instanceview/VisualGroup.cpp @@ -158,13 +158,14 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti painter->setRenderHint(QPainter::Antialiasing); // sizes and offsets, to keep things consistent below - int arrowOffsetLeft = fontMetrics.height() / 2 + 7; - int textOffsetLeft = arrowOffsetLeft * 2; - int arrowSize = 6; - int centerHeight = optRect.top() + fontMetrics.height() / 2; + const int arrowOffsetLeft = fontMetrics.height() / 2 + 7; + const int textOffsetLeft = arrowOffsetLeft * 2; + const int centerHeight = optRect.top() + fontMetrics.height() / 2; + const QString& textToDraw = text.isEmpty() ? QObject::tr("Ungrouped") : text; // BEGIN: arrow { + constexpr int arrowSize = 6; QPolygon arrowPolygon; if (collapsed) { arrowPolygon << QPoint(arrowOffsetLeft - arrowSize / 2, centerHeight - arrowSize) @@ -188,9 +189,26 @@ void VisualGroup::drawHeader(QPainter* painter, const QStyleOptionViewItem& opti textRect.setHeight(fontMetrics.height()); textRect.setRight(textRect.right() - 7); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, !text.isEmpty() ? text : QObject::tr("Ungrouped")); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, textToDraw); } // END: text + + // BEGIN: horizontal line + { + penColor.setAlphaF(0.05); + pen.setColor(penColor); + painter->setPen(pen); + // startPoint is left + arrow + text + space + const int startPoint = + optRect.left() + fontMetrics.height() + fontMetrics.size(Qt::AlignLeft | Qt::AlignVCenter, textToDraw).width() + 20; + painter->setRenderHint(QPainter::Antialiasing, false); + QPolygon polygon; + // for some reason the height (yPos) doesn't look centered, so we are adding 1 to the center height + const int lineHeight = centerHeight + 1; + polygon << QPoint(startPoint, lineHeight) << QPoint(optRect.right() - 3, lineHeight); + painter->drawPolyline(polygon); + } + // END: horizontal line } int VisualGroup::totalHeight() const From d7cb139235c732abb68ffbee70c45a76c998f2db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 10 Dec 2023 00:20:06 +0000 Subject: [PATCH 144/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix-filter': 'github:numtide/nix-filter/41fd48e00c22b4ced525af521ead8792402de0ea' (2023-09-16) → 'github:numtide/nix-filter/c843418ecfd0344ecb85844b082ff5675e02c443' (2023-12-04) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/f5c27c6136db4d76c30e533c20517df6864c46ee' (2023-11-30) → 'github:nixos/nixpkgs/09dc04054ba2ff1f861357d0e7e76d021b273cd7' (2023-12-08) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index efaa524f6..afc584a24 100644 --- a/flake.lock +++ b/flake.lock @@ -91,11 +91,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1694857738, - "narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=", + "lastModified": 1701697642, + "narHash": "sha256-L217WytWZHSY8GW9Gx1A64OnNctbuDbfslaTEofXXRw=", "owner": "numtide", "repo": "nix-filter", - "rev": "41fd48e00c22b4ced525af521ead8792402de0ea", + "rev": "c843418ecfd0344ecb85844b082ff5675e02c443", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1701336116, - "narHash": "sha256-kEmpezCR/FpITc6yMbAh4WrOCiT2zg5pSjnKrq51h5Y=", + "lastModified": 1701998057, + "narHash": "sha256-gAJGhcTO9cso7XDfAScXUlPcva427AUT2q02qrmXPdo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f5c27c6136db4d76c30e533c20517df6864c46ee", + "rev": "09dc04054ba2ff1f861357d0e7e76d021b273cd7", "type": "github" }, "original": { From 3567369d44c6ba9a3b702caf2db18b126f1b4fc4 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Tue, 12 Dec 2023 13:18:12 -0800 Subject: [PATCH 145/755] #1945 resolve minimized windows pull from dock Signed-off-by: theMackabu --- .gitignore | 3 +++ launcher/Application.cpp | 8 ++++++++ maidfile.toml | 13 +++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 maidfile.toml diff --git a/.gitignore b/.gitignore index b5523f685..9f69e985c 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ flatbuild # Snap *.snap + +# maid build +.maid \ No newline at end of file diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ff5eb2a66..9e95712a7 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1513,6 +1513,13 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa auto& window = extras.window; if (window) { + // If the window is minimized on macOS, activate and bring it up + #ifdef Q_OS_MACOS + if (window->isMinimized()) { + window->setWindowState(window->windowState() & ~Qt::WindowMinimized); + } + #endif + window->raise(); window->activateWindow(); } else { @@ -1520,6 +1527,7 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa m_openWindows++; connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); } + if (!page.isEmpty()) { window->selectPage(page); } diff --git a/maidfile.toml b/maidfile.toml new file mode 100644 index 000000000..fbd76b2e7 --- /dev/null +++ b/maidfile.toml @@ -0,0 +1,13 @@ +[project] +name = "Prism" +version = "8.0" + +[tasks] +run = { script = "./build/prismlauncher.app/Contents/MacOS/prismlauncher" } +clean = { script = ["rm -rf build", "mkdir build"] } +build = { script = ["maid init -q", "bash -c 'cd build && ninja'"] } + +[tasks.init] +depends = ["clean"] +path = "build" +script = "cmake -G Ninja -DCMAKE_INSTALL_PREFIX=install -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH='/opt/homebrew/opt/qt' -DQt6_DIR='/opt/homebrew/opt/qt' -DLauncher_BUILD_PLATFORM=macOS -DENABLE_LTO=ON .." From 836f74cd4629748ce8056f9f186284fc55d0d8b4 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Tue, 12 Dec 2023 13:19:16 -0800 Subject: [PATCH 146/755] #1945 resolve issue on windows Signed-off-by: theMackabu --- launcher/Application.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 9e95712a7..6715a0709 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1513,13 +1513,17 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa auto& window = extras.window; if (window) { - // If the window is minimized on macOS, activate and bring it up + // If the window is minimized on macOS or Windows, activate and bring it up #ifdef Q_OS_MACOS if (window->isMinimized()) { window->setWindowState(window->windowState() & ~Qt::WindowMinimized); } + #elif defined(Q_OS_WIN) + if (window->isMinimized()) { + window->showNormal(); + } #endif - + window->raise(); window->activateWindow(); } else { From 201c9783d56cef2e1bba1fdbffdc377bdf294468 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Tue, 12 Dec 2023 13:31:18 -0800 Subject: [PATCH 147/755] clang-format: fix formatting Signed-off-by: theMackabu --- launcher/Application.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6715a0709..5f524219e 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1513,17 +1513,17 @@ InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString pa auto& window = extras.window; if (window) { - // If the window is minimized on macOS or Windows, activate and bring it up - #ifdef Q_OS_MACOS +// If the window is minimized on macOS or Windows, activate and bring it up +#ifdef Q_OS_MACOS if (window->isMinimized()) { window->setWindowState(window->windowState() & ~Qt::WindowMinimized); } - #elif defined(Q_OS_WIN) +#elif defined(Q_OS_WIN) if (window->isMinimized()) { window->showNormal(); } - #endif - +#endif + window->raise(); window->activateWindow(); } else { From 7c33a8e448d238960b89f1e1c2f14a18d12040eb Mon Sep 17 00:00:00 2001 From: theMackabu Date: Tue, 12 Dec 2023 13:37:24 -0800 Subject: [PATCH 148/755] remove: maidfile.toml (local build tool) Signed-off-by: theMackabu --- .gitignore | 5 +---- maidfile.toml | 13 ------------- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 maidfile.toml diff --git a/.gitignore b/.gitignore index 9f69e985c..c699a54c3 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,4 @@ result flatbuild # Snap -*.snap - -# maid build -.maid \ No newline at end of file +*.snap \ No newline at end of file diff --git a/maidfile.toml b/maidfile.toml deleted file mode 100644 index fbd76b2e7..000000000 --- a/maidfile.toml +++ /dev/null @@ -1,13 +0,0 @@ -[project] -name = "Prism" -version = "8.0" - -[tasks] -run = { script = "./build/prismlauncher.app/Contents/MacOS/prismlauncher" } -clean = { script = ["rm -rf build", "mkdir build"] } -build = { script = ["maid init -q", "bash -c 'cd build && ninja'"] } - -[tasks.init] -depends = ["clean"] -path = "build" -script = "cmake -G Ninja -DCMAKE_INSTALL_PREFIX=install -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH='/opt/homebrew/opt/qt' -DQt6_DIR='/opt/homebrew/opt/qt' -DLauncher_BUILD_PLATFORM=macOS -DENABLE_LTO=ON .." From 648aa18e6f84be7fbabc330de09beff9f170afa3 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Tue, 12 Dec 2023 14:22:23 -0800 Subject: [PATCH 149/755] add: refresh on load Signed-off-by: theMackabu --- launcher/ui/widgets/ThemeCustomizationWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index a0e682bb9..79932e20b 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -27,6 +27,7 @@ ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(pa { ui->setupUi(this); loadSettings(); + ThemeCustomizationWidget::refresh(); connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, From 2391ad200f80ef6a84ffef246ed236d66928f0e4 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 12 Dec 2023 23:21:34 +0000 Subject: [PATCH 150/755] Revert change to .gitignore Signed-off-by: TheKodeToad --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c699a54c3..b5523f685 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,4 @@ result flatbuild # Snap -*.snap \ No newline at end of file +*.snap From d10b07567797a9e05fcc9996aefb7f1ba99a13b6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 13 Dec 2023 14:36:58 +0200 Subject: [PATCH 151/755] Sort mods before export Signed-off-by: Trial97 --- launcher/ui/pages/instance/ModFolderPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 8dc32a8ff..8746513be 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -129,7 +129,7 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this, check_allow_update, actionRemoveItemMetadata, actionExportMetadata] { + [this, check_allow_update, actionRemoveItemMetadata] { ui->actionUpdateItem->setEnabled(check_allow_update()); auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); @@ -385,6 +385,7 @@ void ModFolderPage::exportModMetadata() if (selectedMods.length() == 0) selectedMods = m_model->allMods(); + std::sort(selectedMods.begin(), selectedMods.end(), [](const Mod* a, const Mod* b) { return a->name() < b->name(); }); ExportToModListDialog dlg(m_instance->name(), selectedMods, this); dlg.exec(); } From 6a619003288a06c0f664c5454dc4b69a65b4235b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 13 Dec 2023 17:48:11 +0200 Subject: [PATCH 152/755] Fixed curseforge version filter Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 341823244..f419537c7 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -177,6 +177,7 @@ void ModFilterWidget::prepareBasicFilter() auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); m_filter->versions.push_front(Version{ def }); ui->versionsCb->setCheckedItems({ def }); + ui->versionsSimpleCb->setCurrentIndex(ui->versionsSimpleCb->findText(def)); } void ModFilterWidget::onIncludeSnapshotsChanged() From 469aaf6051ab0d7bd22181d53e90bd8b7e73aedb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:16:06 +0000 Subject: [PATCH 153/755] chore(deps): update github/codeql-action action to v3 --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a77b4ae1e..d40d7eb68 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,7 +13,7 @@ jobs: submodules: 'true' - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: config-file: ./.github/codeql/codeql-config.yml queries: security-and-quality @@ -32,4 +32,4 @@ jobs: cmake --build build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 3d50bef10ccf74008642cda8c382d804b2750d3f Mon Sep 17 00:00:00 2001 From: guihkx <626206+guihkx@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:09:13 -0300 Subject: [PATCH 154/755] chore: sync nightly flatpak manifest with flathub's Signed-off-by: guihkx <626206+guihkx@users.noreply.github.com> --- flatpak/org.prismlauncher.PrismLauncher.yml | 11 ++++------- flatpak/patches/gamemode.patch | 12 ------------ flatpak/shared-modules | 2 +- 3 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 flatpak/patches/gamemode.patch diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml index 89727751e..c3ac132b1 100644 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -1,6 +1,6 @@ id: org.prismlauncher.PrismLauncher runtime: org.kde.Platform -runtime-version: "5.15-23.08" +runtime-version: 5.15-23.08 sdk: org.kde.Sdk sdk-extensions: - org.freedesktop.Sdk.Extension.openjdk17 @@ -104,18 +104,15 @@ modules: - install -Dm755 ../data/gamemoderun -t /app/bin sources: - type: archive - archive-type: tar-gzip - url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7 - sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803 + dest-filename: gamemode.tar.gz + url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.8.1 + sha256: 969cf85b5ca3944f3e315cd73a0ee9bea4f9c968cd7d485e9f4745bc1e679c4e x-checker-data: type: json url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest version-query: .tag_name url-query: .tarball_url timestamp-query: .published_at - # from https://github.com/flathub/net.gaijin.WarThunder/blob/7ea6f7a9f84b9c77150c003a7059dc03f8dcbc7f/gamemode.patch - - type: patch - path: patches/gamemode.patch cleanup: - /include - /lib/pkgconfig diff --git a/flatpak/patches/gamemode.patch b/flatpak/patches/gamemode.patch deleted file mode 100644 index 3cc0d7412..000000000 --- a/flatpak/patches/gamemode.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ruN a/common/common-pidfds.c b/common/common-pidfds.c ---- a/common/common-pidfds.c 2021-02-18 20:00:12.000000000 +0100 -+++ b/common/common-pidfds.c 2023-09-07 08:57:42.954362763 +0200 -@@ -58,6 +58,8 @@ - { - return (int)syscall(__NR_pidfd_open, pid, flags); - } -+#else -+#include - #endif - - /* pidfd functions */ diff --git a/flatpak/shared-modules b/flatpak/shared-modules index 45094ca57..55a8e460c 160000 --- a/flatpak/shared-modules +++ b/flatpak/shared-modules @@ -1 +1 @@ -Subproject commit 45094ca570be383d06df729b6972830ec63bd3df +Subproject commit 55a8e460c6343229597a13e973ba4855c27a1c4c From 400b518bc21b8a56131384fd4a56988d31bceb9f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 14 Dec 2023 00:02:15 +0200 Subject: [PATCH 155/755] Removed auto reauthenticate Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 35 ++++------------------------------- launcher/LaunchController.h | 1 - 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 3f3cf2e7e..a30f99439 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -42,7 +42,6 @@ #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/ProfileSelectDialog.h" #include "ui/dialogs/ProfileSetupDialog.h" #include "ui/dialogs/ProgressDialog.h" @@ -257,8 +256,10 @@ void LaunchController::login() continue; } case AccountState::Expired: { - if (reauthenticateCurrentAccount()) - continue; + auto errorString = tr("The account has expired and needs to be logged into manually again."); + QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok, + QMessageBox::StandardButton::Ok); + emitFailed(errorString); return; } case AccountState::Disabled: { @@ -282,34 +283,6 @@ void LaunchController::login() emitFailed(tr("Failed to launch.")); } -bool LaunchController::reauthenticateCurrentAccount() -{ - auto button = - QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), - tr("The account has expired and needs to be reauthenticated. Do you want to reauthenticate this account?"), - QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes); - if (button == QMessageBox::StandardButton::Yes) { - auto accounts = APPLICATION->accounts(); - bool isDefault = accounts->defaultAccount() == m_accountToUse; - accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(m_accountToUse->profileId()))); - if (m_accountToUse->accountType() == AccountType::MSA) { - auto newAccount = - MSALoginDialog::newAccount(m_parentWidget, tr("Please enter your Mojang account email and password to add your account.")); - accounts->addAccount(newAccount); - if (isDefault) { - accounts->setDefaultAccount(newAccount); - } - m_accountToUse = nullptr; - decideAccount(); - return true; - } - emitFailed(tr("Account expired and re-login attempt failed")); - } else { - emitFailed(tr("The account has expired and needs to be reauthenticated")); - } - return false; -} - void LaunchController::launchInstance() { Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL"); diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index 33aedb063..f1c88afb7 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -74,7 +74,6 @@ class LaunchController : public Task { void login(); void launchInstance(); void decideAccount(); - bool reauthenticateCurrentAccount(); private slots: void readyForLaunch(); From f33110ee0cf39596d893aae3b5e40de3f282d44d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 14 Dec 2023 00:19:22 +0200 Subject: [PATCH 156/755] Renamed function Signed-off-by: Trial97 --- launcher/net/NetJob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index faceba058..6740bb8c5 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -155,7 +155,7 @@ void NetJob::emitFailed(QString reason) if (response == QMessageBox::Yes) { m_try = 0; - startNext(); + executeNextSubTask(); return; } #endif From 0ccdcd23e3f3889f3e516d48bb03b27d3ddb62cc Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 14 Dec 2023 13:02:24 +0100 Subject: [PATCH 157/755] fix: open paths directly Signed-off-by: Sefa Eyeoglu --- launcher/DesktopServices.cpp | 68 +++---------------- launcher/DesktopServices.h | 17 +++-- launcher/FileSystem.cpp | 10 ++- launcher/FileSystem.h | 8 ++- launcher/ui/MainWindow.cpp | 18 ++--- launcher/ui/dialogs/IconPickerDialog.cpp | 2 +- .../pages/instance/ExternalResourcesPage.cpp | 4 +- .../ui/pages/instance/ScreenshotsPage.cpp | 4 +- launcher/ui/pages/instance/VersionPage.cpp | 4 +- launcher/ui/pages/instance/WorldListPage.cpp | 4 +- .../ui/widgets/ThemeCustomizationWidget.cpp | 6 +- 11 files changed, 52 insertions(+), 93 deletions(-) diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 17eb7c2df..851516399 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -37,6 +37,7 @@ #include #include #include +#include "FileSystem.h" /** * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. @@ -96,81 +97,30 @@ bool IndirectOpen(T callable, qint64* pid_forked = nullptr) #endif namespace DesktopServices { -bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists) +bool openPath(const QFileInfo& path, [[maybe_unused]] bool ensureExists) { - qDebug() << "Opening directory" << path; - QDir parentPath; - QDir dir(path); - if (ensureExists && !dir.exists()) { - parentPath.mkpath(dir.absolutePath()); + qDebug() << "Opening path" << path; + if (ensureExists) { + FS::ensureFolderPathExists(path); } - auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); }; -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if (!isSandbox()) { - return IndirectOpen(f); - } -#endif - return f(); + return openUrl(QUrl::fromLocalFile(QFileInfo(path).absolutePath())); } -bool openFile(const QString& path) +bool openPath(const QString& path, [[maybe_unused]] bool ensureExists) { - qDebug() << "Opening file" << path; - auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }; -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if (!isSandbox()) { - return IndirectOpen(f); - } else { - return f(); - } -#else - return f(); -#endif -} - -bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid) -{ - qDebug() << "Opening file" << path << "using" << application; -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - if (!isSandbox()) { - return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid); - } else { - return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); - } -#else - return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); -#endif + return openPath(QFileInfo(path), ensureExists); } bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid) { qDebug() << "Running" << application << "with args" << args.join(' '); -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if (!isSandbox()) { - // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid); - } else { - return QProcess::startDetached(application, args, workingDirectory, pid); - } -#else return QProcess::startDetached(application, args, workingDirectory, pid); -#endif } bool openUrl(const QUrl& url) { qDebug() << "Opening URL" << url.toString(); - auto f = [&]() { return QDesktopServices::openUrl(url); }; -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - if (!isSandbox()) { - return IndirectOpen(f); - } else { - return f(); - } -#else - return f(); -#endif + return QDesktopServices::openUrl(url); } bool isFlatpak() diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h index 151db5557..770eac984 100644 --- a/launcher/DesktopServices.h +++ b/launcher/DesktopServices.h @@ -3,31 +3,30 @@ #include #include +class QFileInfo; + /** * This wraps around QDesktopServices and adds workarounds where needed * Use this instead of QDesktopServices! */ namespace DesktopServices { /** - * Open a file in whatever application is applicable + * Open a path in whatever application is applicable. + * @param ensurePathExists Make sure the path exists */ -bool openFile(const QString& path); +bool openPath(const QFileInfo& path, bool ensurePathExists = false); /** - * Open a file in the specified application + * Open a path in whatever application is applicable. + * @param ensurePathExists Make sure the path exists */ -bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0); +bool openPath(const QString& path, bool ensurePathExists = false); /** * Run an application */ bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0); -/** - * Open a directory - */ -bool openDirectory(const QString& path, bool ensureExists = false); - /** * Open the URL, most likely in a browser. Maybe. */ diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index a30d0ae0b..f9be91a2a 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -272,15 +272,19 @@ bool ensureFilePathExists(QString filenamepath) return success; } -bool ensureFolderPathExists(QString foldernamepath) +bool ensureFolderPathExists(const QFileInfo folderPath) { - QFileInfo a(foldernamepath); QDir dir; - QString ensuredPath = a.filePath(); + QString ensuredPath = folderPath.filePath(); bool success = dir.mkpath(ensuredPath); return success; } +bool ensureFolderPathExists(const QString folderPathName) +{ + return ensureFolderPathExists(QFileInfo(folderPathName)); +} + bool copyFileAttributes(QString src, QString dst) { #ifdef Q_OS_WIN32 diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index 861cfa267..f13fb9f28 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -91,7 +91,13 @@ bool ensureFilePathExists(QString filenamepath); * Creates all the folders in a path for the specified path * last segment of the path is treated as a folder name and is created! */ -bool ensureFolderPathExists(QString filenamepath); +bool ensureFolderPathExists(const QFileInfo folderPath); + +/** + * Creates all the folders in a path for the specified path + * last segment of the path is treated as a folder name and is created! + */ +bool ensureFolderPathExists(const QString folderPathName); /** * @brief Copies a directory and it's contents from src to dest diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 42de6c2c0..9f2cc434f 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1197,43 +1197,43 @@ void MainWindow::undoTrashInstance() void MainWindow::on_actionViewLauncherRootFolder_triggered() { - DesktopServices::openDirectory("."); + DesktopServices::openPath("."); } void MainWindow::on_actionViewInstanceFolder_triggered() { QString str = APPLICATION->settings()->get("InstanceDir").toString(); - DesktopServices::openDirectory(str); + DesktopServices::openPath(str); } void MainWindow::on_actionViewCentralModsFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); + DesktopServices::openPath(APPLICATION->settings()->get("CentralModsDir").toString(), true); } void MainWindow::on_actionViewIconThemeFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path(), true); + DesktopServices::openPath(APPLICATION->themeManager()->getIconThemesFolder().path(), true); } void MainWindow::on_actionViewWidgetThemeFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true); + DesktopServices::openPath(APPLICATION->themeManager()->getApplicationThemesFolder().path(), true); } void MainWindow::on_actionViewCatPackFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path(), true); + DesktopServices::openPath(APPLICATION->themeManager()->getCatPacksFolder().path(), true); } void MainWindow::on_actionViewIconsFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true); + DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true); } void MainWindow::on_actionViewLogsFolder_triggered() { - DesktopServices::openDirectory("logs", true); + DesktopServices::openPath("logs", true); } void MainWindow::refreshInstances() @@ -1452,7 +1452,7 @@ void MainWindow::on_actionViewSelectedInstFolder_triggered() { if (m_selectedInstance) { QString str = m_selectedInstance->instanceRoot(); - DesktopServices::openDirectory(QDir(str).absolutePath()); + DesktopServices::openPath(QDir(str).absolutePath()); } } diff --git a/launcher/ui/dialogs/IconPickerDialog.cpp b/launcher/ui/dialogs/IconPickerDialog.cpp index faad3ce75..a196fd587 100644 --- a/launcher/ui/dialogs/IconPickerDialog.cpp +++ b/launcher/ui/dialogs/IconPickerDialog.cpp @@ -159,5 +159,5 @@ IconPickerDialog::~IconPickerDialog() void IconPickerDialog::openFolder() { - DesktopServices::openDirectory(APPLICATION->icons()->getDirectory(), true); + DesktopServices::openPath(APPLICATION->icons()->getDirectory(), true); } diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 48a71b809..2068fa6b1 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -290,12 +290,12 @@ void ExternalResourcesPage::disableItem() void ExternalResourcesPage::viewConfigs() { - DesktopServices::openDirectory(m_instance->instanceConfigFolder(), true); + DesktopServices::openPath(m_instance->instanceConfigFolder(), true); } void ExternalResourcesPage::viewFolder() { - DesktopServices::openDirectory(m_model->dir().absolutePath(), true); + DesktopServices::openPath(m_model->dir().absolutePath(), true); } bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous) diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index 83fb0d5ff..01061160b 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -325,7 +325,7 @@ void ScreenshotsPage::onItemActivated(QModelIndex index) return; auto info = m_model->fileInfo(index); QString fileName = info.absoluteFilePath(); - DesktopServices::openFile(info.absoluteFilePath()); + DesktopServices::openPath(info.absoluteFilePath()); } void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection& selected) @@ -352,7 +352,7 @@ void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection& selected) void ScreenshotsPage::on_actionView_Folder_triggered() { - DesktopServices::openDirectory(m_folder, true); + DesktopServices::openPath(m_folder, true); } void ScreenshotsPage::on_actionUpload_triggered() diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp index e425269c8..487433230 100644 --- a/launcher/ui/pages/instance/VersionPage.cpp +++ b/launcher/ui/pages/instance/VersionPage.cpp @@ -447,12 +447,12 @@ void VersionPage::on_actionAdd_Empty_triggered() void VersionPage::on_actionLibrariesFolder_triggered() { - DesktopServices::openDirectory(m_inst->getLocalLibraryPath(), true); + DesktopServices::openPath(m_inst->getLocalLibraryPath(), true); } void VersionPage::on_actionMinecraftFolder_triggered() { - DesktopServices::openDirectory(m_inst->gameRoot(), true); + DesktopServices::openPath(m_inst->gameRoot(), true); } void VersionPage::versionCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 587bb6ce6..692db7ad7 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -207,7 +207,7 @@ void WorldListPage::on_actionRemove_triggered() void WorldListPage::on_actionView_Folder_triggered() { - DesktopServices::openDirectory(m_worlds->dir().absolutePath(), true); + DesktopServices::openPath(m_worlds->dir().absolutePath(), true); } void WorldListPage::on_actionDatapacks_triggered() @@ -223,7 +223,7 @@ void WorldListPage::on_actionDatapacks_triggered() auto fullPath = m_worlds->data(index, WorldList::FolderRole).toString(); - DesktopServices::openDirectory(FS::PathCombine(fullPath, "datapacks"), true); + DesktopServices::openPath(FS::PathCombine(fullPath, "datapacks"), true); } void WorldListPage::on_actionReset_Icon_triggered() diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index 0de97441f..25b91857c 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -34,11 +34,11 @@ ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(pa connect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); connect(ui->iconsFolder, &QPushButton::clicked, this, - [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); }); + [] { DesktopServices::openPath(APPLICATION->themeManager()->getIconThemesFolder().path()); }); connect(ui->widgetStyleFolder, &QPushButton::clicked, this, - [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); + [] { DesktopServices::openPath(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); connect(ui->catPackFolder, &QPushButton::clicked, this, - [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); }); + [] { DesktopServices::openPath(APPLICATION->themeManager()->getCatPacksFolder().path()); }); } ThemeCustomizationWidget::~ThemeCustomizationWidget() From d0fe700b530bdc12afb945f6e2822e45bc68693e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:23:26 +0000 Subject: [PATCH 158/755] chore(deps): update actions/download-artifact action to v4 --- .github/workflows/trigger_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 28578165f..ebf5f96e6 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -32,7 +32,7 @@ jobs: submodules: "true" path: "PrismLauncher-source" - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Grab and store version run: | tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$") From 317d52b44a8b72652b01bd0b155427e0892a8d91 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:23:30 +0000 Subject: [PATCH 159/755] chore(deps): update actions/upload-artifact action to v4 --- .github/workflows/build.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24e7194ae..e24ccbf6d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -517,70 +517,70 @@ jobs: - name: Upload binary tarball (macOS) if: runner.os == 'macOS' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher.tar.gz - name: Upload binary zip (Windows) if: runner.os == 'Windows' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} path: ${{ env.INSTALL_DIR }}/** - name: Upload binary zip (Windows, portable) if: runner.os == 'Windows' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: ${{ env.INSTALL_PORTABLE_DIR }}/** - name: Upload installer (Windows) if: runner.os == 'Windows' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-Setup.exe - name: Upload binary tarball (Linux, Qt 5) if: runner.os == 'Linux' && matrix.qt_ver != 6 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher.tar.gz - name: Upload binary tarball (Linux, portable, Qt 5) if: runner.os == 'Linux' && matrix.qt_ver != 6 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-portable.tar.gz - name: Upload binary tarball (Linux, Qt 6) if: runner.os == 'Linux' && matrix.qt_ver !=5 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher.tar.gz - name: Upload binary tarball (Linux, portable, Qt 6) if: runner.os == 'Linux' && matrix.qt_ver != 5 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-portable.tar.gz - name: Upload AppImage (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage - name: Upload AppImage Zsync (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync path: PrismLauncher-Linux-x86_64.AppImage.zsync From 8cbdecc454de7ee8f3dadbd00c35b339a8581c45 Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Sat, 16 Dec 2023 12:52:59 +0100 Subject: [PATCH 160/755] Update GetModDependenciesTask.cpp Signed-off-by: lumiscosity --- launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 5457d5a9c..5aef4e585 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -183,7 +183,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType }; ResourceAPI::DependencySearchCallbacks callbacks; callbacks.on_fail = [](QString reason, int) { - qCritical() << tr("A network error occurred. Could not load project dependenies:%1").arg(reason); + qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason); }; callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, [[maybe_unused]] auto& pack) { try { @@ -285,4 +285,4 @@ QHash GetModDependenciesTask::getRequiredBy() rby[addonId.toString()] = req; } return rby; -} \ No newline at end of file +} From 56397446dbe05ea346359f401587383d4a1a84dd Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Sat, 16 Dec 2023 12:53:59 +0100 Subject: [PATCH 161/755] Update ModFolderPage.cpp Signed-off-by: lumiscosity --- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index ce1cc3a90..313fef2b6 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -242,7 +242,7 @@ void ModFolderPage::updateMods(bool includeDeps) if (m_instance != nullptr && m_instance->isRunning()) { auto response = CustomMessageBox::selectable(this, tr("Confirm Update"), - tr("If you update mods while the game is running may cause mod duplication and game crashes.\n" + tr("Updating mods while the game is running may cause mod duplication and game crashes.\n" "The old files may not be deleted as they are in use.\n" "Are you sure you want to do this?"), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) From eb8e150fa804f0b34b21d7a95e840516cd119c97 Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Sat, 16 Dec 2023 13:00:22 +0100 Subject: [PATCH 162/755] one more Signed-off-by: lumiscosity --- launcher/ui/pages/modplatform/ResourceModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 6c02150bd..8a70eb4de 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -238,7 +238,7 @@ void ResourceModel::loadEntry(QModelIndex& entry) callbacks.on_abort = [this] { if (!s_running_models.constFind(this).value()) return; - qCritical() << tr("The request was abborted for an unknown reason"); + qCritical() << tr("The request was aborted for an unknown reason"); }; if (auto job = m_api->getProjectInfo(std::move(args), std::move(callbacks)); job) From 213963257cf2b2f7a265893c3112cd71bbce6199 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 16 Dec 2023 13:03:51 +0100 Subject: [PATCH 163/755] chore: remove maybe_unused Signed-off-by: Sefa Eyeoglu --- launcher/DesktopServices.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 851516399..c9d515323 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -97,7 +97,7 @@ bool IndirectOpen(T callable, qint64* pid_forked = nullptr) #endif namespace DesktopServices { -bool openPath(const QFileInfo& path, [[maybe_unused]] bool ensureExists) +bool openPath(const QFileInfo& path, bool ensureExists) { qDebug() << "Opening path" << path; if (ensureExists) { @@ -106,7 +106,7 @@ bool openPath(const QFileInfo& path, [[maybe_unused]] bool ensureExists) return openUrl(QUrl::fromLocalFile(QFileInfo(path).absolutePath())); } -bool openPath(const QString& path, [[maybe_unused]] bool ensureExists) +bool openPath(const QString& path, bool ensureExists) { return openPath(QFileInfo(path), ensureExists); } From a8220cd296a5b9540df07bce7be7e1f6cc28ffe0 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 16 Dec 2023 13:12:12 +0100 Subject: [PATCH 164/755] chore: remove unused methods Signed-off-by: Sefa Eyeoglu --- launcher/DesktopServices.cpp | 62 ------------------------------------ launcher/DesktopServices.h | 5 --- 2 files changed, 67 deletions(-) diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index c9d515323..ecad825a5 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -39,63 +39,6 @@ #include #include "FileSystem.h" -/** - * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. - */ -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - -#include -#include -#include -#include - -template -bool IndirectOpen(T callable, qint64* pid_forked = nullptr) -{ - auto pid = fork(); - if (pid_forked) { - if (pid > 0) - *pid_forked = pid; - else - *pid_forked = 0; - } - if (pid == -1) { - qWarning() << "IndirectOpen failed to fork: " << errno; - return false; - } - // child - do the stuff - if (pid == 0) { - // unset all this garbage so it doesn't get passed to the child process - qunsetenv("LD_PRELOAD"); - qunsetenv("LD_LIBRARY_PATH"); - qunsetenv("LD_DEBUG"); - qunsetenv("QT_PLUGIN_PATH"); - qunsetenv("QT_FONTPATH"); - - // open the URL - auto status = callable(); - - // detach from the parent process group. - setsid(); - - // die. now. do not clean up anything, it would just hang forever. - _exit(status ? 0 : 1); - } else { - // parent - assume it worked. - int status; - while (waitpid(pid, &status, 0)) { - if (WIFEXITED(status)) { - return WEXITSTATUS(status) == 0; - } - if (WIFSIGNALED(status)) { - return false; - } - } - return true; - } -} -#endif - namespace DesktopServices { bool openPath(const QFileInfo& path, bool ensureExists) { @@ -141,9 +84,4 @@ bool isSnap() #endif } -bool isSandbox() -{ - return isSnap() || isFlatpak(); -} - } // namespace DesktopServices diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h index 770eac984..6c4cbdce7 100644 --- a/launcher/DesktopServices.h +++ b/launcher/DesktopServices.h @@ -41,9 +41,4 @@ bool isFlatpak(); * Determine whether the launcher is running in a Snap environment */ bool isSnap(); - -/** - * Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment - */ -bool isSandbox(); } // namespace DesktopServices From 8f29cdf1956d4ef9fa58fe8fdf22eb99a8e24aa5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 15:27:48 +0000 Subject: [PATCH 165/755] chore(deps): update korthout/backport-action action to v2.3.0 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 12850d697..dee01e084 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.2.0 + uses: korthout/backport-action@v2.3.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From 74e2e491340f19282c480beb0c89402661f0a053 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 17 Dec 2023 00:20:10 +0000 Subject: [PATCH 166/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/09dc04054ba2ff1f861357d0e7e76d021b273cd7' (2023-12-08) → 'github:nixos/nixpkgs/aa9d4729cbc99dabacb50e3994dcefb3ea0f7447' (2023-12-14) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/e5ee5c5f3844550c01d2131096c7271cec5e9b78' (2023-11-25) → 'github:cachix/pre-commit-hooks.nix/007a45d064c1c32d04e1b8a0de5ef00984c419bc' (2023-12-13) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index afc584a24..f754388af 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1701998057, - "narHash": "sha256-gAJGhcTO9cso7XDfAScXUlPcva427AUT2q02qrmXPdo=", + "lastModified": 1702539185, + "narHash": "sha256-KnIRG5NMdLIpEkZTnN5zovNYc0hhXjAgv6pfd5Z4c7U=", "owner": "nixos", "repo": "nixpkgs", - "rev": "09dc04054ba2ff1f861357d0e7e76d021b273cd7", + "rev": "aa9d4729cbc99dabacb50e3994dcefb3ea0f7447", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1700922917, - "narHash": "sha256-ej2fch/T584b5K9sk1UhmZF7W6wEfDHuoUYpFN8dtvM=", + "lastModified": 1702456155, + "narHash": "sha256-I2XhXGAecdGlqi6hPWYT83AQtMgL+aa3ulA85RAEgOk=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e5ee5c5f3844550c01d2131096c7271cec5e9b78", + "rev": "007a45d064c1c32d04e1b8a0de5ef00984c419bc", "type": "github" }, "original": { From a1e3901b3f4e4910e3fe40206879907a1f7ff03c Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 17 Dec 2023 12:39:17 +0100 Subject: [PATCH 167/755] fix: don't log katabasis by default Signed-off-by: Sefa Eyeoglu --- launcher/qtlogging.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/qtlogging.ini b/launcher/qtlogging.ini index c12d1e109..5266de59b 100644 --- a/launcher/qtlogging.ini +++ b/launcher/qtlogging.ini @@ -5,6 +5,7 @@ qt.*.debug=false # don't log credentials by default launcher.auth.credentials.debug=false +katabasis.*.debug=false # remove the debug lines, other log levels still get through launcher.task.net.download.debug=false # enable or disable whole catageries From 43306350e07a5cb5866aa1ee4e7bee8d6945cf48 Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Fri, 22 Dec 2023 15:02:41 +0100 Subject: [PATCH 168/755] Add cat opacity Signed-off-by: lumiscosity --- launcher/Application.cpp | 1 + launcher/ui/instanceview/InstanceView.cpp | 2 + launcher/ui/pages/global/LauncherPage.cpp | 6 +++ launcher/ui/pages/global/LauncherPage.ui | 50 ++++++++++++++++++++++- 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 5f524219e..98773efe1 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -667,6 +667,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // The cat m_settings->registerSetting("TheCat", false); + m_settings->registerSetting("CatOpacity", 100); m_settings->registerSetting("StatusBarVisible", true); diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index e665097f4..6f3812a62 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -458,6 +458,7 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) QPainter painter(this->viewport()); if (m_catVisible) { + painter.setOpacity(APPLICATION->settings()->get("CatOpacity").toFloat() / 100); int widWidth = this->viewport()->width(); int widHeight = this->viewport()->height(); if (m_catPixmap.width() < widWidth) @@ -468,6 +469,7 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) QRect rectOfPixmap = pixmap.rect(); rectOfPixmap.moveBottomRight(this->viewport()->rect().bottomRight()); painter.drawPixmap(rectOfPixmap.topLeft(), pixmap); + painter.setOpacity(1.0); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index d15883db5..78c44380a 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -221,6 +221,9 @@ void LauncherPage::applySettings() break; } + // Cat + s->set("CatOpacity", ui->catOpacitySpinBox->value()); + // Mods s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked()); s->set("ModDependenciesDisabled", ui->dependenciesDisableBtn->isChecked()); @@ -276,6 +279,9 @@ void LauncherPage::loadSettings() ui->sortByNameBtn->setChecked(true); } + // Cat + ui->catOpacitySpinBox->setValue(s->get("CatOpacity").toInt()); + // Mods ui->metadataDisableBtn->setChecked(s->get("ModMetadataDisabled").toBool()); ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 83897f7a7..18b52e1b8 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -186,7 +186,7 @@ - + Disable the automatic detection, installation, and updating of mod dependencies. @@ -300,6 +300,54 @@ + + + + Cat + + + + + + Set the cat's opacity. 0% is fully transparent and 100% is fully opaque. + + + Opacity + + + + + + + + + + % + + + 100 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + From 358cf5b3485cfb23cce226c02fef03523468d4eb Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Fri, 22 Dec 2023 22:01:05 +0100 Subject: [PATCH 169/755] add archived project warning Signed-off-by: lumiscosity --- launcher/modplatform/ModIndex.h | 2 ++ launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 2 ++ launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 2 ++ launcher/modplatform/modrinth/ModrinthPackManifest.h | 2 ++ launcher/ui/pages/modplatform/ResourcePage.cpp | 4 ++++ launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 4 ++++ 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 72294c399..eff7e7f9f 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -122,6 +122,8 @@ struct ExtraPackData { QString wikiUrl; QString discordUrl; + QString status; + QString body; }; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 7d0893261..c1c30ab5f 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -104,6 +104,8 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob pack.extraData.donate.append(donate); } + pack.extraData.status = Json::ensureString(obj, "status"); + pack.extraData.body = Json::ensureString(obj, "body").remove("
    "); pack.extraDataLoaded = true; diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index d86f1c0e5..014d990f0 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -94,6 +94,8 @@ void loadIndexedInfo(Modpack& pack, QJsonObject& obj) pack.extra.donate.append(donate); } + + pack.extra.status = Json::ensureString(obj, "status"); pack.extraInfoLoaded = true; } diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 6fc55a043..1ffd31d83 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -77,6 +77,8 @@ struct ModpackExtra { QString discordUrl; QList donate; + + QString status; }; struct ModpackVersion { diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 44a91003d..42139a8a5 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -200,6 +200,10 @@ void ResourcePage::updateUi() } if (current_pack->extraDataLoaded) { + if (current_pack->extraData.status == "archived") { + text += "

    " + tr("This project has been archived. It will not receive any further updates unless the author decides to unarchive the project."); + } + if (!current_pack->extraData.donate.isEmpty()) { text += "

    " + tr("Donate information: "); auto donateToStr = [](ModPlatform::DonationData& donate) -> QString { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 227ed646b..bac294b60 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -142,7 +142,7 @@ void ModpackListModel::performPaginatedSearch() callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; callbacks.on_abort = [this] { qCritical() << "Search task aborted by an unknown reason!"; - searchRequestFailed("Abborted"); + searchRequestFailed("Aborted"); }; static const ModrinthAPI api; if (auto job = api.getProjectInfo({ projectId }, std::move(callbacks)); job) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index b4045a046..79ebfefd6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -267,6 +267,10 @@ void ModrinthPage::updateUI() text += "
    " + tr(" by ") + QString("%2").arg(std::get<1>(current.author).toString(), std::get<0>(current.author)); if (current.extraInfoLoaded) { + if (current.extra.status == "archived") { + text += "

    " + tr("This project has been archived. It will not receive any further updates unless the author decides to unarchive the project."); + } + if (!current.extra.donate.isEmpty()) { text += "

    " + tr("Donate information: "); auto donateToStr = [](Modrinth::DonationData& donate) -> QString { From 13d29ac6f466776f13b01f90ba805af0490a41ca Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 22 Dec 2023 23:35:48 +0200 Subject: [PATCH 170/755] Updated the size sort code Signed-off-by: Trial97 --- launcher/minecraft/mod/Resource.cpp | 13 +++++++------ launcher/minecraft/mod/Resource.h | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index d8727db0a..2fe7e87d4 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "FileSystem.h" #include "StringUtils.h" @@ -20,7 +21,7 @@ void Resource::setFile(QFileInfo file_info) parseFile(); } -QString calculateFileSize(const QFileInfo& file) +std::tuple calculateFileSize(const QFileInfo& file) { if (file.isDir()) { auto dir = QDir(file.absoluteFilePath()); @@ -29,9 +30,9 @@ QString calculateFileSize(const QFileInfo& file) auto str = QObject::tr("item"); if (count != 1) str = QObject::tr("items"); - return QString("%1 %2").arg(QString::number(count), str); + return { QString("%1 %2").arg(QString::number(count), str), -count }; } - return StringUtils::humanReadableFileSize(file.size(), true); + return { StringUtils::humanReadableFileSize(file.size(), true), file.size() }; } void Resource::parseFile() @@ -42,7 +43,7 @@ void Resource::parseFile() m_internal_id = file_name; - m_size_str = calculateFileSize(m_file_info); + std::tie(m_size_str, m_size_info) = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -107,9 +108,9 @@ std::pair Resource::compare(const Resource& other, SortType type) con return { -1, type == SortType::DATE }; break; case SortType::SIZE: { - if (fileinfo().size() > other.fileinfo().size()) + if (sizeInfo() > other.sizeInfo()) return { 1, type == SortType::SIZE }; - if (fileinfo().size() < other.fileinfo().size()) + if (sizeInfo() < other.sizeInfo()) return { -1, type == SortType::SIZE }; break; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 029afbd5b..74f4d006e 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -46,6 +46,7 @@ class Resource : public QObject { [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } [[nodiscard]] QString sizeStr() const { return m_size_str; } + [[nodiscard]] qint64 sizeInfo() const { return m_size_info; } [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } @@ -119,4 +120,5 @@ class Resource : public QObject { bool m_is_resolved = false; int m_resolution_ticket = 0; QString m_size_str; + qint64 m_size_info; }; From 526e580fbb77b0758ba4141969a4380ac7de4472 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 23 Dec 2023 00:33:38 +0200 Subject: [PATCH 171/755] Force clear the resource selection Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ModPage.cpp | 1 + launcher/ui/pages/modplatform/ResourcePackPage.cpp | 1 + launcher/ui/pages/modplatform/ShaderPackPage.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index d6cc1fdcc..851c1c9e5 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -90,6 +90,7 @@ void ModPage::filterMods() void ModPage::triggerSearch() { m_filter = m_filter_widget->getFilter(); + m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect); m_ui->packView->clearSelection(); m_ui->packDescription->clear(); m_ui->versionSelectionBox->clear(); diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.cpp b/launcher/ui/pages/modplatform/ResourcePackPage.cpp index 2a4d02815..fc2dc15f3 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackPage.cpp @@ -23,6 +23,7 @@ ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialo void ResourcePackResourcePage::triggerSearch() { + m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect); m_ui->packView->clearSelection(); m_ui->packDescription->clear(); m_ui->versionSelectionBox->clear(); diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp index 586dffc55..8be068312 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -24,6 +24,7 @@ ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, void ShaderPackResourcePage::triggerSearch() { + m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect); m_ui->packView->clearSelection(); m_ui->packDescription->clear(); m_ui->versionSelectionBox->clear(); From 9a33bdcf501655dbef2981094ee05662f34cd671 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 23 Dec 2023 01:02:04 +0200 Subject: [PATCH 172/755] Fixed openURL cast for resource Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ResourcePage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 44a91003d..8f660a8b0 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -404,9 +404,9 @@ void ResourcePage::openUrl(const QUrl& url) auto jump = [url, slug, model, view] { for (int row = 0; row < model->rowCount({}); row++) { const QModelIndex index = model->index(row); - const auto pack = model->data(index, Qt::UserRole).value(); + const auto pack = model->data(index, Qt::UserRole).value(); - if (pack.slug == slug) { + if (pack->slug == slug) { view->setCurrentIndex(index); return; } From 1bf386603dece42df0815495ae6b43b0e2d665c8 Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Sat, 23 Dec 2023 11:27:19 +0100 Subject: [PATCH 173/755] Strip newlines in mod descriptions Mirrors Modrinth page behaviour. Signed-off-by: lumiscosity --- launcher/ui/pages/modplatform/ResourceModel.cpp | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 8a70eb4de..aafec7f57 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -77,7 +77,7 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant case UserDataTypes::TITLE: return pack->name; case UserDataTypes::DESCRIPTION: - return pack->description; + return pack->description.simplified(); case UserDataTypes::SELECTED: return pack->isAnyVersionSelected(); case UserDataTypes::INSTALLED: diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index bac294b60..6ee3e0c67 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -105,7 +105,7 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian case UserDataTypes::TITLE: return pack.name; case UserDataTypes::DESCRIPTION: - return pack.description; + return pack.description.simplified(); case UserDataTypes::SELECTED: return false; case UserDataTypes::INSTALLED: From 1e48bf838db3f72fd391ecf9c91aa8e693daf213 Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Sat, 23 Dec 2023 12:05:07 +0100 Subject: [PATCH 174/755] move simplify call to drawing Signed-off-by: lumiscosity --- launcher/ui/pages/modplatform/ResourceModel.cpp | 2 +- launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp | 2 +- launcher/ui/widgets/ProjectItem.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index aafec7f57..8a70eb4de 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -77,7 +77,7 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant case UserDataTypes::TITLE: return pack->name; case UserDataTypes::DESCRIPTION: - return pack->description.simplified(); + return pack->description; case UserDataTypes::SELECTED: return pack->isAnyVersionSelected(); case UserDataTypes::INSTALLED: diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 6ee3e0c67..bac294b60 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -105,7 +105,7 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian case UserDataTypes::TITLE: return pack.name; case UserDataTypes::DESCRIPTION: - return pack.description.simplified(); + return pack.description; case UserDataTypes::SELECTED: return false; case UserDataTypes::INSTALLED: diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index 60b92b28b..6946df41f 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -88,7 +88,7 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o } { // Description painting - auto description = index.data(UserDataTypes::DESCRIPTION).toString(); + auto description = index.data(UserDataTypes::DESCRIPTION).toString().simplified(); QTextLayout text_layout(description, opt.font); From c29af83dc328612d340c54e3ec037b1264d66d51 Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Sat, 23 Dec 2023 14:16:29 +0100 Subject: [PATCH 175/755] fix styling Signed-off-by: lumiscosity --- launcher/modplatform/modrinth/ModrinthPackManifest.cpp | 2 +- launcher/ui/pages/modplatform/ResourcePage.cpp | 3 ++- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 014d990f0..7846e966d 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -94,7 +94,7 @@ void loadIndexedInfo(Modpack& pack, QJsonObject& obj) pack.extra.donate.append(donate); } - + pack.extra.status = Json::ensureString(obj, "status"); pack.extraInfoLoaded = true; diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 42139a8a5..d8e8a652c 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -201,7 +201,8 @@ void ResourcePage::updateUi() if (current_pack->extraDataLoaded) { if (current_pack->extraData.status == "archived") { - text += "

    " + tr("This project has been archived. It will not receive any further updates unless the author decides to unarchive the project."); + text += "

    " + tr("This project has been archived. It will not receive any further updates unless the author decides " + "to unarchive the project."); } if (!current_pack->extraData.donate.isEmpty()) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 79ebfefd6..fffa21940 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -268,7 +268,8 @@ void ModrinthPage::updateUI() if (current.extraInfoLoaded) { if (current.extra.status == "archived") { - text += "

    " + tr("This project has been archived. It will not receive any further updates unless the author decides to unarchive the project."); + text += "

    " + tr("This project has been archived. It will not receive any further updates unless the author decides " + "to unarchive the project."); } if (!current.extra.donate.isEmpty()) { From ea38f8168a066cb8922c8c3b32914f1eb2584c8a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 24 Dec 2023 00:19:31 +0000 Subject: [PATCH 176/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/aa9d4729cbc99dabacb50e3994dcefb3ea0f7447' (2023-12-14) → 'github:nixos/nixpkgs/d6863cbcbbb80e71cecfc03356db1cda38919523' (2023-12-21) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index f754388af..70affded6 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1702539185, - "narHash": "sha256-KnIRG5NMdLIpEkZTnN5zovNYc0hhXjAgv6pfd5Z4c7U=", + "lastModified": 1703134684, + "narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=", "owner": "nixos", "repo": "nixpkgs", - "rev": "aa9d4729cbc99dabacb50e3994dcefb3ea0f7447", + "rev": "d6863cbcbbb80e71cecfc03356db1cda38919523", "type": "github" }, "original": { From eca5d88576900d32edea2000258f37d700af5f06 Mon Sep 17 00:00:00 2001 From: tildejustin Date: Sun, 24 Dec 2023 22:20:06 -0500 Subject: [PATCH 177/755] remove legacy processArguments code Signed-off-by: tildejustin --- launcher/minecraft/MojangVersionFormat.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/launcher/minecraft/MojangVersionFormat.cpp b/launcher/minecraft/MojangVersionFormat.cpp index 6ee10ec15..bb782e47f 100644 --- a/launcher/minecraft/MojangVersionFormat.cpp +++ b/launcher/minecraft/MojangVersionFormat.cpp @@ -157,20 +157,6 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "mainClass", out->mainClass); Bits::readString(in, "minecraftArguments", out->minecraftArguments); - if (out->minecraftArguments.isEmpty()) { - QString processArguments; - Bits::readString(in, "processArguments", processArguments); - QString toCompare = processArguments.toLower(); - if (toCompare == "legacy") { - out->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } else if (toCompare == "username_session") { - out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } else if (toCompare == "username_session_version") { - out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } else if (!toCompare.isEmpty()) { - out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments)); - } - } Bits::readString(in, "type", out->type); Bits::readString(in, "assets", out->assets); From ad01102badd42b9418828fbd73a197f78f9a33b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 31 Dec 2023 00:20:06 +0000 Subject: [PATCH 178/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/d6863cbcbbb80e71cecfc03356db1cda38919523' (2023-12-21) → 'github:nixos/nixpkgs/e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870' (2023-12-25) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/007a45d064c1c32d04e1b8a0de5ef00984c419bc' (2023-12-13) → 'github:cachix/pre-commit-hooks.nix/9d3d7e18c6bc4473d7520200d4ddab12f8402d38' (2023-12-30) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 70affded6..774f394cd 100644 --- a/flake.lock +++ b/flake.lock @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1703134684, - "narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=", + "lastModified": 1703499205, + "narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d6863cbcbbb80e71cecfc03356db1cda38919523", + "rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870", "type": "github" }, "original": { @@ -153,11 +153,11 @@ ] }, "locked": { - "lastModified": 1702456155, - "narHash": "sha256-I2XhXGAecdGlqi6hPWYT83AQtMgL+aa3ulA85RAEgOk=", + "lastModified": 1703939133, + "narHash": "sha256-Gxe+mfOT6bL7wLC/tuT2F+V+Sb44jNr8YsJ3cyIl4Mo=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "007a45d064c1c32d04e1b8a0de5ef00984c419bc", + "rev": "9d3d7e18c6bc4473d7520200d4ddab12f8402d38", "type": "github" }, "original": { From 73f956c897e50f79353291e46af0daadff8640b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 07:30:12 +0000 Subject: [PATCH 179/755] chore(deps): update hendrikmuhs/ccache-action action to v1.2.11 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e24ccbf6d..1159aad6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -145,7 +145,7 @@ jobs: - name: Setup ccache if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' - uses: hendrikmuhs/ccache-action@v1.2.10 + uses: hendrikmuhs/ccache-action@v1.2.11 with: key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} From 1130fe851012b3d7deff207db495b455501e17fc Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 2 Jan 2024 17:57:24 +0000 Subject: [PATCH 180/755] Happy new year :D Signed-off-by: TheKodeToad --- CMakeLists.txt | 2 +- COPYING.md | 4 ++-- launcher/Application.cpp | 3 +-- launcher/ui/dialogs/AboutDialog.cpp | 3 +-- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++-- program_info/CMakeLists.txt | 4 ++-- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index faeb1c44e..e42186cb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -377,7 +377,7 @@ if(UNIX AND APPLE) set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) - set(MACOSX_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}") + set(MACOSX_BUNDLE_COPYRIGHT "${Launcher_Copyright_Mac}") set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") diff --git a/COPYING.md b/COPYING.md index 0221d1b08..f14e2958e 100644 --- a/COPYING.md +++ b/COPYING.md @@ -1,7 +1,7 @@ ## Prism Launcher Prism Launcher - Minecraft Launcher - Copyright (C) 2022-2023 Prism Launcher Contributors + Copyright (C) 2022-2024 Prism Launcher Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -436,7 +436,7 @@ Copyright (C) 2007 Johann Ollivier Lapeyre Copyright (C) 2007 Kenneth Wimer Copyright (C) 2007 Riccardo Iaconelli - + and others This library is free software; you can redistribute it and/or diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 5f524219e..75a0ce702 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -494,8 +494,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } { - qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " - << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Platform : " << BuildConfig.BUILD_PLATFORM; qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index cee01e821..17b79ecaa 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -174,8 +174,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia QString urlText("

    %1

    "); ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT)); - QString copyText("© 2022-2023 %1"); - ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT)); + ui->copyLabel->setText(BuildConfig.LAUNCHER_COPYRIGHT); connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 1c5aaf1e8..5fe22bdd0 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -436,8 +436,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } { // log debug program info - qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" - << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + " Updater, " + + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; diff --git a/program_info/CMakeLists.txt b/program_info/CMakeLists.txt index 8c6827119..91b213274 100644 --- a/program_info/CMakeLists.txt +++ b/program_info/CMakeLists.txt @@ -14,8 +14,8 @@ set(Launcher_DisplayName "Prism Launcher") set(Launcher_Name "${Launcher_CommonName}" PARENT_SCOPE) set(Launcher_DisplayName "${Launcher_DisplayName}" PARENT_SCOPE) -set(Launcher_Copyright "Prism Launcher Contributors\\n© 2021-2022 PolyMC Contributors \\n© 2012-2021 MultiMC Contributors") -set(Launcher_Copyright_Mac "Prism Launcher Contributors, © 2021-2022 PolyMC Contributors and © 2012-2021 MultiMC Contributors" PARENT_SCOPE) +set(Launcher_Copyright "© 2022-2024 Prism Launcher Contributors\\n© 2021-2022 PolyMC Contributors\\n© 2012-2021 MultiMC Contributors") +set(Launcher_Copyright_Mac "© 2022-2024 Prism Launcher Contributors, © 2021-2022 PolyMC Contributors and © 2012-2021 MultiMC Contributors" PARENT_SCOPE) set(Launcher_Copyright "${Launcher_Copyright}" PARENT_SCOPE) set(Launcher_Domain "prismlauncher.org" PARENT_SCOPE) set(Launcher_UserAgent "${Launcher_CommonName}/${Launcher_VERSION_NAME}" PARENT_SCOPE) From 05e453309629d2c4cc1cf4b1322669cb8aed21ff Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 24 Dec 2023 16:39:56 +0000 Subject: [PATCH 181/755] Add online mode fix for legacy versions; minor refactors in legacy Signed-off-by: TheKodeToad --- launcher/ui/pages/global/MinecraftPage.ui | 2 +- .../ui/pages/instance/InstanceSettingsPage.ui | 2 +- libraries/launcher/CMakeLists.txt | 3 +- .../legacy/fix/online/Handler.java | 7 +- .../legacy/fix/online/OnlineFixes.java | 2 + .../legacy/fix/online/OnlineModeFix.java | 64 +++++++++++++++++++ .../legacy/fix/online/SkinFix.java | 8 +-- ...ction.java => ByteArrayUrlConnection.java} | 17 ++--- .../legacy/utils/url/UrlUtils.java | 2 +- .../org/prismlauncher/EntryPoint.java | 46 ++----------- .../org/prismlauncher/SystemProperties.java | 38 +++++++++++ .../prismlauncher/utils/ReflectionUtils.java | 6 -- 12 files changed, 129 insertions(+), 68 deletions(-) create mode 100644 libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java rename libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/{CustomUrlConnection.java => ByteArrayUrlConnection.java} (86%) create mode 100644 libraries/launcher/org/prismlauncher/SystemProperties.java diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 2a3c0d96d..3008099e3 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -206,7 +206,7 @@ - <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html> + <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>Current fixes include: skin and online mode support.</p></body></html> Enable online fixes (experimental) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 3b06ac865..632569e0c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -605,7 +605,7 @@ - <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>This currently allows modern skins to be used.</p></body></html> + <html><head/><body><p>Emulates usages of old online services which are no longer operating.</p><p>Current fixes include: skin and online mode support.</p></body></html> Enable online fixes (experimental) diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index 7bf160760..4cd1ba58b 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -25,13 +25,14 @@ set(LEGACY_SRC legacy/org/prismlauncher/legacy/LegacyLauncher.java legacy/org/prismlauncher/legacy/fix/online/Handler.java legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java + legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java legacy/org/prismlauncher/legacy/fix/online/SkinFix.java legacy/org/prismlauncher/legacy/utils/Base64.java legacy/org/prismlauncher/legacy/utils/api/MojangApi.java legacy/org/prismlauncher/legacy/utils/api/Texture.java legacy/org/prismlauncher/legacy/utils/json/JsonParseException.java legacy/org/prismlauncher/legacy/utils/json/JsonParser.java - legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java + legacy/org/prismlauncher/legacy/utils/url/ByteArrayUrlConnection.java legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java legacy/net/minecraft/Launcher.java legacy/org/prismlauncher/legacy/LegacyProxy.java diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java index f85a8bc79..5ef3d7ac2 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/Handler.java @@ -53,11 +53,16 @@ final class Handler extends URLStreamHandler { protected URLConnection openConnection(URL address, Proxy proxy) throws IOException { URLConnection result; - // try skin fix + // try various fixes... result = SkinFix.openConnection(address, proxy); if (result != null) return result; + result = OnlineModeFix.openConnection(address, proxy); + if (result != null) + return result; + + // ...then give up and make the request directly return UrlUtils.openConnection(address, proxy); } } diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java index 88facff69..9ba57ff71 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineFixes.java @@ -46,6 +46,8 @@ import java.net.URLStreamHandlerFactory; /** * Fixes skins by redirecting to other URLs. + * Thanks to MineOnline for the implementation from which this was inspired! + * See https://github.com/ahnewark/MineOnline/tree/main/src/main/java/gg/codie/mineonline/protocol. * * @see {@link Handler} * @see {@link UrlUtils} diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java new file mode 100644 index 000000000..6476dd882 --- /dev/null +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Linking this library statically or dynamically with other modules is + * making a combined work based on this library. Thus, the terms and + * conditions of the GNU General Public License cover the whole + * combination. + * + * As a special exception, the copyright holders of this library give + * you permission to link this library with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also meet, + * for each linked independent module, the terms and conditions of the + * license of that module. An independent module is a module which is + * not derived from or based on this library. If you modify this + * library, you may extend this exception to your version of the + * library, but you are not obliged to do so. If you do not wish to do + * so, delete this exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.prismlauncher.legacy.fix.online; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; + +public final class OnlineModeFix { + public static URLConnection openConnection(URL address, Proxy proxy) throws IOException { + // we start with "http://www.minecraft.net/game/joinserver.jsp?user=..." + if (!(address.getHost().equals("www.minecraft.net") && address.getPath().equals("/game/joinserver.jsp"))) + return null; + + // change it to "https://session.minecraft.net/game/joinserver.jsp?user=..." + // this seems to be the modern version of the same endpoint... + // maybe Mojang planned to patch old versions of the game to use it + // if it ever disappears this should be changed to use sessionserver.mojang.com/session/minecraft/join + // which of course has a different usage requiring JSON serialisation... + URL url; + try { + url = new URL("https", "session.minecraft.net", address.getPort(), address.getFile()); + } catch (MalformedURLException e) { + throw new AssertionError("url should be valid", e); + } + + return url.openConnection(); + } +} diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java index e734bdbc7..d5b185450 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/SkinFix.java @@ -54,7 +54,7 @@ package org.prismlauncher.legacy.fix.online; import org.prismlauncher.legacy.utils.api.MojangApi; import org.prismlauncher.legacy.utils.api.Texture; -import org.prismlauncher.legacy.utils.url.CustomUrlConnection; +import org.prismlauncher.legacy.utils.url.ByteArrayUrlConnection; import org.prismlauncher.legacy.utils.url.UrlUtils; import java.awt.AlphaComposite; @@ -97,9 +97,9 @@ final class SkinFix { URLConnection connection = UrlUtils.openConnection(texture.getUrl(), proxy); try (InputStream in = connection.getInputStream()) { - // thank you craftycodie! + // thank you ahnewark! // this is heavily based on - // https://github.com/craftycodie/MineOnline/blob/4f4f86f9d051e0a6fd7ff0b95b2a05f7437683d7/src/main/java/gg/codie/mineonline/gui/textures/TextureHelper.java#L17 + // https://github.com/ahnewark/MineOnline/blob/4f4f86f9d051e0a6fd7ff0b95b2a05f7437683d7/src/main/java/gg/codie/mineonline/gui/textures/TextureHelper.java#L17 BufferedImage image = ImageIO.read(in); Graphics2D graphics = image.createGraphics(); graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); @@ -131,7 +131,7 @@ final class SkinFix { image = image.getSubimage(0, 0, 64, 32); ImageIO.write(image, "png", out); - return new CustomUrlConnection(out.toByteArray()); + return new ByteArrayUrlConnection(out.toByteArray()); } } diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/ByteArrayUrlConnection.java similarity index 86% rename from libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java rename to libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/ByteArrayUrlConnection.java index 71b0e68f2..bc9cf2cc2 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/CustomUrlConnection.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/ByteArrayUrlConnection.java @@ -40,16 +40,12 @@ import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; -public final class CustomUrlConnection extends HttpURLConnection { +public final class ByteArrayUrlConnection extends HttpURLConnection { private final InputStream in; - public CustomUrlConnection(byte[] data) { - this(new ByteArrayInputStream(data)); - } - - public CustomUrlConnection(InputStream in) { + public ByteArrayUrlConnection(byte[] data) { super(null); - this.in = in; + this.in = new ByteArrayInputStream(data); } @Override @@ -58,12 +54,7 @@ public final class CustomUrlConnection extends HttpURLConnection { } @Override - public void disconnect() { - try { - in.close(); - } catch (IOException e) { - } - } + public void disconnect() {} @Override public InputStream getInputStream() throws IOException { diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java index b0072485e..ae91b683c 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/utils/url/UrlUtils.java @@ -101,7 +101,7 @@ public final class UrlUtils { } catch (IOException | Error | RuntimeException e) { throw e; // rethrow if possible } catch (Throwable e) { - throw new AssertionError(e); // oh dear! this isn't meant to happen + throw new AssertionError("openConnection should not throw", e); // oh dear! this isn't meant to happen } } } diff --git a/libraries/launcher/org/prismlauncher/EntryPoint.java b/libraries/launcher/org/prismlauncher/EntryPoint.java index 699adfe30..8b9046f0c 100644 --- a/libraries/launcher/org/prismlauncher/EntryPoint.java +++ b/libraries/launcher/org/prismlauncher/EntryPoint.java @@ -81,10 +81,9 @@ public final class EntryPoint { PreLaunchAction action = PreLaunchAction.PROCEED; try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) { - String line; - while (action == PreLaunchAction.PROCEED) { - if ((line = reader.readLine()) != null) + String line = reader.readLine(); + if (line != null) action = parseLine(line, params); else action = PreLaunchAction.ABORT; @@ -105,7 +104,7 @@ public final class EntryPoint { return ExitCode.ABORT; } - setProperties(params); + SystemProperties.apply(params); String launcherType = params.getString("launcher"); @@ -141,43 +140,10 @@ public final class EntryPoint { } } - private static void setProperties(Parameters params) { - String launcherBrand = params.getString("launcherBrand", null); - String launcherVersion = params.getString("launcherVersion", null); - String name = params.getString("instanceName", null); - String iconId = params.getString("instanceIconKey", null); - String iconPath = params.getString("instanceIconPath", null); - String windowTitle = params.getString("windowTitle", null); - String windowDimensions = params.getString("windowParams", null); - - if (launcherBrand != null) - System.setProperty("minecraft.launcher.brand", launcherBrand); - if (launcherVersion != null) - System.setProperty("minecraft.launcher.version", launcherVersion); - - // set useful properties for mods - if (name != null) - System.setProperty("org.prismlauncher.instance.name", name); - if (iconId != null) - System.setProperty("org.prismlauncher.instance.icon.id", iconId); - if (iconPath != null) - System.setProperty("org.prismlauncher.instance.icon.path", iconPath); - if (windowTitle != null) - System.setProperty("org.prismlauncher.window.title", windowTitle); - if (windowDimensions != null) - System.setProperty("org.prismlauncher.window.dimensions", windowDimensions); - - // set multimc properties for compatibility - if (name != null) - System.setProperty("multimc.instance.title", name); - if (iconId != null) - System.setProperty("multimc.instance.icon", iconId); - } - private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException { switch (input) { case "": - break; + return PreLaunchAction.PROCEED; case "launch": return PreLaunchAction.LAUNCH; @@ -192,9 +158,9 @@ public final class EntryPoint { throw new ParseException(input, "[key] [value]"); params.add(pair[0], pair[1]); - } - return PreLaunchAction.PROCEED; + return PreLaunchAction.PROCEED; + } } private enum PreLaunchAction { PROCEED, LAUNCH, ABORT } diff --git a/libraries/launcher/org/prismlauncher/SystemProperties.java b/libraries/launcher/org/prismlauncher/SystemProperties.java new file mode 100644 index 000000000..5120e930d --- /dev/null +++ b/libraries/launcher/org/prismlauncher/SystemProperties.java @@ -0,0 +1,38 @@ +package org.prismlauncher; + +import org.prismlauncher.utils.Parameters; + +public final class SystemProperties { + public static void apply(Parameters params) { + String launcherBrand = params.getString("launcherBrand", null); + String launcherVersion = params.getString("launcherVersion", null); + String name = params.getString("instanceName", null); + String iconId = params.getString("instanceIconKey", null); + String iconPath = params.getString("instanceIconPath", null); + String windowTitle = params.getString("windowTitle", null); + String windowDimensions = params.getString("windowParams", null); + + if (launcherBrand != null) + System.setProperty("minecraft.launcher.brand", launcherBrand); + if (launcherVersion != null) + System.setProperty("minecraft.launcher.version", launcherVersion); + + // set useful properties for mods + if (name != null) + System.setProperty("org.prismlauncher.instance.name", name); + if (iconId != null) + System.setProperty("org.prismlauncher.instance.icon.id", iconId); + if (iconPath != null) + System.setProperty("org.prismlauncher.instance.icon.path", iconPath); + if (windowTitle != null) + System.setProperty("org.prismlauncher.window.title", windowTitle); + if (windowDimensions != null) + System.setProperty("org.prismlauncher.window.dimensions", windowDimensions); + + // set multimc properties for compatibility + if (name != null) + System.setProperty("multimc.instance.title", name); + if (iconId != null) + System.setProperty("multimc.instance.icon", iconId); + } +} diff --git a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java index 3b2bfd9bb..9d03a90a6 100644 --- a/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java +++ b/libraries/launcher/org/prismlauncher/utils/ReflectionUtils.java @@ -54,15 +54,9 @@ package org.prismlauncher.utils; -import org.prismlauncher.utils.logging.Log; - -import java.applet.Applet; -import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; public final class ReflectionUtils { private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); From 345cdf5c9d6417b5c2ce15e8967de3886d1113f1 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 2 Jan 2024 19:10:41 +0000 Subject: [PATCH 182/755] Pass proxy Signed-off-by: TheKodeToad --- .../org/prismlauncher/legacy/fix/online/OnlineModeFix.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java index 6476dd882..5ed33b85c 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java @@ -41,6 +41,8 @@ import java.net.Proxy; import java.net.URL; import java.net.URLConnection; +import org.prismlauncher.legacy.utils.url.UrlUtils; + public final class OnlineModeFix { public static URLConnection openConnection(URL address, Proxy proxy) throws IOException { // we start with "http://www.minecraft.net/game/joinserver.jsp?user=..." @@ -59,6 +61,6 @@ public final class OnlineModeFix { throw new AssertionError("url should be valid", e); } - return url.openConnection(); + return UrlUtils.openConnection(url, proxy); } } From e5b608447a1c4860343aed15ef1e1bd5940ba17d Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 3 Jan 2024 18:14:47 +0100 Subject: [PATCH 183/755] chore: improve param name Signed-off-by: Sefa Eyeoglu --- launcher/DesktopServices.cpp | 8 ++++---- launcher/DesktopServices.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index ecad825a5..7c070bd31 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -40,18 +40,18 @@ #include "FileSystem.h" namespace DesktopServices { -bool openPath(const QFileInfo& path, bool ensureExists) +bool openPath(const QFileInfo& path, bool ensureFolderPathExists) { qDebug() << "Opening path" << path; - if (ensureExists) { + if (ensureFolderPathExists) { FS::ensureFolderPathExists(path); } return openUrl(QUrl::fromLocalFile(QFileInfo(path).absolutePath())); } -bool openPath(const QString& path, bool ensureExists) +bool openPath(const QString& path, bool ensureFolderPathExists) { - return openPath(QFileInfo(path), ensureExists); + return openPath(QFileInfo(path), ensureFolderPathExists); } bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid) diff --git a/launcher/DesktopServices.h b/launcher/DesktopServices.h index 6c4cbdce7..6c6208e82 100644 --- a/launcher/DesktopServices.h +++ b/launcher/DesktopServices.h @@ -12,15 +12,15 @@ class QFileInfo; namespace DesktopServices { /** * Open a path in whatever application is applicable. - * @param ensurePathExists Make sure the path exists + * @param ensureFolderPathExists Make sure the path exists */ -bool openPath(const QFileInfo& path, bool ensurePathExists = false); +bool openPath(const QFileInfo& path, bool ensureFolderPathExists = false); /** * Open a path in whatever application is applicable. - * @param ensurePathExists Make sure the path exists + * @param ensureFolderPathExists Make sure the path exists */ -bool openPath(const QString& path, bool ensurePathExists = false); +bool openPath(const QString& path, bool ensureFolderPathExists = false); /** * Run an application From 786e03571b529c610ada33d87581e65cefc4cae3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 3 Jan 2024 22:50:55 +0200 Subject: [PATCH 184/755] Format java code Signed-off-by: Trial97 --- .../org/prismlauncher/legacy/fix/online/OnlineModeFix.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java index 5ed33b85c..1bab76d53 100644 --- a/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java +++ b/libraries/launcher/legacy/org/prismlauncher/legacy/fix/online/OnlineModeFix.java @@ -35,14 +35,14 @@ package org.prismlauncher.legacy.fix.online; +import org.prismlauncher.legacy.utils.url.UrlUtils; + import java.io.IOException; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; -import org.prismlauncher.legacy.utils.url.UrlUtils; - public final class OnlineModeFix { public static URLConnection openConnection(URL address, Proxy proxy) throws IOException { // we start with "http://www.minecraft.net/game/joinserver.jsp?user=..." From 219e2862f9d67d6a81ef634846237e1eec177d69 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 5 Jan 2024 23:02:42 +0200 Subject: [PATCH 185/755] Fixed openAL placeholder Signed-off-by: Trial97 --- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 74785f97e..d4fd0ec5b 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -346,7 +346,7 @@ void InstanceSettingsPage::loadSettings() #ifdef Q_OS_LINUX ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath); #else - ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME)); + ui->lineEditOpenALPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME)); #endif // Performance From 2cb07d3e380be6caaf922648aa75b822915818f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 7 Jan 2024 00:20:13 +0000 Subject: [PATCH 186/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5' (2023-12-01) → 'github:hercules-ci/flake-parts/88a2cd8166694ba0b6cb374700799cec53aef527' (2024-01-01) • Updated input 'flake-parts/nixpkgs-lib': 'github:NixOS/nixpkgs/e92039b55bcd58469325ded85d4f58dd5a4eaf58?dir=lib' (2023-11-29) → 'github:NixOS/nixpkgs/b0d36bd0a420ecee3bc916c91886caca87c894e9?dir=lib' (2023-12-30) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870' (2023-12-25) → 'github:nixos/nixpkgs/63143ac2c9186be6d9da6035fa22620018c85932' (2024-01-02) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 774f394cd..9ed93f9fd 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1701473968, - "narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=", + "lastModified": 1704152458, + "narHash": "sha256-DS+dGw7SKygIWf9w4eNBUZsK+4Ug27NwEWmn2tnbycg=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5", + "rev": "88a2cd8166694ba0b6cb374700799cec53aef527", "type": "github" }, "original": { @@ -106,11 +106,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1703499205, - "narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=", + "lastModified": 1704161960, + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870", + "rev": "63143ac2c9186be6d9da6035fa22620018c85932", "type": "github" }, "original": { @@ -123,11 +123,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1701253981, - "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", + "lastModified": 1703961334, + "narHash": "sha256-M1mV/Cq+pgjk0rt6VxoyyD+O8cOUiai8t9Q6Yyq4noY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", + "rev": "b0d36bd0a420ecee3bc916c91886caca87c894e9", "type": "github" }, "original": { From d94e641900db85cab74df95fa5bf100247b83f59 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 7 Jan 2024 22:00:31 +0200 Subject: [PATCH 187/755] Load versions on first edit Signed-off-by: Trial97 --- launcher/ui/pages/instance/ModFolderPage.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 313fef2b6..aba87942a 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -321,6 +321,12 @@ bool CoreModFolderPage::shouldDisplay() const auto version = inst->getPackProfile(); + ProgressDialog loadDialog(parentWidget()); + auto update = inst->createUpdateTask(Net::Mode::Offline); + if (update) { + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(update.get()); + } if (!version) return true; if (!version->getComponent("net.minecraftforge")) From 26474d13749dd81869e155d64125c4b7d73ff0f2 Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 11 Jan 2024 03:06:37 -0500 Subject: [PATCH 188/755] nix: deduplicate nixpkgs input Signed-off-by: seth --- flake.lock | 22 +++------------------- flake.nix | 13 +++++++++---- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/flake.lock b/flake.lock index 9ed93f9fd..1af961f3d 100644 --- a/flake.lock +++ b/flake.lock @@ -18,7 +18,9 @@ }, "flake-parts": { "inputs": { - "nixpkgs-lib": "nixpkgs-lib" + "nixpkgs-lib": [ + "nixpkgs" + ] }, "locked": { "lastModified": 1704152458, @@ -120,24 +122,6 @@ "type": "github" } }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1703961334, - "narHash": "sha256-M1mV/Cq+pgjk0rt6VxoyyD+O8cOUiai8t9Q6Yyq4noY=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "b0d36bd0a420ecee3bc916c91886caca87c894e9", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "pre-commit-hooks": { "inputs": { "flake-compat": [ diff --git a/flake.nix b/flake.nix index afb0ec63a..4e2bd8a34 100644 --- a/flake.nix +++ b/flake.nix @@ -3,13 +3,18 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; + }; nix-filter.url = "github:numtide/nix-filter"; pre-commit-hooks = { url = "github:cachix/pre-commit-hooks.nix"; - inputs.nixpkgs.follows = "nixpkgs"; - inputs.nixpkgs-stable.follows = "nixpkgs"; - inputs.flake-compat.follows = "flake-compat"; + inputs = { + nixpkgs.follows = "nixpkgs"; + nixpkgs-stable.follows = "nixpkgs"; + flake-compat.follows = "flake-compat"; + }; }; flake-compat = { url = "github:edolstra/flake-compat"; From 839adfc90bac8204581b930efc7cf2ad55629546 Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 11 Jan 2024 03:11:58 -0500 Subject: [PATCH 189/755] nix: add garnix cache to nixConfig Signed-off-by: seth --- flake.nix | 5 +++++ nix/README.md | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 4e2bd8a34..1582400f1 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,11 @@ { description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)"; + nixConfig = { + extra-substituters = ["https://cache.garnix.io"]; + extra-trusted-public-keys = ["cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="]; + }; + inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; flake-parts = { diff --git a/nix/README.md b/nix/README.md index 7a422fc5b..f7923577f 100644 --- a/nix/README.md +++ b/nix/README.md @@ -9,7 +9,8 @@ See [Package variants](#package-variants) for a list of available packages. ## Installing a development release (flake) We use [garnix](https://garnix.io/) to build and cache our development builds. -If you want to avoid rebuilds you may add the garnix cache to your substitutors. +If you want to avoid rebuilds you may add the garnix cache to your substitutors, or use `--accept-flake-config` +to temporarily enable it when using `nix` commands. Example (NixOS): From b0f65476d987623eeb1d6f1a32e2c6cfc552cbaa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:59:17 +0000 Subject: [PATCH 190/755] chore(deps): update actions/cache action to v3.3.3 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1159aad6c..c602a6dbe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -151,7 +151,7 @@ jobs: - name: Retrieve ccache cache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' - uses: actions/cache@v3.3.2 + uses: actions/cache@v3.3.3 with: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} From 9c610b616abb60b41d600a10820faf965ed51b81 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:02:43 +0000 Subject: [PATCH 191/755] chore(deps): update cachix/install-nix-action action to v25 --- .github/workflows/update-flake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index 538972968..2d78997ed 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24 + - uses: cachix/install-nix-action@6004951b182f8860210c8d6f0d808ec5b1a33d28 # v25 - uses: DeterminateSystems/update-flake-lock@v20 with: From 96c714de6eb27bf10ea0f295067e92b853130dbc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 14 Jan 2024 00:20:26 +0000 Subject: [PATCH 192/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/88a2cd8166694ba0b6cb374700799cec53aef527' (2024-01-01) → 'github:hercules-ci/flake-parts/07f6395285469419cf9d078f59b5b49993198c00' (2024-01-11) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/63143ac2c9186be6d9da6035fa22620018c85932' (2024-01-02) → 'github:nixos/nixpkgs/eabe8d3eface69f5bb16c18f8662a702f50c20d5' (2024-01-09) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/9d3d7e18c6bc4473d7520200d4ddab12f8402d38' (2023-12-30) → 'github:cachix/pre-commit-hooks.nix/274ae3979a0eacae422e1bbcf63b8b7a335e1114' (2024-01-12) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 1af961f3d..ea11e9f26 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1704152458, - "narHash": "sha256-DS+dGw7SKygIWf9w4eNBUZsK+4Ug27NwEWmn2tnbycg=", + "lastModified": 1704982712, + "narHash": "sha256-2Ptt+9h8dczgle2Oo6z5ni5rt/uLMG47UFTR1ry/wgg=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "88a2cd8166694ba0b6cb374700799cec53aef527", + "rev": "07f6395285469419cf9d078f59b5b49993198c00", "type": "github" }, "original": { @@ -108,11 +108,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704161960, - "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", + "lastModified": 1704842529, + "narHash": "sha256-OTeQA+F8d/Evad33JMfuXC89VMetQbsU4qcaePchGr4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "63143ac2c9186be6d9da6035fa22620018c85932", + "rev": "eabe8d3eface69f5bb16c18f8662a702f50c20d5", "type": "github" }, "original": { @@ -137,11 +137,11 @@ ] }, "locked": { - "lastModified": 1703939133, - "narHash": "sha256-Gxe+mfOT6bL7wLC/tuT2F+V+Sb44jNr8YsJ3cyIl4Mo=", + "lastModified": 1705072518, + "narHash": "sha256-90dERRuG781f0EWjn2AOtScZqsTcpIFLpY8TN2VbkL8=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "9d3d7e18c6bc4473d7520200d4ddab12f8402d38", + "rev": "274ae3979a0eacae422e1bbcf63b8b7a335e1114", "type": "github" }, "original": { From 67d088dc53337fc2abeae3ce20c89c9495f5c9be Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sun, 14 Jan 2024 11:36:17 +0100 Subject: [PATCH 193/755] fix: simplify openPath calls Signed-off-by: Sefa Eyeoglu --- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/pages/instance/ScreenshotsPage.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 9f2cc434f..85573314d 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1452,7 +1452,7 @@ void MainWindow::on_actionViewSelectedInstFolder_triggered() { if (m_selectedInstance) { QString str = m_selectedInstance->instanceRoot(); - DesktopServices::openPath(QDir(str).absolutePath()); + DesktopServices::openPath(QFileInfo(str)); } } diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index 01061160b..c3f955733 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -324,8 +324,7 @@ void ScreenshotsPage::onItemActivated(QModelIndex index) if (!index.isValid()) return; auto info = m_model->fileInfo(index); - QString fileName = info.absoluteFilePath(); - DesktopServices::openPath(info.absoluteFilePath()); + DesktopServices::openPath(info); } void ScreenshotsPage::onCurrentSelectionChanged(const QItemSelection& selected) From baebef982eebbe7ea30d9d54cda9cadac279e1be Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 13 Jan 2024 16:10:48 +0100 Subject: [PATCH 194/755] feat: add macOS code signing Signed-off-by: Sefa Eyeoglu --- .github/workflows/build.yml | 32 ++++++++++++++++++++++++++- .github/workflows/trigger_builds.yml | 3 +++ .github/workflows/trigger_release.yml | 3 +++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c602a6dbe..ec1ac220d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,15 @@ on: WINDOWS_CODESIGN_PASSWORD: description: Password for signing Windows builds required: false + APPLE_CODESIGN_CERT: + description: Certificate for signing macOS builds + required: false + APPLE_CODESIGN_PASSWORD: + description: Password for signing macOS builds + required: false + APPLE_CODESIGN_ID: + description: Certificate ID for signing macOS builds + required: false CACHIX_AUTH_TOKEN: description: Private token for authenticating against Cachix cache required: false @@ -336,6 +345,20 @@ jobs: # PACKAGE BUILDS ## + - name: Fetch codesign certificate (macOS) + if: runner.os == 'macOS' + run: | + echo '${{ secrets.APPLE_CODESIGN_CERT }}' | base64 --decode > codesign.p12 + if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then + security create-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain + security import codesign.p12 -k build.keychain -P '${{ secrets.APPLE_CODESIGN_PASSWORD }}' -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k '${{ secrets.APPLE_CODESIGN_PASSWORD }}' build.keychain + else + echo ":warning: Using ad-hoc code signing for macOS, as certificate was not present." >> $GITHUB_STEP_SUMMARY + fi + - name: Package (macOS) if: runner.os == 'macOS' run: | @@ -343,7 +366,14 @@ jobs: cd ${{ env.INSTALL_DIR }} chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher" - sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher" + + if [ -n '${{ secrets.APPLE_CODESIGN_ID }}' ]; then + APPLE_CODESIGN_ID='${{ secrets.APPLE_CODESIGN_ID }}' + else + APPLE_CODESIGN_ID='-' + fi + + sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher" mv "PrismLauncher.app" "Prism Launcher.app" tar -czf ../PrismLauncher.tar.gz * diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 70fda60ed..9df647759 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -32,6 +32,9 @@ jobs: SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} + APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }} + APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }} + APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index ebf5f96e6..ea2a7f3a1 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -16,6 +16,9 @@ jobs: SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }} WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} + APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }} + APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }} + APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} From 27c52eff8b9add54c23f4e033c64e8e9bd7aaa9a Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Sat, 13 Jan 2024 18:04:46 +0100 Subject: [PATCH 195/755] feat: add macOS notarization Signed-off-by: Sefa Eyeoglu --- .github/workflows/build.yml | 31 +++++++++++++++++++++++++-- .github/workflows/trigger_builds.yml | 3 +++ .github/workflows/trigger_release.yml | 11 ++++++---- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec1ac220d..4c296fa8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,15 @@ on: APPLE_CODESIGN_ID: description: Certificate ID for signing macOS builds required: false + APPLE_NOTARIZE_APPLE_ID: + description: Apple ID used for notarizing macOS builds + required: false + APPLE_NOTARIZE_TEAM_ID: + description: Team ID used for notarizing macOS builds + required: false + APPLE_NOTARIZE_PASSWORD: + description: Password used for notarizing macOS builds + required: false CACHIX_AUTH_TOKEN: description: Private token for authenticating against Cachix cache required: false @@ -375,7 +384,25 @@ jobs: sudo codesign --sign "$APPLE_CODESIGN_ID" --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher" mv "PrismLauncher.app" "Prism Launcher.app" - tar -czf ../PrismLauncher.tar.gz * + + - name: Notarize (macOS) + if: runner.os == 'macOS' + run: | + cd ${{ env.INSTALL_DIR }} + + if [ -n '${{ secrets.APPLE_NOTARIZE_PASSWORD }}' ]; then + ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip + xcrun notarytool submit ../PrismLauncher.zip \ + --wait --progress \ + --apple-id '${{ secrets.APPLE_NOTARIZE_APPLE_ID }}' \ + --team-id '${{ secrets.APPLE_NOTARIZE_TEAM_ID }}' \ + --password '${{ secrets.APPLE_NOTARIZE_PASSWORD }}' + + xcrun stapler staple "Prism Launcher.app" + else + echo ":warning: Skipping notarization as credentials are not present." >> $GITHUB_STEP_SUMMARY + fi + ditto -c -k --sequesterRsrc --keepParent "Prism Launcher.app" ../PrismLauncher.zip - name: Make Sparkle signature (macOS) if: matrix.name == 'macOS' @@ -550,7 +577,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }} - path: PrismLauncher.tar.gz + path: PrismLauncher.zip - name: Upload binary zip (Windows) if: runner.os == 'Windows' diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 9df647759..0b8386d69 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -35,6 +35,9 @@ jobs: APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }} APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }} APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} + APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }} + APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} + APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index ea2a7f3a1..9b2bf810f 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -19,6 +19,9 @@ jobs: APPLE_CODESIGN_CERT: ${{ secrets.APPLE_CODESIGN_CERT }} APPLE_CODESIGN_PASSWORD: ${{ secrets.APPLE_CODESIGN_PASSWORD }} APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} + APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }} + APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} + APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} @@ -49,8 +52,8 @@ jobs: mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync - mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz - mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz + mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip + mv PrismLauncher-macOS*/PrismLauncher.zip PrismLauncher-macOS-${{ env.VERSION }}.zip tar --exclude='.git' -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }} @@ -105,6 +108,6 @@ jobs: PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe - PrismLauncher-macOS-${{ env.VERSION }}.tar.gz - PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz + PrismLauncher-macOS-${{ env.VERSION }}.zip + PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip PrismLauncher-${{ env.VERSION }}.tar.gz From de9232783e50cf3fa522dc13bddf937754ff33ce Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 17 Jan 2024 13:09:56 +0100 Subject: [PATCH 196/755] chore: remove cachix auth token secret Signed-off-by: Sefa Eyeoglu --- .github/workflows/build.yml | 3 --- .github/workflows/trigger_builds.yml | 1 - .github/workflows/trigger_release.yml | 1 - 3 files changed, 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c296fa8c..22d8defaa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,9 +39,6 @@ on: APPLE_NOTARIZE_PASSWORD: description: Password used for notarizing macOS builds required: false - CACHIX_AUTH_TOKEN: - description: Private token for authenticating against Cachix cache - required: false GPG_PRIVATE_KEY: description: Private key for AppImage signing required: false diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 0b8386d69..9efafc8cc 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -38,6 +38,5 @@ jobs: APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }} APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} - CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 9b2bf810f..2afbaeb61 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -22,7 +22,6 @@ jobs: APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }} APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} - CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} From bfd1262c14fd2c57ecb8ebf081687212b11a738e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 20:29:50 +0000 Subject: [PATCH 197/755] chore(deps): update actions/cache action to v4 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c602a6dbe..982ecee4a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -151,7 +151,7 @@ jobs: - name: Retrieve ccache cache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' - uses: actions/cache@v3.3.3 + uses: actions/cache@v4.0.0 with: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} From 30913fdb2bef0f09843e1834f5c69e345327329e Mon Sep 17 00:00:00 2001 From: "erik.lundstedt" Date: Sun, 21 Jan 2024 00:31:00 +0100 Subject: [PATCH 198/755] fix a minor spelling misstake in Mod.cpp closes https://github.com/PrismLauncher/PrismLauncher/issues/2047 Signed-off-by: erik.lundstedt --- launcher/minecraft/mod/Mod.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 310946379..32d0d1614 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -274,7 +274,7 @@ QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const return {}; if (m_pack_image_cache_key.was_ever_used) { - qDebug() << "Mod" << name() << "Had it's icon evicted form the cache. reloading..."; + qDebug() << "Mod" << name() << "Had it's icon evicted from the cache. reloading..."; PixmapCache::markCacheMissByEviciton(); } // Image got evicted from the cache or an attempt to load it has not been made. load it and retry. From c7cd8b787704133e1581a0264f69cf9b9549dfd5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 21 Jan 2024 00:20:26 +0000 Subject: [PATCH 199/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nix-filter': 'github:numtide/nix-filter/c843418ecfd0344ecb85844b082ff5675e02c443' (2023-12-04) → 'github:numtide/nix-filter/3449dc925982ad46246cfc36469baf66e1b64f17' (2024-01-15) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/eabe8d3eface69f5bb16c18f8662a702f50c20d5' (2024-01-09) → 'github:nixos/nixpkgs/e5d1c87f5813afde2dda384ac807c57a105721cc' (2024-01-19) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/274ae3979a0eacae422e1bbcf63b8b7a335e1114' (2024-01-12) → 'github:cachix/pre-commit-hooks.nix/f56597d53fd174f796b5a7d3ee0b494f9e2285cc' (2024-01-20) • Updated input 'pre-commit-hooks/flake-utils': 'github:numtide/flake-utils/a1720a10a6cfe8234c0e93907ffe81be440f4cef' (2023-05-31) → 'github:numtide/flake-utils/4022d587cbbfd70fe950c1e2083a02621806a725' (2023-12-04) • Updated input 'pre-commit-hooks/gitignore': 'github:hercules-ci/gitignore.nix/a20de23b925fd8264fd7fad6454652e142fd7f73' (2022-08-14) → 'github:hercules-ci/gitignore.nix/43e1aa1308018f37118e34d3a9cb4f5e75dc11d5' (2023-12-29) --- flake.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/flake.lock b/flake.lock index ea11e9f26..5b0619292 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1685518550, - "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", "owner": "numtide", "repo": "flake-utils", - "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", "type": "github" }, "original": { @@ -62,11 +62,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", "type": "github" }, "original": { @@ -93,11 +93,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1701697642, - "narHash": "sha256-L217WytWZHSY8GW9Gx1A64OnNctbuDbfslaTEofXXRw=", + "lastModified": 1705332318, + "narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=", "owner": "numtide", "repo": "nix-filter", - "rev": "c843418ecfd0344ecb85844b082ff5675e02c443", + "rev": "3449dc925982ad46246cfc36469baf66e1b64f17", "type": "github" }, "original": { @@ -108,11 +108,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704842529, - "narHash": "sha256-OTeQA+F8d/Evad33JMfuXC89VMetQbsU4qcaePchGr4=", + "lastModified": 1705697961, + "narHash": "sha256-XepT3WS516evSFYkme3GrcI3+7uwXHqtHbip+t24J7E=", "owner": "nixos", "repo": "nixpkgs", - "rev": "eabe8d3eface69f5bb16c18f8662a702f50c20d5", + "rev": "e5d1c87f5813afde2dda384ac807c57a105721cc", "type": "github" }, "original": { @@ -137,11 +137,11 @@ ] }, "locked": { - "lastModified": 1705072518, - "narHash": "sha256-90dERRuG781f0EWjn2AOtScZqsTcpIFLpY8TN2VbkL8=", + "lastModified": 1705757126, + "narHash": "sha256-Eksr+n4Q8EYZKAN0Scef5JK4H6FcHc+TKNHb95CWm+c=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "274ae3979a0eacae422e1bbcf63b8b7a335e1114", + "rev": "f56597d53fd174f796b5a7d3ee0b494f9e2285cc", "type": "github" }, "original": { From f54ac25614b62b0a43d014d8b47f9faee8112d12 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 21 Jan 2024 02:28:44 +0000 Subject: [PATCH 200/755] Use `minecraft` instead of `.minecraft` for better accessibility Signed-off-by: TheKodeToad --- launcher/InstanceCopyTask.cpp | 6 +++--- launcher/InstanceImportTask.cpp | 4 ++-- launcher/minecraft/MinecraftInstance.cpp | 6 +++--- launcher/modplatform/import_ftb/PackInstallTask.cpp | 2 +- launcher/modplatform/legacy_ftb/PackInstallTask.cpp | 4 ++-- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 4 ++-- launcher/modplatform/technic/SingleZipPackInstallTask.cpp | 2 +- launcher/modplatform/technic/SolderPackInstallTask.cpp | 2 +- launcher/modplatform/technic/TechnicPackProcessor.cpp | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index cdcd61ba6..52eb7d879 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -43,10 +43,10 @@ void InstanceCopyTask::executeTask() QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft")); QString staging_mc_dir; - if (mcDir.exists() && !dotMCDir.exists()) - staging_mc_dir = mcDir.filePath(); - else + if (dotMCDir.exists() && !mcDir.exists()) staging_mc_dir = dotMCDir.filePath(); + else + staging_mc_dir = mcDir.filePath(); FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves")); savesCopy.followSymlinks(true); diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 9f0cf7460..d4676f358 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -164,8 +164,8 @@ void InstanceImportTask::processZipPack() } else if (technicFound) { // process as Technic pack qDebug() << "Technic:" << technicFound; - extractDir.mkpath(".minecraft"); - extractDir.cd(".minecraft"); + extractDir.mkpath("minecraft"); + extractDir.cd("minecraft"); m_modpackType = ModpackType::Technic; } else { QStringList paths_to_ignore{ "overrides/" }; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4229f73eb..5a8c0d28a 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -292,10 +292,10 @@ QString MinecraftInstance::gameRoot() const QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); - if (mcDir.exists() && !dotMCDir.exists()) - return mcDir.filePath(); - else + if (dotMCDir.exists() && !mcDir.exists()) return dotMCDir.filePath(); + else + return mcDir.filePath(); } QString MinecraftInstance::binRoot() const diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp index 9a3b2595b..946ec4eb7 100644 --- a/launcher/modplatform/import_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -37,7 +37,7 @@ void PackInstallTask::executeTask() progress(1, 2); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { - FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft")); + FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, "minecraft")); folderCopy.followSymlinks(true); return folderCopy(); }); diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 091296751..0048c7fac 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -137,7 +137,7 @@ void PackInstallTask::install() QDir unzipMcDir(m_stagingPath + "/unzip/minecraft"); if (unzipMcDir.exists()) { // ok, found minecraft dir, move contents to instance dir - if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) { + if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/minecraft")) { emitFailed(tr("Failed to move unzipped Minecraft!")); return; } @@ -155,7 +155,7 @@ void PackInstallTask::install() bool fallback = true; // handle different versions - QFile packJson(m_stagingPath + "/.minecraft/pack.json"); + QFile packJson(m_stagingPath + "/minecraft/pack.json"); QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods"); if (packJson.exists()) { packJson.open(QIODevice::ReadOnly | QIODevice::Text); diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 96d9c84d2..824fdce7e 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -173,7 +173,7 @@ bool ModrinthCreationTask::createInstance() FS::ensureFilePathExists(new_index_place); QFile::rename(index_path, new_index_place); - auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft"); + auto mcPath = FS::PathCombine(m_stagingPath, "minecraft"); auto override_path = FS::PathCombine(m_stagingPath, "overrides"); if (QFile::exists(override_path)) { @@ -234,7 +234,7 @@ bool ModrinthCreationTask::createInstance() m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network())); - auto root_modpack_path = FS::PathCombine(m_stagingPath, ".minecraft"); + auto root_modpack_path = FS::PathCombine(m_stagingPath, "minecraft"); auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path); for (auto file : m_files) { diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index ad08d72d4..cc9ced10b 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -62,7 +62,7 @@ void Technic::SingleZipPackInstallTask::downloadSucceeded() m_abortable = false; setStatus(tr("Extracting modpack")); - QDir extractDir(FS::PathCombine(m_stagingPath, ".minecraft")); + QDir extractDir(FS::PathCombine(m_stagingPath, "minecraft")); qDebug() << "Attempting to create instance from" << m_archivePath; // open the zip and find relevant files in it diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index c162d6253..ed8b0a8a4 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -140,7 +140,7 @@ void Technic::SolderPackInstallTask::downloadSucceeded() m_filesNetJob.reset(); m_extractFuture = QtConcurrent::run([this]() { int i = 0; - QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft"); + QString extractDir = FS::PathCombine(m_stagingPath, "minecraft"); FS::ensureFolderPathExists(extractDir); while (m_modCount > i) { diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp index 3b9424bf8..90f59ce54 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.cpp +++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -33,7 +33,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString& minecraftVersion, [[maybe_unused]] const bool isSolder) { - QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft"); + QString minecraftPath = FS::PathCombine(stagingPath, "minecraft"); QString configPath = FS::PathCombine(stagingPath, "instance.cfg"); auto instanceSettings = std::make_shared(configPath); MinecraftInstance instance(globalSettings, instanceSettings, stagingPath); From e4306d5cb6f61368b1b3dcc77af2e268814f0c82 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sun, 21 Jan 2024 08:52:49 +0200 Subject: [PATCH 201/755] Update launcher/ui/themes/CatPack.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/themes/CatPack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/themes/CatPack.cpp b/launcher/ui/themes/CatPack.cpp index 0fe628fe9..85eb85a18 100644 --- a/launcher/ui/themes/CatPack.cpp +++ b/launcher/ui/themes/CatPack.cpp @@ -132,7 +132,7 @@ QString JsonCatPack::path(QDate now) auto files = QDir(m_default_path).entryInfoList(supportedImageFormats, QDir::Files, QDir::Name); if (files.length() == 0) return ""; - auto idx = now.dayOfYear() % files.length(); + auto idx = (now.dayOfYear() - 1) % files.length(); auto isRandom = dInfo.fileName().compare("random", Qt::CaseInsensitive) == 0; if (isRandom) idx = QRandomGenerator::global()->bounded(0, files.length()); From f77749e4486f3a19a75b74f933e6d9de9c0c0a44 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 21 Jan 2024 21:24:47 +0200 Subject: [PATCH 202/755] Resized Refresh theme button Signed-off-by: Trial97 --- .../ui/widgets/ThemeCustomizationWidget.ui | 283 ++++++++++-------- 1 file changed, 157 insertions(+), 126 deletions(-) diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui index 322f9d6a7..1faa45c4f 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -13,7 +13,7 @@ Form - + QLayout::SetMinimumSize @@ -29,150 +29,181 @@ 0 - - - - &Icons - - - iconsComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - - - - - View icon themes folder. - + + + + - + &Icons - - - .. - - - true + + iconsComboBox - - - - - - &Widgets - - - widgetStyleComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View icon themes folder. + + + + + + + + + true + + + + - - - - View widget themes folder. - + + - + &Widgets - - - .. - - - true + + widgetStyleComboBox - - - - - - The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. - - - C&at - - - backgroundCatComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View widget themes folder. + + + + + + + + + true + + + + + + + The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. + + C&at + + + backgroundCatComboBox + - - - - View cat packs folder. - - - - - - - .. - - - true - - + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. + + + + + + + View cat packs folder. + + + + + + + + + true + + + + - - - - Refresh - - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refresh all + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + From 0dc4a10f4898dfce43a6febdcfbb675e0182f38a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:26:37 +0000 Subject: [PATCH 203/755] chore(deps): update korthout/backport-action action to v2.4.0 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index dee01e084..866e9a898 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.3.0 + uses: korthout/backport-action@v2.4.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From 12bf91376411d74432f5e88c85d68cef003fd899 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 22 Jan 2024 20:36:57 +0200 Subject: [PATCH 204/755] Fixed open path a second time Signed-off-by: Trial97 --- launcher/DesktopServices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp index 7c070bd31..841c1399c 100644 --- a/launcher/DesktopServices.cpp +++ b/launcher/DesktopServices.cpp @@ -46,7 +46,7 @@ bool openPath(const QFileInfo& path, bool ensureFolderPathExists) if (ensureFolderPathExists) { FS::ensureFolderPathExists(path); } - return openUrl(QUrl::fromLocalFile(QFileInfo(path).absolutePath())); + return openUrl(QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath())); } bool openPath(const QString& path, bool ensureFolderPathExists) From 9bbeb5ef3ae389113bc7219b61600ebadcb4ce7b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 22 Jan 2024 21:26:46 +0200 Subject: [PATCH 205/755] Made updater interval configurable Signed-off-by: Trial97 --- launcher/ui/pages/global/LauncherPage.cpp | 2 ++ launcher/ui/pages/global/LauncherPage.ui | 27 +++++++++++++++++++++++ launcher/updater/PrismExternalUpdater.cpp | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 78c44380a..fc062023c 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -185,6 +185,7 @@ void LauncherPage::applySettings() // Updates if (APPLICATION->updater()) { APPLICATION->updater()->setAutomaticallyChecksForUpdates(ui->autoUpdateCheckBox->isChecked()); + APPLICATION->updater()->setUpdateCheckInterval(ui->updateIntervalSpinBox->value() * 3600); } s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked()); @@ -234,6 +235,7 @@ void LauncherPage::loadSettings() // Updates if (APPLICATION->updater()) { ui->autoUpdateCheckBox->setChecked(APPLICATION->updater()->getAutomaticallyChecksForUpdates()); + ui->updateIntervalSpinBox->setValue(APPLICATION->updater()->getUpdateCheckInterval() / 3600); } // Toolbar/menu bar settings (not applicable if native menu bar is present) diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 18b52e1b8..68f4e7436 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -58,6 +58,33 @@ + + + + + + Update interval + + + + + + + Set it to 0 to only check on launch + + + h + + + 0 + + + 99999999 + + + + +
    diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index bee72e3a0..dc1aae872 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -257,7 +257,7 @@ void PrismExternalUpdater::setBetaAllowed(bool allowed) void PrismExternalUpdater::resetAutoCheckTimer() { - if (priv->autoCheck) { + if (priv->autoCheck && priv->updateInterval > 0) { int timeoutDuration = 0; auto now = QDateTime::currentDateTime(); if (priv->lastCheck.isValid()) { From 6b637a8797dad184d5ed73a93f00e9f86da22ee0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 22 Jan 2024 22:32:42 +0200 Subject: [PATCH 206/755] Display minecraft version if not mentioned for modrinth packs Signed-off-by: Trial97 --- .../modplatform/modrinth/ModrinthPackManifest.cpp | 4 ++++ launcher/modplatform/modrinth/ModrinthPackManifest.h | 1 + .../ui/pages/modplatform/modrinth/ModrinthPage.cpp | 11 ++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 7846e966d..f360df43a 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -131,6 +131,10 @@ auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion file.name = Json::requireString(obj, "name"); file.version = Json::requireString(obj, "version_number"); + auto gameVersions = Json::ensureArray(obj, "game_versions"); + if (!gameVersions.isEmpty()) { + file.gameVersion = Json::ensureString(gameVersions[0]); + } file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); file.changelog = Json::ensureString(obj, "changelog"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 1ffd31d83..2bd61c5d9 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -84,6 +84,7 @@ struct ModpackExtra { struct ModpackVersion { QString name; QString version; + QString gameVersion; ModPlatform::IndexedVersionType version_type; QString changelog; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index fffa21940..452416edd 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -222,11 +222,12 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI } 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%3").arg(version.name, version.version, release_type), - QVariant(version.id)); - else - ui->versionSelectionBox->addItem(QString("%1%2").arg(version.name, release_type), QVariant(version.id)); + auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion) + ? QString(" for %1").arg(version.gameVersion) + : ""; + auto versionStr = !version.name.contains(version.version) ? version.version : ""; + ui->versionSelectionBox->addItem(QString("%1%2 — %3%4").arg(version.name, mcVersion, versionStr, release_type), + QVariant(version.id)); } QVariant current_updated; From 72e6218df9df28f657b6626bb775ace58d507647 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:21:30 +0000 Subject: [PATCH 207/755] chore(deps): update korthout/backport-action action to v2.4.1 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 866e9a898..9b51b201e 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,7 +24,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.4.0 + uses: korthout/backport-action@v2.4.1 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From 8312713dc2274670db6f9167125af8a27c2fd8a6 Mon Sep 17 00:00:00 2001 From: seth Date: Wed, 17 Jan 2024 22:46:29 -0500 Subject: [PATCH 208/755] refactor(nix): nix-filter -> lib.fileset with our own use of `map`, this is basically a drop-in replacement for nix-filter, but already in nixpkgs! Signed-off-by: seth --- flake.lock | 16 ---------------- flake.nix | 1 - nix/distribution.nix | 16 ---------------- nix/pkg/default.nix | 15 +++++++++++++-- 4 files changed, 13 insertions(+), 35 deletions(-) diff --git a/flake.lock b/flake.lock index 5b0619292..79075c3c7 100644 --- a/flake.lock +++ b/flake.lock @@ -91,21 +91,6 @@ "type": "github" } }, - "nix-filter": { - "locked": { - "lastModified": 1705332318, - "narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=", - "owner": "numtide", - "repo": "nix-filter", - "rev": "3449dc925982ad46246cfc36469baf66e1b64f17", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "nix-filter", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1705697961, @@ -155,7 +140,6 @@ "flake-compat": "flake-compat", "flake-parts": "flake-parts", "libnbtplusplus": "libnbtplusplus", - "nix-filter": "nix-filter", "nixpkgs": "nixpkgs", "pre-commit-hooks": "pre-commit-hooks" } diff --git a/flake.nix b/flake.nix index 1582400f1..e16c76699 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,6 @@ url = "github:hercules-ci/flake-parts"; inputs.nixpkgs-lib.follows = "nixpkgs"; }; - nix-filter.url = "github:numtide/nix-filter"; pre-commit-hooks = { url = "github:cachix/pre-commit-hooks.nix"; inputs = { diff --git a/nix/distribution.nix b/nix/distribution.nix index ca9999dcb..01c90f783 100644 --- a/nix/distribution.nix +++ b/nix/distribution.nix @@ -26,24 +26,8 @@ overlays.default = final: prev: let version = builtins.substring 0 8 self.lastModifiedDate or "dirty"; - filteredSelf = inputs.nix-filter.lib.filter { - root = ../.; - include = [ - "buildconfig" - "cmake" - "launcher" - "libraries" - "program_info" - "tests" - ../COPYING.md - ../CMakeLists.txt - ]; - }; - # common args for prismlauncher evaluations unwrappedArgs = { - self = filteredSelf; - inherit (inputs) libnbtplusplus; inherit ((final.darwin or prev.darwin).apple_sdk.frameworks) Cocoa; inherit version; diff --git a/nix/pkg/default.nix b/nix/pkg/default.nix index fd19a0b3d..0078def8c 100644 --- a/nix/pkg/default.nix +++ b/nix/pkg/default.nix @@ -16,7 +16,6 @@ gamemode, msaClientID ? null, gamemodeSupport ? stdenv.isLinux, - self, version, libnbtplusplus, }: @@ -25,7 +24,19 @@ assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is on pname = "prismlauncher-unwrapped"; inherit version; - src = lib.cleanSource self; + src = lib.fileset.toSource { + root = ../../.; + fileset = lib.fileset.unions (map (fileName: ../../${fileName}) [ + "buildconfig" + "cmake" + "launcher" + "libraries" + "program_info" + "tests" + "COPYING.md" + "CMakeLists.txt" + ]); + }; nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja canonicalize-jars-hook]; buildInputs = From 8de1060bfb8eba7ad6efbb2093df61992e25e3cd Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:42:01 +0100 Subject: [PATCH 209/755] fix: remove alpha notice on modrinth packs with the modrinth app being released for quite a while now it's weird to still have this (tbh they've never been truly alpha) Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .../modplatform/modrinth/ModrinthPage.ui | 58 +++++++------------ 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 78a25feae..68b1d4e24 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -11,43 +11,7 @@ - - - - - true - - - - Note: Modrinth modpacks are still in alpha phase. Some things may be rough on the edges, or not working at all! Use it with caution. - - - Qt::AlignCenter - - - true - - - - - - - - - Search and filter ... - - - - - - - Search - - - - - - + @@ -77,7 +41,7 @@ - + @@ -97,6 +61,24 @@ + + + + + + Search and filter ... + + + + + + + Search + + + + + From a0e7729aa62a5bb6a83e401dd863e69cbd886edc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 24 Jan 2024 18:26:43 +0200 Subject: [PATCH 210/755] Started workin on stuff Signed-off-by: Trial97 --- launcher/Application.cpp | 18 +- launcher/Application.h | 2 - launcher/CMakeLists.txt | 2 - launcher/JavaCommon.cpp | 30 ++- launcher/JavaCommon.h | 12 +- launcher/MMCZip.cpp | 106 ++++++++++ launcher/MMCZip.h | 28 +++ launcher/SysInfo.cpp | 18 +- launcher/SysInfo.h | 1 + launcher/java/JavaChecker.cpp | 39 ++-- launcher/java/JavaChecker.h | 62 +++--- launcher/java/JavaCheckerJob.cpp | 41 ---- launcher/java/JavaCheckerJob.h | 56 ------ launcher/java/JavaInstallList.cpp | 37 ++-- launcher/java/JavaInstallList.h | 12 +- launcher/java/JavaUtils.h | 5 +- .../java/providers/AdoptiumJavaDownloader.cpp | 119 +++++++++++ .../java/providers/AdoptiumJavaDownloader.h | 36 ++++ .../java/providers/AzulJavaDownloader.cpp | 159 +++++++++++++++ launcher/java/providers/AzulJavaDownloader.h | 37 ++++ .../java/providers/BasicJavaDownloader.cpp | 29 +++ launcher/java/providers/BasicJavaDownloader.h | 41 ++++ .../java/providers/MojangJavaDownloader.cpp | 185 ++++++++++++++++++ .../java/providers/MojanglJavaDownloader.h | 37 ++++ launcher/launch/steps/CheckJava.cpp | 14 +- launcher/launch/steps/CheckJava.h | 4 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 21 +- launcher/ui/widgets/JavaSettingsWidget.h | 4 +- 28 files changed, 908 insertions(+), 247 deletions(-) delete mode 100644 launcher/java/JavaCheckerJob.cpp delete mode 100644 launcher/java/JavaCheckerJob.h create mode 100644 launcher/java/providers/AdoptiumJavaDownloader.cpp create mode 100644 launcher/java/providers/AdoptiumJavaDownloader.h create mode 100644 launcher/java/providers/AzulJavaDownloader.cpp create mode 100644 launcher/java/providers/AzulJavaDownloader.h create mode 100644 launcher/java/providers/BasicJavaDownloader.cpp create mode 100644 launcher/java/providers/BasicJavaDownloader.h create mode 100644 launcher/java/providers/MojangJavaDownloader.cpp create mode 100644 launcher/java/providers/MojanglJavaDownloader.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 661c6c5be..aa702aa7e 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -44,6 +44,7 @@ #include "BuildConfig.h" #include "DataMigrationTask.h" +#include "java/JavaInstallList.h" #include "net/PasteUpload.h" #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" @@ -125,6 +126,7 @@ #include #include +#include "SysInfo.h" #ifdef Q_OS_LINUX #include @@ -607,7 +609,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // Memory m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); - m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem()); + m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::suitableMaxMem()); m_settings->registerSetting("PermGen", 128); // Java Settings @@ -1667,20 +1669,6 @@ QString Application::getUserAgentUncached() return BuildConfig.USER_AGENT_UNCACHED; } -int Application::suitableMaxMem() -{ - float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte; - int maxMemoryAlloc; - - // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB - if (totalRAM < (4096 * 1.5)) - maxMemoryAlloc = (int)(totalRAM / 1.5); - else - maxMemoryAlloc = 4096; - - return maxMemoryAlloc; -} - bool Application::handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, diff --git a/launcher/Application.h b/launcher/Application.h index 7669e08ec..85bf2dff4 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -180,8 +180,6 @@ class Application : public QApplication { void ShowGlobalSettings(class QWidget* parent, QString open_page = QString()); - int suitableMaxMem(); - bool updaterEnabled(); QString updaterBinaryName(); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 48ca8f085..e735f2081 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -422,8 +422,6 @@ set(SETTINGS_SOURCES set(JAVA_SOURCES java/JavaChecker.h java/JavaChecker.cpp - java/JavaCheckerJob.h - java/JavaCheckerJob.cpp java/JavaInstall.h java/JavaInstall.cpp java/JavaInstallList.h diff --git a/launcher/JavaCommon.cpp b/launcher/JavaCommon.cpp index e16ac9255..cfc3cfe42 100644 --- a/launcher/JavaCommon.cpp +++ b/launcher/JavaCommon.cpp @@ -63,7 +63,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent) return true; } -void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result) +void JavaCommon::javaWasOk(QWidget* parent, const JavaChecker::Result& result) { QString text; text += QObject::tr( @@ -79,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result) CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show(); } -void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result) +void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result) { auto htmlError = result.errorLog; QString text; @@ -89,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result) CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } -void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result) +void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result) { QString text; text += QObject::tr( @@ -116,34 +116,26 @@ void JavaCommon::TestCheck::run() emit finished(); return; } - checker.reset(new JavaChecker()); + checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0, this)); connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished); - checker->m_path = m_path; - checker->performCheck(); + checker->start(); } -void JavaCommon::TestCheck::checkFinished(JavaCheckResult result) +void JavaCommon::TestCheck::checkFinished(JavaChecker::Result result) { - if (result.validity != JavaCheckResult::Validity::Valid) { + if (result.validity != JavaChecker::Result::Validity::Valid) { javaBinaryWasBad(m_parent, result); emit finished(); return; } - checker.reset(new JavaChecker()); + checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0, this)); connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs); - checker->m_path = m_path; - checker->m_args = m_args; - checker->m_minMem = m_minMem; - checker->m_maxMem = m_maxMem; - if (result.javaVersion.requiresPermGen()) { - checker->m_permGen = m_permGen; - } - checker->performCheck(); + checker->start(); } -void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result) +void JavaCommon::TestCheck::checkFinishedWithArgs(JavaChecker::Result result) { - if (result.validity == JavaCheckResult::Validity::Valid) { + if (result.validity == JavaChecker::Result::Validity::Valid) { javaWasOk(m_parent, result); emit finished(); return; diff --git a/launcher/JavaCommon.h b/launcher/JavaCommon.h index c96f7a985..7c5510efc 100644 --- a/launcher/JavaCommon.h +++ b/launcher/JavaCommon.h @@ -10,11 +10,11 @@ namespace JavaCommon { bool checkJVMArgs(QString args, QWidget* parent); // Show a dialog saying that the Java binary was usable -void javaWasOk(QWidget* parent, const JavaCheckResult& result); +void javaWasOk(QWidget* parent, const JavaChecker::Result& result); // Show a dialog saying that the Java binary was not usable because of bad options -void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result); +void javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result); // Show a dialog saying that the Java binary was not usable -void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result); +void javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result); // Show a dialog if we couldn't find Java Checker void javaCheckNotFound(QWidget* parent); @@ -32,11 +32,11 @@ class TestCheck : public QObject { void finished(); private slots: - void checkFinished(JavaCheckResult result); - void checkFinishedWithArgs(JavaCheckResult result); + void checkFinished(JavaChecker::Result result); + void checkFinishedWithArgs(JavaChecker::Result result); private: - std::shared_ptr checker; + JavaChecker::Ptr checker; QWidget* m_parent = nullptr; QString m_path; QString m_args; diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 3bfe16ab5..78a3b290d 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -507,6 +507,112 @@ bool ExportToZipTask::abort() } return false; } + +void ExtractZipTask::executeTask() +{ + m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); }); + connect(&m_zip_watcher, &QFutureWatcher::finished, this, &ExtractZipTask::finish); + m_zip_watcher.setFuture(m_zip_future); +} + +auto ExtractZipTask::extractZip() -> ZipResult +{ + auto target = m_output_dir.absolutePath(); + auto target_top_dir = QUrl::fromLocalFile(target); + + QStringList extracted; + + qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input->getZipName() << "to" << target; + auto numEntries = m_input->getEntriesCount(); + if (numEntries < 0) { + return ZipResult(tr("Failed to enumerate files in archive")); + } + if (numEntries == 0) { + logWarning(tr("Extracting empty archives seems odd...")); + return ZipResult(); + } + if (!m_input->goToFirstFile()) { + return ZipResult(tr("Failed to seek to first file in zip")); + } + + setStatus("Extracting files..."); + setProgress(0, numEntries); + do { + if (m_zip_future.isCanceled()) + return ZipResult(); + setProgress(m_progress + 1, m_progressTotal); + QString file_name = m_input->getCurrentFileName(); + if (!file_name.startsWith(m_subdirectory)) + continue; + + auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, m_subdirectory.size())); + auto original_name = relative_file_name; + setStatus("Unziping: " + relative_file_name); + + // Fix subdirs/files ending with a / getting transformed into absolute paths + if (relative_file_name.startsWith('/')) + relative_file_name = relative_file_name.mid(1); + + // Fix weird "folders with a single file get squashed" thing + QString sub_path; + if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) { + sub_path = relative_file_name.section('/', 0, -2) + '/'; + FS::ensureFolderPathExists(FS::PathCombine(target, sub_path)); + + relative_file_name = relative_file_name.split('/').last(); + } + + QString target_file_path; + if (relative_file_name.isEmpty()) { + target_file_path = target + '/'; + } else { + target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name); + if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/')) + target_file_path += '/'; + } + + if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { + return ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2") + .arg(relative_file_name, target)); + } + + if (!JlCompress::extractFile(m_input.get(), "", target_file_path)) { + JlCompress::removeFile(extracted); + return ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path)); + } + + extracted.append(target_file_path); + QFile::setPermissions(target_file_path, + QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + + qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; + } while (m_input->goToNextFile()); + + return ZipResult(); +} + +void ExtractZipTask::finish() +{ + if (m_zip_future.isCanceled()) { + emitAborted(); + } else if (auto result = m_zip_future.result(); result.has_value()) { + emitFailed(result.value()); + } else { + emitSucceeded(); + } +} + +bool ExtractZipTask::abort() +{ + if (m_zip_future.isRunning()) { + m_zip_future.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} + #endif } // namespace MMCZip diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 45b1df0b3..2b396eb9c 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -194,5 +194,33 @@ class ExportToZipTask : public Task { QFuture m_build_zip_future; QFutureWatcher m_build_zip_watcher; }; + +class ExtractZipTask : public Task { + public: + ExtractZipTask(QString input, QDir outputDir, QString subdirectory = "") + : ExtractZipTask(std::make_shared(input), outputDir, subdirectory) + {} + ExtractZipTask(std::shared_ptr input, QDir outputDir, QString subdirectory = "") + : m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory) + {} + virtual ~ExtractZipTask() = default; + + typedef std::optional ZipResult; + + protected: + virtual void executeTask() override; + bool abort() override; + + ZipResult extractZip(); + void finish(); + + private: + std::shared_ptr m_input; + QDir m_output_dir; + QString m_subdirectory; + + QFuture m_zip_future; + QFutureWatcher m_zip_watcher; +}; #endif } // namespace MMCZip diff --git a/launcher/SysInfo.cpp b/launcher/SysInfo.cpp index ad251e29f..f15dde0e4 100644 --- a/launcher/SysInfo.cpp +++ b/launcher/SysInfo.cpp @@ -1,5 +1,6 @@ #include #include +#include "sys.h" #ifdef Q_OS_MACOS #include #endif @@ -7,9 +8,6 @@ #include #include #include -#include "Application.h" -#include "Commandline.h" -#include "java/JavaUtils.h" #ifdef Q_OS_MACOS bool rosettaDetect() @@ -59,4 +57,18 @@ QString useQTForArch() #endif return qtArch; } + +int suitableMaxMem() +{ + float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte; + int maxMemoryAlloc; + + // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB + if (totalRAM < (4096 * 1.5)) + maxMemoryAlloc = (int)(totalRAM / 1.5); + else + maxMemoryAlloc = 4096; + + return maxMemoryAlloc; +} } // namespace SysInfo diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h index 3650bf008..499c3b1dd 100644 --- a/launcher/SysInfo.h +++ b/launcher/SysInfo.h @@ -3,4 +3,5 @@ namespace SysInfo { QString currentSystem(); QString useQTForArch(); +int suitableMaxMem(); } // namespace SysInfo diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp index 20caba189..7271c0d09 100644 --- a/launcher/java/JavaChecker.cpp +++ b/launcher/java/JavaChecker.cpp @@ -40,14 +40,13 @@ #include #include -#include "Application.h" #include "Commandline.h" -#include "FileSystem.h" -#include "JavaUtils.h" +#include "java/JavaUtils.h" -JavaChecker::JavaChecker(QObject* parent) : QObject(parent) {} +JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id, QObject* parent) + : Task(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id){}; -void JavaChecker::performCheck() +void JavaChecker::executeTask() { QString checkerJar = JavaUtils::getJavaCheckPath(); @@ -69,7 +68,7 @@ void JavaChecker::performCheck() if (m_maxMem != 0) { args << QString("-Xmx%1m").arg(m_maxMem); } - if (m_permGen != 64) { + if (m_permGen != 64 && m_permGen != 0) { args << QString("-XX:PermSize=%1m").arg(m_permGen); } @@ -112,11 +111,10 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) QProcessPtr _process = process; process.reset(); - JavaCheckResult result; - { - result.path = m_path; - result.id = m_id; - } + Result result = { + m_path, + m_id, + }; result.errorLog = m_stderr; result.outLog = m_stdout; qDebug() << "STDOUT" << m_stdout; @@ -124,8 +122,9 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) qDebug() << "Java checker finished with status" << status << "exit code" << exitcode; if (status == QProcess::CrashExit || exitcode == 1) { - result.validity = JavaCheckResult::Validity::Errored; + result.validity = Result::Validity::Errored; emit checkFinished(result); + emitSucceeded(); return; } @@ -158,8 +157,9 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) } if (!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) { - result.validity = JavaCheckResult::Validity::ReturnedInvalidData; + result.validity = Result::Validity::ReturnedInvalidData; emit checkFinished(result); + emitSucceeded(); return; } @@ -168,7 +168,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) auto java_vendor = results["java.vendor"]; bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64"; - result.validity = JavaCheckResult::Validity::Valid; + result.validity = Result::Validity::Valid; result.is_64bit = is_64; result.mojangPlatform = is_64 ? "64" : "32"; result.realPlatform = os_arch; @@ -176,6 +176,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) result.javaVendor = java_vendor; qDebug() << "Java checker succeeded."; emit checkFinished(result); + emitSucceeded(); } void JavaChecker::error(QProcess::ProcessError err) @@ -187,15 +188,9 @@ void JavaChecker::error(QProcess::ProcessError err) qDebug() << "Native environment:"; qDebug() << QProcessEnvironment::systemEnvironment().toStringList(); killTimer.stop(); - JavaCheckResult result; - { - result.path = m_path; - result.id = m_id; - } - - emit checkFinished(result); - return; + emit checkFinished({ m_path, m_id }); } + emitSucceeded(); } void JavaChecker::timeout() diff --git a/launcher/java/JavaChecker.h b/launcher/java/JavaChecker.h index 7111f8522..0c6191c21 100644 --- a/launcher/java/JavaChecker.h +++ b/launcher/java/JavaChecker.h @@ -3,49 +3,51 @@ #include #include -#include "QObjectPtr.h" - #include "JavaVersion.h" +#include "QObjectPtr.h" +#include "tasks/Task.h" -class JavaChecker; - -struct JavaCheckResult { - QString path; - QString mojangPlatform; - QString realPlatform; - JavaVersion javaVersion; - QString javaVendor; - QString outLog; - QString errorLog; - bool is_64bit = false; - int id; - enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; -}; - -using QProcessPtr = shared_qobject_ptr; -using JavaCheckerPtr = shared_qobject_ptr; -class JavaChecker : public QObject { +class JavaChecker : public Task { Q_OBJECT public: - explicit JavaChecker(QObject* parent = 0); - void performCheck(); + using QProcessPtr = shared_qobject_ptr; + using Ptr = shared_qobject_ptr; - QString m_path; - QString m_args; - int m_id = 0; - int m_minMem = 0; - int m_maxMem = 0; - int m_permGen = 64; + struct Result { + QString path; + int id; + QString mojangPlatform; + QString realPlatform; + JavaVersion javaVersion; + QString javaVendor; + QString outLog; + QString errorLog; + bool is_64bit = false; + enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; + }; + + explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0, QObject* parent = 0); signals: - void checkFinished(JavaCheckResult result); + void checkFinished(Result result); + + protected: + virtual void executeTask() override; private: QProcessPtr process; QTimer killTimer; QString m_stdout; QString m_stderr; - public slots: + + QString m_path; + QString m_args; + int m_minMem = 0; + int m_maxMem = 0; + int m_permGen = 64; + int m_id = 0; + + private slots: void timeout(); void finished(int exitcode, QProcess::ExitStatus); void error(QProcess::ProcessError); diff --git a/launcher/java/JavaCheckerJob.cpp b/launcher/java/JavaCheckerJob.cpp deleted file mode 100644 index 870e2a09a..000000000 --- a/launcher/java/JavaCheckerJob.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "JavaCheckerJob.h" - -#include - -void JavaCheckerJob::partFinished(JavaCheckResult result) -{ - num_finished++; - qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" << javacheckers.size(); - setProgress(num_finished, javacheckers.size()); - - javaresults.replace(result.id, result); - - if (num_finished == javacheckers.size()) { - emitSucceeded(); - } -} - -void JavaCheckerJob::executeTask() -{ - qDebug() << m_job_name.toLocal8Bit() << " started."; - for (auto iter : javacheckers) { - javaresults.append(JavaCheckResult()); - connect(iter.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); - iter->performCheck(); - } -} diff --git a/launcher/java/JavaCheckerJob.h b/launcher/java/JavaCheckerJob.h deleted file mode 100644 index ddf827968..000000000 --- a/launcher/java/JavaCheckerJob.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "JavaChecker.h" -#include "tasks/Task.h" - -class JavaCheckerJob; -using JavaCheckerJobPtr = shared_qobject_ptr; - -// FIXME: this just seems horribly redundant -class JavaCheckerJob : public Task { - Q_OBJECT - public: - explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name){}; - virtual ~JavaCheckerJob(){}; - - bool addJavaCheckerAction(JavaCheckerPtr base) - { - javacheckers.append(base); - // if this is already running, the action needs to be started right away! - if (isRunning()) { - setProgress(num_finished, javacheckers.size()); - connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); - base->performCheck(); - } - return true; - } - QList getResults() { return javaresults; } - - private slots: - void partFinished(JavaCheckResult result); - - protected: - virtual void executeTask() override; - - private: - QString m_job_name; - QList javacheckers; - QList javaresults; - int num_finished = 0; -}; diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index d8be4963f..ef99d6853 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -38,11 +38,13 @@ #include #include +#include -#include "java/JavaCheckerJob.h" +#include "Application.h" +#include "java/JavaChecker.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" -#include "minecraft/VersionFilterData.h" +#include "tasks/ConcurrentTask.h" JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {} @@ -55,7 +57,7 @@ Task::Ptr JavaInstallList::getLoadTask() Task::Ptr JavaInstallList::getCurrentTask() { if (m_status == Status::InProgress) { - return m_loadTask; + return m_load_task; } return nullptr; } @@ -64,8 +66,8 @@ void JavaInstallList::load() { if (m_status != Status::InProgress) { m_status = Status::InProgress; - m_loadTask.reset(new JavaListLoadTask(this)); - m_loadTask->start(); + m_load_task.reset(new JavaListLoadTask(this)); + m_load_task->start(); } } @@ -129,7 +131,7 @@ void JavaInstallList::updateListData(QList versions) } endResetModel(); m_status = Status::Done; - m_loadTask.reset(); + m_load_task.reset(); } bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) @@ -149,11 +151,9 @@ void JavaInstallList::sortVersions() JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task() { m_list = vlist; - m_currentRecommended = NULL; + m_current_recommended = NULL; } -JavaListLoadTask::~JavaListLoadTask() {} - void JavaListLoadTask::executeTask() { setStatus(tr("Detecting Java installations...")); @@ -161,20 +161,17 @@ void JavaListLoadTask::executeTask() JavaUtils ju; QList candidate_paths = ju.FindJavaPaths(); - m_job.reset(new JavaCheckerJob("Java detection")); + ConcurrentTask::Ptr job(new ConcurrentTask(this, "Java detection", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); + m_job.reset(job); connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished); connect(m_job.get(), &Task::progress, this, &Task::setProgress); qDebug() << "Probing the following Java paths: "; int id = 0; for (QString candidate : candidate_paths) { - qDebug() << " " << candidate; - - auto candidate_checker = new JavaChecker(); - candidate_checker->m_path = candidate; - candidate_checker->m_id = id; - m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); - + auto checker = new JavaChecker(candidate, "", 0, 0, 0, id, this); + connect(checker, &JavaChecker::checkFinished, [this](JavaChecker::Result result) { m_results << result; }); + job->addTask(Task::Ptr(checker)); id++; } @@ -184,11 +181,11 @@ void JavaListLoadTask::executeTask() void JavaListLoadTask::javaCheckerFinished() { QList candidates; - auto results = m_job->getResults(); + std::sort(m_results.begin(), m_results.end(), [](JavaChecker::Result a, JavaChecker::Result b) { return a.id < b.id; }); qDebug() << "Found the following valid Java installations:"; - for (JavaCheckResult result : results) { - if (result.validity == JavaCheckResult::Validity::Valid) { + for (auto result : m_results) { + if (result.validity == JavaChecker::Result::Validity::Valid) { JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = result.javaVersion; diff --git a/launcher/java/JavaInstallList.h b/launcher/java/JavaInstallList.h index 1eebadf23..08f0b310d 100644 --- a/launcher/java/JavaInstallList.h +++ b/launcher/java/JavaInstallList.h @@ -19,9 +19,9 @@ #include #include "BaseVersionList.h" +#include "java/JavaChecker.h" #include "tasks/Task.h" -#include "JavaCheckerJob.h" #include "JavaInstall.h" #include "QObjectPtr.h" @@ -53,7 +53,7 @@ class JavaInstallList : public BaseVersionList { protected: Status m_status = Status::NotDone; - shared_qobject_ptr m_loadTask; + shared_qobject_ptr m_load_task; QList m_vlist; }; @@ -62,14 +62,16 @@ class JavaListLoadTask : public Task { public: explicit JavaListLoadTask(JavaInstallList* vlist); - virtual ~JavaListLoadTask(); + virtual ~JavaListLoadTask() = default; + protected: void executeTask() override; public slots: void javaCheckerFinished(); protected: - shared_qobject_ptr m_job; + Task::Ptr m_job; JavaInstallList* m_list; - JavaInstall* m_currentRecommended; + JavaInstall* m_current_recommended; + QList m_results; }; diff --git a/launcher/java/JavaUtils.h b/launcher/java/JavaUtils.h index 616179706..c052ca325 100644 --- a/launcher/java/JavaUtils.h +++ b/launcher/java/JavaUtils.h @@ -15,10 +15,9 @@ #pragma once +#include #include - -#include "JavaChecker.h" -#include "JavaInstallList.h" +#include "java/JavaInstall.h" #ifdef Q_OS_WIN #include diff --git a/launcher/java/providers/AdoptiumJavaDownloader.cpp b/launcher/java/providers/AdoptiumJavaDownloader.cpp new file mode 100644 index 000000000..4f8499d4a --- /dev/null +++ b/launcher/java/providers/AdoptiumJavaDownloader.cpp @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "java/providers/AdoptiumJavaDownloader.h" +#include +#include +#include "MMCZip.h" + +#include "Application.h" +#include "net/NetJob.h" +#include "tasks/Task.h" + +void AdoptiumJavaDownloader::executeTask() +{ + downloadJava(); +}; + +QString AdoptiumJavaDownloader::getArch() const +{ + if (m_os_arch == "arm64") + return "aarch64"; + if (m_os_arch.isEmpty()) + return "x86"; + return m_os_arch; +} + +void AdoptiumJavaDownloader::downloadJava() +{ + // JRE found ! download the zip + setStatus(tr("Downloading Java from Adoptium")); + + auto javaVersion = m_is_legacy ? QString("8") : QString("17"); + auto azulOS = m_os_name == "osx" ? "mac" : m_os_name; + auto arch = getArch(); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", "adoptiumJRE.zip"); + + auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); + download->addNetAction(Net::Download::makeCached( + QString("https://api.adoptium.net/v3/binary/latest/%1/ga/%2/%3/jre/hotspot/normal/eclipse").arg(javaVersion, azulOS, arch), entry)); + auto fullPath = entry->getFullPath(); + + connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); + // connect(download.get(), &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); + connect(download.get(), &NetJob::progress, this, &AdoptiumJavaDownloader::progress); + connect(download.get(), &NetJob::failed, this, &AdoptiumJavaDownloader::emitFailed); + connect(this, &Task::aborted, download.get(), &NetJob::abort); + connect(download.get(), &NetJob::succeeded, [this, fullPath] { + // This should do all of the extracting and creating folders + extractJava(fullPath); + }); + download->start(); +}; + +void AdoptiumJavaDownloader::extractJava(QString input) +{ + setStatus(tr("Extracting java")); + auto zip = std::make_shared(input); + auto files = zip->getFileNameList(); + if (files.isEmpty()) { + emitFailed("Empty archive"); + return; + } + auto zipTask = makeShared(input, m_final_path, files[0]); + + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); + }); + + connect(this, &Task::aborted, zipTask.get(), &Task::abort); + connect(zipTask.get(), &Task::finished, [zipTask, this] { disconnect(this, &Task::aborted, zipTask.get(), &Task::abort); }); + + connect(zipTask.get(), &Task::succeeded, this, &AdoptiumJavaDownloader::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &AdoptiumJavaDownloader::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &AdoptiumJavaDownloader::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + zipTask->start(); +}; + +static const QStringList supportedOs = { + "linux", "windows", "mac", "solaris", "aix", "alpine-linux", +}; + +static const QStringList supportedArch = { + "x64", "x86", "x32", "ppc64", "ppc64le", "s390x", "aarch64", "arm", "sparcv9", "riscv64", +}; + +bool AdoptiumJavaDownloader::isSupported() const +{ + return supportedOs.contains(m_os_name == "osx" ? "mac" : m_os_name) && supportedArch.contains(getArch()); +}; \ No newline at end of file diff --git a/launcher/java/providers/AdoptiumJavaDownloader.h b/launcher/java/providers/AdoptiumJavaDownloader.h new file mode 100644 index 000000000..f0ae6239b --- /dev/null +++ b/launcher/java/providers/AdoptiumJavaDownloader.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "java/providers/BasicJavaDownloader.h" + +class AdoptiumJavaDownloader : public BasicJavaDownloader { + Q_OBJECT + public: + void executeTask() override; + + virtual QString name() const override { return "Adoptium"; }; + virtual bool isSupported() const override; + private slots: + void downloadJava(); + void extractJava(QString input); + + private: + QString getArch() const; +}; \ No newline at end of file diff --git a/launcher/java/providers/AzulJavaDownloader.cpp b/launcher/java/providers/AzulJavaDownloader.cpp new file mode 100644 index 000000000..674da592e --- /dev/null +++ b/launcher/java/providers/AzulJavaDownloader.cpp @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "java/providers/AzulJavaDownloader.h" +#include +#include "MMCZip.h" + +#include "Application.h" +#include "Json.h" +#include "net/NetJob.h" +#include "tasks/Task.h" + +void AzulJavaDownloader::executeTask() +{ + downloadJavaList(); +}; + +void AzulJavaDownloader::downloadJavaList() +{ + setStatus(tr("Querying Azul meta")); + + auto javaVersion = m_is_legacy ? QString("8.0") : QString("17.0"); + auto azulOS = m_os_name == "osx" ? "macos" : m_os_name; + auto arch = getArch(); + auto metaResponse = std::make_shared(); + auto downloadJob = makeShared(QString("JRE::QueryAzulMeta"), APPLICATION->network()); + downloadJob->addNetAction(Net::Download::makeByteArray(QString("https://api.azul.com/metadata/v1/zulu/packages/?" + "java_version=%1" + "&os=%2" + "&arch=%3" + "&archive_type=zip" + "&java_package_type=jre" + "&support_term=lts" + "&latest=true" + "status=ga" + "&availability_types=CA" + "&page=1" + "&page_size=1") + .arg(javaVersion, azulOS, arch), + metaResponse)); + connect(downloadJob.get(), &NetJob::finished, + [downloadJob, metaResponse, this] { disconnect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); }); + connect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); + connect(downloadJob.get(), &NetJob::failed, this, &AzulJavaDownloader::emitFailed); + connect(downloadJob.get(), &NetJob::progress, this, &AzulJavaDownloader::progress); + connect(downloadJob.get(), &NetJob::succeeded, [metaResponse, this] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << *metaResponse; + return; + } + auto array = Json::ensureArray(doc.array()); + if (!array.empty()) { + downloadJava(array); + } else { + emitFailed(tr("No suitable JRE found")); + } + }); + downloadJob->start(); +}; + +QString AzulJavaDownloader::getArch() const +{ + if (m_os_arch == "arm64") + return "aarch64"; + if (m_os_arch == "arm") + return "aarch32"; + if (m_os_arch.isEmpty()) + return "x86"; + return m_os_arch; +} + +void AzulJavaDownloader::downloadJava(const QJsonArray& array) +{ + // JRE found ! download the zip + setStatus(tr("Downloading Java from Azul")); + + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", "azulJRE.zip"); + + auto downloadURL = QUrl(array[0].toObject()["url"].toString()); + auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); + download->addNetAction(Net::Download::makeCached(downloadURL, entry)); + auto fullPath = entry->getFullPath(); + + connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); + // connect(download.get(), &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); + connect(download.get(), &NetJob::progress, this, &AzulJavaDownloader::progress); + connect(download.get(), &NetJob::failed, this, &AzulJavaDownloader::emitFailed); + connect(this, &Task::aborted, download.get(), &NetJob::abort); + connect(download.get(), &NetJob::succeeded, [downloadURL, this, fullPath] { + // This should do all of the extracting and creating folders + extractJava(fullPath, downloadURL.fileName().chopped(4)); + }); + download->start(); +}; + +void AzulJavaDownloader::extractJava(QString input, QString subdirectory) +{ + setStatus(tr("Extracting java")); + auto zipTask = makeShared(input, m_final_path, subdirectory); + + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); + }); + + connect(this, &Task::aborted, zipTask.get(), &Task::abort); + connect(zipTask.get(), &Task::finished, [zipTask, this] { disconnect(this, &Task::aborted, zipTask.get(), &Task::abort); }); + + connect(zipTask.get(), &Task::succeeded, this, &AzulJavaDownloader::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &AzulJavaDownloader::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &AzulJavaDownloader::propagateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + zipTask->start(); +}; + +static const QStringList supportedOs = { + "macos", "linux", "windows", "linux-musl", "linux-glibc", "qnx", "solaris", "aix", +}; + +static const QStringList supportedArch = { + "x86", "x64", "amd64", "i686", "arm", "aarch64", "aarch32", "aarch32sf", "aarch32hf", "ppc", + "ppc64", "ppc32", "ppc32hf", "ppc32spe", "sparc", "sparc64", "sparc32", "sparcv9", "sparcv9-64", "sparcv9-32", +}; + +bool AzulJavaDownloader::isSupported() const +{ + return supportedOs.contains(m_os_name == "osx" ? "macos" : m_os_name) && supportedArch.contains(getArch()); +}; \ No newline at end of file diff --git a/launcher/java/providers/AzulJavaDownloader.h b/launcher/java/providers/AzulJavaDownloader.h new file mode 100644 index 000000000..c4bc8c761 --- /dev/null +++ b/launcher/java/providers/AzulJavaDownloader.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "java/providers/BasicJavaDownloader.h" + +class AzulJavaDownloader : public BasicJavaDownloader { + Q_OBJECT + public: + void executeTask() override; + + virtual QString name() const override { return "Azul"; }; + virtual bool isSupported() const override; + private slots: + void downloadJavaList(); + void downloadJava(const QJsonArray& doc); + void extractJava(QString input, QString subdirectory); + + private: + QString getArch() const; +}; \ No newline at end of file diff --git a/launcher/java/providers/BasicJavaDownloader.cpp b/launcher/java/providers/BasicJavaDownloader.cpp new file mode 100644 index 000000000..9f353d9c1 --- /dev/null +++ b/launcher/java/providers/BasicJavaDownloader.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "java/providers/BasicJavaDownloader.h" + +#include "SysInfo.h" +#include "tasks/Task.h" + +BasicJavaDownloader::BasicJavaDownloader(QString final_path, bool m_is_legacy, QObject* parent) + : Task(parent) + , m_os_name(SysInfo::currentSystem()) + , m_os_arch(SysInfo::useQTForArch()) + , m_final_path(final_path) + , m_is_legacy(m_is_legacy) +{} diff --git a/launcher/java/providers/BasicJavaDownloader.h b/launcher/java/providers/BasicJavaDownloader.h new file mode 100644 index 000000000..34c944ec8 --- /dev/null +++ b/launcher/java/providers/BasicJavaDownloader.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "tasks/Task.h" + +class BasicJavaDownloader : public Task { + Q_OBJECT + public: + BasicJavaDownloader(QString final_path, bool m_is_legacy = false, QObject* parent = nullptr); + virtual ~BasicJavaDownloader() = default; + + [[nodiscard]] bool canAbort() const override { return true; } + + virtual QString name() const = 0; + virtual bool isSupported() const = 0; + + protected: + QString m_os_name; + QString m_os_arch; + QString m_final_path; + bool m_is_legacy; + + Task::Ptr m_current_task; +}; \ No newline at end of file diff --git a/launcher/java/providers/MojangJavaDownloader.cpp b/launcher/java/providers/MojangJavaDownloader.cpp new file mode 100644 index 000000000..fc2a17e94 --- /dev/null +++ b/launcher/java/providers/MojangJavaDownloader.cpp @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "java/providers/MojanglJavaDownloader.h" + +#include "Application.h" +#include "FileSystem.h" +#include "Json.h" +#include "net/ChecksumValidator.h" +#include "net/NetJob.h" + +struct File { + QString path; + QString url; + QByteArray hash; + bool isExec; +}; + +void MojangJavaDownloader::executeTask() +{ + downloadJavaList(); +}; + +void MojangJavaDownloader::downloadJavaList() +{ + auto netJob = makeShared(QString("JRE::QueryVersions"), APPLICATION->network()); + auto response = std::make_shared(); + setStatus(tr("Querying mojang meta")); + netJob->addNetAction(Net::Download::makeByteArray( + QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); + + connect(netJob.get(), &NetJob::finished, [netJob, this] { + // delete so that it's not called on a deleted job + // FIXME: is this needed? qt should handle this + disconnect(this, &Task::aborted, netJob.get(), &NetJob::abort); + }); + connect(this, &Task::aborted, netJob.get(), &NetJob::abort); + + connect(netJob.get(), &NetJob::progress, this, &MojangJavaDownloader::progress); + connect(netJob.get(), &NetJob::failed, this, &MojangJavaDownloader::emitFailed); + connect(netJob.get(), &NetJob::succeeded, [response, this, netJob] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << *response; + emitFailed(parse_error.errorString()); + return; + } + auto versionArray = Json::ensureArray(Json::ensureObject(doc.object(), getOS()), m_is_legacy ? "jre-legacy" : "java-runtime-gamma"); + if (!versionArray.empty()) { + parseManifest(versionArray); + + } else { + // mojang does not have a JRE for us, so fail + emitFailed("No suitable JRE found"); + } + }); + + netJob->start(); +}; + +QString MojangJavaDownloader::getOS() const +{ + if (m_os_name == "windows") { + if (m_os_arch == "x86_64") { + return "windows-x64"; + } + if (m_os_arch == "i386") { + return "windows-x86"; + } + // Unknown, maybe arm, appending arch for downloader + return "windows-" + m_os_arch; + } + if (m_os_name == "osx") { + if (m_os_arch == "arm64") { + return "mac-os-arm64"; + } + return "mac-os"; + } + if (m_os_name == "linux") { + if (m_os_arch == "x86_64") { + return "linux"; + } + // will work for i386, and arm(64) + return "linux-" + m_os_arch; + } + return {}; +} +void MojangJavaDownloader::parseManifest(const QJsonArray& versionArray) +{ + setStatus(tr("Downloading Java from Mojang")); + auto url = Json::ensureString(Json::ensureObject(Json::ensureObject(versionArray[0]), "manifest"), "url"); + auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); + auto files = std::make_shared(); + + download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); + + connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); + connect(download.get(), &NetJob::progress, this, &MojangJavaDownloader::progress); + connect(download.get(), &NetJob::failed, this, &MojangJavaDownloader::emitFailed); + connect(this, &Task::aborted, download.get(), &NetJob::abort); + + connect(download.get(), &NetJob::succeeded, [files, this] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << *files; + emitFailed(parse_error.errorString()); + return; + } + downloadJava(doc); + }); + download->start(); +}; + +void MojangJavaDownloader::downloadJava(const QJsonDocument& doc) +{ + // valid json doc, begin making jre spot + FS::ensureFolderPathExists(m_final_path); + std::vector toDownload; + auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files"); + for (const auto& paths : list.keys()) { + auto file = FS::PathCombine(m_final_path, paths); + + const QJsonObject& meta = Json::ensureObject(list, paths); + auto type = Json::ensureString(meta, "type"); + if (type == "directory") { + FS::ensureFolderPathExists(file); + } else if (type == "link") { + // this is linux only ! + auto path = Json::ensureString(meta, "target"); + if (!path.isEmpty()) { + auto target = FS::PathCombine(file, "../" + path); + QFile(target).link(file); + } + } else if (type == "file") { + // TODO download compressed version if it exists ? + auto raw = Json::ensureObject(Json::ensureObject(meta, "downloads"), "raw"); + auto isExec = Json::ensureBoolean(meta, "executable", false); + auto url = Json::ensureString(raw, "url"); + if (!url.isEmpty() && QUrl(url).isValid()) { + auto f = File{ file, url, QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec }; + toDownload.push_back(f); + } + } + } + auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); + for (const auto& file : toDownload) { + auto dl = Net::Download::makeFile(file.url, file.path); + if (!file.hash.isEmpty()) { + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); + } + if (file.isExec) { + connect(dl.get(), &Net::Download::succeeded, + [file] { QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); }); + } + elementDownload->addNetAction(dl); + } + connect(elementDownload, &NetJob::finished, [elementDownload, this] { + disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); + elementDownload->deleteLater(); + }); + connect(elementDownload, &NetJob::progress, this, &MojangJavaDownloader::progress); + connect(elementDownload, &NetJob::failed, this, &MojangJavaDownloader::emitFailed); + + connect(this, &Task::aborted, elementDownload, &NetJob::abort); + connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); + elementDownload->start(); +}; diff --git a/launcher/java/providers/MojanglJavaDownloader.h b/launcher/java/providers/MojanglJavaDownloader.h new file mode 100644 index 000000000..a1b5b1765 --- /dev/null +++ b/launcher/java/providers/MojanglJavaDownloader.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "java/providers/BasicJavaDownloader.h" + +class MojangJavaDownloader : public BasicJavaDownloader { + Q_OBJECT + public: + void executeTask() override; + + virtual QString name() const override { return "Mojang"; }; + virtual bool isSupported() const override { return !getOS().isEmpty(); }; + private slots: + void downloadJavaList(); + void parseManifest(const QJsonArray& versionArray); + void downloadJava(const QJsonDocument& doc); + + private: + QString getOS() const; +}; \ No newline at end of file diff --git a/launcher/launch/steps/CheckJava.cpp b/launcher/launch/steps/CheckJava.cpp index 81337a88e..c776092fe 100644 --- a/launcher/launch/steps/CheckJava.cpp +++ b/launcher/launch/steps/CheckJava.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include "java/JavaUtils.h" @@ -90,11 +91,10 @@ void CheckJava::executeTask() // if timestamps are not the same, or something is missing, check! if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0 || storedVendor.size() == 0) { - m_JavaChecker.reset(new JavaChecker); + m_JavaChecker.reset(new JavaChecker(realJavaPath, "", 0, 0, 0, 0, this)); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); - m_JavaChecker->m_path = realJavaPath; - m_JavaChecker->performCheck(); + m_JavaChecker->start(); return; } else { auto verString = instance->settings()->get("JavaVersion").toString(); @@ -106,10 +106,10 @@ void CheckJava::executeTask() emitSucceeded(); } -void CheckJava::checkJavaFinished(JavaCheckResult result) +void CheckJava::checkJavaFinished(JavaChecker::Result result) { switch (result.validity) { - case JavaCheckResult::Validity::Errored: { + case JavaChecker::Result::Validity::Errored: { // Error message displayed if java can't start emit logLine(QString("Could not start java:"), MessageLevel::Error); emit logLines(result.errorLog.split('\n'), MessageLevel::Error); @@ -117,14 +117,14 @@ void CheckJava::checkJavaFinished(JavaCheckResult result) emitFailed(QString("Could not start java!")); return; } - case JavaCheckResult::Validity::ReturnedInvalidData: { + case JavaChecker::Result::Validity::ReturnedInvalidData: { emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error); emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher); emitSucceeded(); return; } - case JavaCheckResult::Validity::Valid: { + case JavaChecker::Result::Validity::Valid: { auto instance = m_parent->instance(); printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor); instance->settings()->set("JavaVersion", result.javaVersion.toString()); diff --git a/launcher/launch/steps/CheckJava.h b/launcher/launch/steps/CheckJava.h index 4436e2a55..5ba9e4018 100644 --- a/launcher/launch/steps/CheckJava.h +++ b/launcher/launch/steps/CheckJava.h @@ -28,7 +28,7 @@ class CheckJava : public LaunchStep { virtual void executeTask(); virtual bool canAbort() const { return false; } private slots: - void checkJavaFinished(JavaCheckResult result); + void checkJavaFinished(JavaChecker::Result result); private: void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor); @@ -37,5 +37,5 @@ class CheckJava : public LaunchStep { private: QString m_javaPath; QString m_javaSignature; - JavaCheckerPtr m_JavaChecker; + JavaChecker::Ptr m_JavaChecker; }; diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 63d7aa57c..be6b195dc 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -14,7 +14,9 @@ #include "FileSystem.h" #include "JavaCommon.h" #include "JavaDownloader.h" +#include "java/JavaChecker.h" #include "java/JavaInstall.h" +#include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "ui/dialogs/CustomMessageBox.h" @@ -368,30 +370,25 @@ void JavaSettingsWidget::checkJavaPath(const QString& path) return; } setJavaStatus(JavaStatus::Pending); - m_checker.reset(new JavaChecker()); - m_checker->m_path = path; - m_checker->m_minMem = minHeapSize(); - m_checker->m_maxMem = maxHeapSize(); - if (m_permGenSpinBox->isVisible()) { - m_checker->m_permGen = m_permGenSpinBox->value(); - } + m_checker.reset( + new JavaChecker(path, "", minHeapSize(), maxHeapSize(), m_permGenSpinBox->isVisible() ? m_permGenSpinBox->value() : 0, 0, this)); connect(m_checker.get(), &JavaChecker::checkFinished, this, &JavaSettingsWidget::checkFinished); - m_checker->performCheck(); + m_checker->start(); } -void JavaSettingsWidget::checkFinished(JavaCheckResult result) +void JavaSettingsWidget::checkFinished(JavaChecker::Result result) { m_result = result; switch (result.validity) { - case JavaCheckResult::Validity::Valid: { + case JavaChecker::Result::Validity::Valid: { setJavaStatus(JavaStatus::Good); break; } - case JavaCheckResult::Validity::ReturnedInvalidData: { + case JavaChecker::Result::Validity::ReturnedInvalidData: { setJavaStatus(JavaStatus::ReturnedInvalidData); break; } - case JavaCheckResult::Validity::Errored: { + case JavaChecker::Result::Validity::Errored: { setJavaStatus(JavaStatus::DoesNotStart); break; } diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 0e05bf341..d3cd2c5a7 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -51,7 +51,7 @@ class JavaSettingsWidget : public QWidget { void on_javaBrowseBtn_clicked(); void on_javaStatusBtn_clicked(); void on_javaDownloadBtn_clicked(); - void checkFinished(JavaCheckResult result); + void checkFinished(JavaChecker::Result result); protected: /* methods */ void checkJavaPathOnEdit(const QString& path); @@ -89,5 +89,5 @@ class JavaSettingsWidget : public QWidget { QString queuedCheck; uint64_t m_availableMemory = 0ull; shared_qobject_ptr m_checker; - JavaCheckResult m_result; + JavaChecker::Result m_result; }; From e89703238365cbcca8cce19b923a28bbb2057516 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 25 Jan 2024 22:22:21 +0200 Subject: [PATCH 211/755] Added java metadata Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 3 +++ launcher/java/JavaVersion.cpp | 20 +++++++++++++++++++ launcher/java/JavaVersion.h | 4 ++++ launcher/minecraft/MojangVersionFormat.cpp | 6 ++++++ launcher/minecraft/OneSixVersionFormat.cpp | 14 +++++++++++++ launcher/minecraft/VersionFile.h | 8 ++++++++ .../ui/pages/instance/InstanceSettingsPage.ui | 2 -- 7 files changed, 55 insertions(+), 2 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index e735f2081..2af33178d 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -430,6 +430,9 @@ set(JAVA_SOURCES java/JavaUtils.cpp java/JavaVersion.h java/JavaVersion.cpp + + java/JavaRuntime.h + java/JavaRuntime.cpp ) set(TRANSLATIONS_SOURCES diff --git a/launcher/java/JavaVersion.cpp b/launcher/java/JavaVersion.cpp index b77bf2adf..3de6f5ad6 100644 --- a/launcher/java/JavaVersion.cpp +++ b/launcher/java/JavaVersion.cpp @@ -109,3 +109,23 @@ bool JavaVersion::operator>(const JavaVersion& rhs) { return (!operator<(rhs)) && (!operator==(rhs)); } + +JavaVersion::JavaVersion(int major, int minor, int security, int build, QString name) + : m_major(major), m_minor(minor), m_security(security), m_name(name), m_parseable(true) +{ + if (build != 0) { + m_prerelease = QString::number(build); + m_string = m_prerelease; + } + if (m_security != 0) + m_string = QString::number(m_security) + "." + m_string; + else if (!m_string.isEmpty()) { + m_string = "0." + m_string; + } + if (m_minor != 0) + m_string = QString::number(m_minor) + "." + m_string; + else if (!m_string.isEmpty()) { + m_string = "0." + m_string; + } + m_string = QString::number(m_major) + "." + m_string; +} diff --git a/launcher/java/JavaVersion.h b/launcher/java/JavaVersion.h index 421578ea1..eee992346 100644 --- a/launcher/java/JavaVersion.h +++ b/launcher/java/JavaVersion.h @@ -16,6 +16,7 @@ class JavaVersion { public: JavaVersion() {} JavaVersion(const QString& rhs); + JavaVersion(int major, int minor, int security, int build = 0, QString name = ""); JavaVersion& operator=(const QString& rhs); @@ -32,12 +33,15 @@ class JavaVersion { int major() { return m_major; } int minor() { return m_minor; } int security() { return m_security; } + QString build() { return m_prerelease; } + QString name() { return m_name; } private: QString m_string; int m_major = 0; int m_minor = 0; int m_security = 0; + QString m_name = ""; bool m_parseable = false; QString m_prerelease; }; diff --git a/launcher/minecraft/MojangVersionFormat.cpp b/launcher/minecraft/MojangVersionFormat.cpp index bb782e47f..d17a3a21f 100644 --- a/launcher/minecraft/MojangVersionFormat.cpp +++ b/launcher/minecraft/MojangVersionFormat.cpp @@ -185,6 +185,9 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi out->compatibleJavaMajors.append(requireInteger(compatible)); } } + if (in.contains("compatibleJavaName")) { + out->compatibleJavaName = requireString(in.value("compatibleJavaName")); + } if (in.contains("downloads")) { auto downloadsObj = requireObject(in, "downloads"); @@ -259,6 +262,9 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj } out.insert("compatibleJavaMajors", compatibleJavaMajorsOut); } + if (!in->compatibleJavaName.isEmpty()) { + writeString(out, "compatibleJavaName", in->compatibleJavaName); + } } QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch) diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index 306c95a6a..5f3b4f2a2 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -36,6 +36,8 @@ #include "OneSixVersionFormat.h" #include #include +#include +#include "java/JavaRuntime.h" #include "minecraft/Agent.h" #include "minecraft/ParseUtils.h" @@ -255,6 +257,18 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc out->m_volatile = requireBoolean(root, "volatile"); } + if (root.contains("runtimes")) { + auto runtimes = requireObject(root, "runtimes"); + out->runtimes = {}; + for (auto key : runtimes.keys()) { + QList list; + for (auto runtime : ensureArray(runtimes, key)) { + list.append(JavaRuntime::parseJavaMeta(ensureObject(runtime))); + } + out->runtimes[key] = list; + } + } + /* removed features that shouldn't be used */ if (root.contains("tweakers")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'")); diff --git a/launcher/minecraft/VersionFile.h b/launcher/minecraft/VersionFile.h index 280e35ee3..297c19709 100644 --- a/launcher/minecraft/VersionFile.h +++ b/launcher/minecraft/VersionFile.h @@ -36,6 +36,8 @@ #pragma once #include +#include +#include #include #include #include @@ -45,6 +47,7 @@ #include "Agent.h" #include "Library.h" #include "ProblemProvider.h" +#include "java/JavaRuntime.h" #include "minecraft/Rule.h" class PackProfile; @@ -98,6 +101,9 @@ class VersionFile : public ProblemContainer { /// Mojang: list of compatible java majors QList compatibleJavaMajors; + /// Mojang: the name of recomended java version + QString compatibleJavaName; + /// Mojang: type of the Minecraft version QString type; @@ -149,6 +155,8 @@ class VersionFile : public ProblemContainer { /// is volatile -- may be removed as soon as it is no longer needed by something else bool m_volatile = false; + QHash> runtimes; + public: // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more. QMap> mojangDownloads; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index cb37366cc..7762ca8a7 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -796,8 +796,6 @@ enableFeralGamemodeCheck enableMangoHud useDiscreteGpuCheck - modLoaderSettingsGroupBox - disableQuiltBeaconCheckBox gameTimeGroupBox serverJoinGroupBox serverJoinAddress From f36be3f0e3a32cb5f5711a727ecd07cf1c28b8dd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 25 Jan 2024 22:58:12 +0200 Subject: [PATCH 212/755] Cleanup downloaders Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 4 + launcher/java/JavaRuntime.cpp | 56 ++++++ launcher/java/JavaRuntime.h | 30 ++++ .../ArchiveJavaDownloader.cpp} | 53 ++---- .../ArchiveJavaDownloader.h} | 16 +- .../ManifestJavaDownloader.cpp} | 93 ++-------- .../ManifestJavaDownloader.h} | 20 ++- .../java/providers/AdoptiumJavaDownloader.h | 36 ---- .../java/providers/AzulJavaDownloader.cpp | 159 ------------------ launcher/java/providers/AzulJavaDownloader.h | 37 ---- .../java/providers/BasicJavaDownloader.cpp | 29 ---- 11 files changed, 132 insertions(+), 401 deletions(-) create mode 100644 launcher/java/JavaRuntime.cpp create mode 100644 launcher/java/JavaRuntime.h rename launcher/java/{providers/AdoptiumJavaDownloader.cpp => download/ArchiveJavaDownloader.cpp} (63%) rename launcher/java/{providers/BasicJavaDownloader.h => download/ArchiveJavaDownloader.h} (73%) rename launcher/java/{providers/MojangJavaDownloader.cpp => download/ManifestJavaDownloader.cpp} (55%) rename launcher/java/{providers/MojanglJavaDownloader.h => download/ManifestJavaDownloader.h} (70%) delete mode 100644 launcher/java/providers/AdoptiumJavaDownloader.h delete mode 100644 launcher/java/providers/AzulJavaDownloader.cpp delete mode 100644 launcher/java/providers/AzulJavaDownloader.h delete mode 100644 launcher/java/providers/BasicJavaDownloader.cpp diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 2af33178d..ac3f8eb68 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -433,6 +433,10 @@ set(JAVA_SOURCES java/JavaRuntime.h java/JavaRuntime.cpp + java/download/ArchiveJavaDownloader.cpp + java/download/ArchiveJavaDownloader.h + java/download/ManifestJavaDownloader.cpp + java/download/ManifestJavaDownloader.h ) set(TRANSLATIONS_SOURCES diff --git a/launcher/java/JavaRuntime.cpp b/launcher/java/JavaRuntime.cpp new file mode 100644 index 000000000..0de8c839b --- /dev/null +++ b/launcher/java/JavaRuntime.cpp @@ -0,0 +1,56 @@ +#include "java/JavaRuntime.h" + +#include + +#include "Json.h" +#include "java/JavaVersion.h" +#include "minecraft/ParseUtils.h" + +namespace JavaRuntime { + +DownloadType parseDownloadType(QString javaDownload) +{ + if (javaDownload == "manifest") + return DownloadType::Manifest; + // if (javaDownload == "archive") + return DownloadType::Archive; +} +QString downloadTypeToString(DownloadType javaDownload) +{ + switch (javaDownload) { + case DownloadType::Manifest: + return "manifest"; + case DownloadType::Archive: + return "archive"; + } +} +MetaPtr parseJavaMeta(const QJsonObject& in) +{ + auto meta = std::make_shared(); + + meta->name = Json::ensureString(in, "name", ""); + meta->vendor = Json::ensureString(in, "vendor", ""); + meta->url = Json::ensureString(in, "url", ""); + meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", "")); + meta->recommended = Json::ensureBoolean(in, "recommended", false); + meta->downloadType = parseDownloadType(Json::ensureString(in, "downloadType", "")); + meta->packageType = Json::ensureString(in, "packageType", ""); + + if (in.contains("checksum")) { + auto obj = Json::requireObject(in, "checksum"); + meta->checksumHash = Json::ensureString(obj, "hash", ""); + meta->checksumType = Json::ensureString(obj, "type", ""); + } + + if (in.contains("version")) { + auto obj = Json::requireObject(in, "checksum"); + auto name = Json::ensureString(obj, "name", ""); + auto major = Json::ensureInteger(obj, "major", 0); + auto minor = Json::ensureInteger(obj, "minor", 0); + auto security = Json::ensureInteger(obj, "security", 0); + auto build = Json::ensureInteger(obj, "build", 0); + meta->version = JavaVersion(major, minor, security, build, name); + } + return meta; +} +} // namespace JavaRuntime diff --git a/launcher/java/JavaRuntime.h b/launcher/java/JavaRuntime.h new file mode 100644 index 000000000..1ea5d2e48 --- /dev/null +++ b/launcher/java/JavaRuntime.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "java/JavaVersion.h" + +namespace JavaRuntime { + +enum class DownloadType { Manifest, Archive }; + +struct Meta { + QString name; + QString vendor; + QString url; + QDateTime releaseTime; + QString checksumType; + QString checksumHash; + bool recommended; + DownloadType downloadType; + QString packageType; + JavaVersion version; +}; +using MetaPtr = std::shared_ptr; + +DownloadType parseDownloadType(QString javaDownload); +QString downloadTypeToString(DownloadType javaDownload); +MetaPtr parseJavaMeta(const QJsonObject& libObj); + +} // namespace JavaRuntime \ No newline at end of file diff --git a/launcher/java/providers/AdoptiumJavaDownloader.cpp b/launcher/java/download/ArchiveJavaDownloader.cpp similarity index 63% rename from launcher/java/providers/AdoptiumJavaDownloader.cpp rename to launcher/java/download/ArchiveJavaDownloader.cpp index 4f8499d4a..844be1dce 100644 --- a/launcher/java/providers/AdoptiumJavaDownloader.cpp +++ b/launcher/java/download/ArchiveJavaDownloader.cpp @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "java/providers/AdoptiumJavaDownloader.h" +#include "java/download/ArchiveJavaDownloader.h" #include #include #include "MMCZip.h" @@ -24,39 +24,21 @@ #include "net/NetJob.h" #include "tasks/Task.h" -void AdoptiumJavaDownloader::executeTask() -{ - downloadJava(); -}; - -QString AdoptiumJavaDownloader::getArch() const -{ - if (m_os_arch == "arm64") - return "aarch64"; - if (m_os_arch.isEmpty()) - return "x86"; - return m_os_arch; -} - -void AdoptiumJavaDownloader::downloadJava() +void ArchiveJavaDownloader::executeTask() { // JRE found ! download the zip - setStatus(tr("Downloading Java from Adoptium")); + setStatus(tr("Downloading Java")); - auto javaVersion = m_is_legacy ? QString("8") : QString("17"); - auto azulOS = m_os_name == "osx" ? "mac" : m_os_name; - auto arch = getArch(); - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", "adoptiumJRE.zip"); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.toLocalFile()); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); - download->addNetAction(Net::Download::makeCached( - QString("https://api.adoptium.net/v3/binary/latest/%1/ga/%2/%3/jre/hotspot/normal/eclipse").arg(javaVersion, azulOS, arch), entry)); + download->addNetAction(Net::Download::makeCached(m_url, entry)); auto fullPath = entry->getFullPath(); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); // connect(download.get(), &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); - connect(download.get(), &NetJob::progress, this, &AdoptiumJavaDownloader::progress); - connect(download.get(), &NetJob::failed, this, &AdoptiumJavaDownloader::emitFailed); + connect(download.get(), &NetJob::progress, this, &ArchiveJavaDownloader::progress); + connect(download.get(), &NetJob::failed, this, &ArchiveJavaDownloader::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); connect(download.get(), &NetJob::succeeded, [this, fullPath] { // This should do all of the extracting and creating folders @@ -65,7 +47,7 @@ void AdoptiumJavaDownloader::downloadJava() download->start(); }; -void AdoptiumJavaDownloader::extractJava(QString input) +void ArchiveJavaDownloader::extractJava(QString input) { setStatus(tr("Extracting java")); auto zip = std::make_shared(input); @@ -85,14 +67,14 @@ void AdoptiumJavaDownloader::extractJava(QString input) connect(this, &Task::aborted, zipTask.get(), &Task::abort); connect(zipTask.get(), &Task::finished, [zipTask, this] { disconnect(this, &Task::aborted, zipTask.get(), &Task::abort); }); - connect(zipTask.get(), &Task::succeeded, this, &AdoptiumJavaDownloader::emitSucceeded); - connect(zipTask.get(), &Task::aborted, this, &AdoptiumJavaDownloader::emitAborted); + connect(zipTask.get(), &Task::succeeded, this, &ArchiveJavaDownloader::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &ArchiveJavaDownloader::emitAborted); connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); - connect(zipTask.get(), &Task::stepProgress, this, &AdoptiumJavaDownloader::propagateStepProgress); + connect(zipTask.get(), &Task::stepProgress, this, &ArchiveJavaDownloader::propagateStepProgress); connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); @@ -104,16 +86,3 @@ void AdoptiumJavaDownloader::extractJava(QString input) }); zipTask->start(); }; - -static const QStringList supportedOs = { - "linux", "windows", "mac", "solaris", "aix", "alpine-linux", -}; - -static const QStringList supportedArch = { - "x64", "x86", "x32", "ppc64", "ppc64le", "s390x", "aarch64", "arm", "sparcv9", "riscv64", -}; - -bool AdoptiumJavaDownloader::isSupported() const -{ - return supportedOs.contains(m_os_name == "osx" ? "mac" : m_os_name) && supportedArch.contains(getArch()); -}; \ No newline at end of file diff --git a/launcher/java/providers/BasicJavaDownloader.h b/launcher/java/download/ArchiveJavaDownloader.h similarity index 73% rename from launcher/java/providers/BasicJavaDownloader.h rename to launcher/java/download/ArchiveJavaDownloader.h index 34c944ec8..d17657086 100644 --- a/launcher/java/providers/BasicJavaDownloader.h +++ b/launcher/java/download/ArchiveJavaDownloader.h @@ -18,24 +18,24 @@ #pragma once +#include #include "tasks/Task.h" -class BasicJavaDownloader : public Task { +class ArchiveJavaDownloader : public Task { Q_OBJECT public: - BasicJavaDownloader(QString final_path, bool m_is_legacy = false, QObject* parent = nullptr); - virtual ~BasicJavaDownloader() = default; + ArchiveJavaDownloader(QUrl url, QString final_path); + virtual ~ArchiveJavaDownloader() = default; [[nodiscard]] bool canAbort() const override { return true; } + void executeTask() override; - virtual QString name() const = 0; - virtual bool isSupported() const = 0; + private slots: + void extractJava(QString input); protected: - QString m_os_name; - QString m_os_arch; + QUrl m_url; QString m_final_path; - bool m_is_legacy; Task::Ptr m_current_task; }; \ No newline at end of file diff --git a/launcher/java/providers/MojangJavaDownloader.cpp b/launcher/java/download/ManifestJavaDownloader.cpp similarity index 55% rename from launcher/java/providers/MojangJavaDownloader.cpp rename to launcher/java/download/ManifestJavaDownloader.cpp index fc2a17e94..4f7dab94e 100644 --- a/launcher/java/providers/MojangJavaDownloader.cpp +++ b/launcher/java/download/ManifestJavaDownloader.cpp @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "java/providers/MojanglJavaDownloader.h" +#include "java/download/ManifestJavaDownloader.h" #include "Application.h" #include "FileSystem.h" @@ -30,89 +30,18 @@ struct File { bool isExec; }; -void MojangJavaDownloader::executeTask() +ManifestJavaDownloader::ManifestJavaDownloader(QUrl url, QString final_path) : m_url(url), m_final_path(final_path){}; +void ManifestJavaDownloader::executeTask() { - downloadJavaList(); -}; - -void MojangJavaDownloader::downloadJavaList() -{ - auto netJob = makeShared(QString("JRE::QueryVersions"), APPLICATION->network()); - auto response = std::make_shared(); - setStatus(tr("Querying mojang meta")); - netJob->addNetAction(Net::Download::makeByteArray( - QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); - - connect(netJob.get(), &NetJob::finished, [netJob, this] { - // delete so that it's not called on a deleted job - // FIXME: is this needed? qt should handle this - disconnect(this, &Task::aborted, netJob.get(), &NetJob::abort); - }); - connect(this, &Task::aborted, netJob.get(), &NetJob::abort); - - connect(netJob.get(), &NetJob::progress, this, &MojangJavaDownloader::progress); - connect(netJob.get(), &NetJob::failed, this, &MojangJavaDownloader::emitFailed); - connect(netJob.get(), &NetJob::succeeded, [response, this, netJob] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *response; - emitFailed(parse_error.errorString()); - return; - } - auto versionArray = Json::ensureArray(Json::ensureObject(doc.object(), getOS()), m_is_legacy ? "jre-legacy" : "java-runtime-gamma"); - if (!versionArray.empty()) { - parseManifest(versionArray); - - } else { - // mojang does not have a JRE for us, so fail - emitFailed("No suitable JRE found"); - } - }); - - netJob->start(); -}; - -QString MojangJavaDownloader::getOS() const -{ - if (m_os_name == "windows") { - if (m_os_arch == "x86_64") { - return "windows-x64"; - } - if (m_os_arch == "i386") { - return "windows-x86"; - } - // Unknown, maybe arm, appending arch for downloader - return "windows-" + m_os_arch; - } - if (m_os_name == "osx") { - if (m_os_arch == "arm64") { - return "mac-os-arm64"; - } - return "mac-os"; - } - if (m_os_name == "linux") { - if (m_os_arch == "x86_64") { - return "linux"; - } - // will work for i386, and arm(64) - return "linux-" + m_os_arch; - } - return {}; -} -void MojangJavaDownloader::parseManifest(const QJsonArray& versionArray) -{ - setStatus(tr("Downloading Java from Mojang")); - auto url = Json::ensureString(Json::ensureObject(Json::ensureObject(versionArray[0]), "manifest"), "url"); + setStatus(tr("Downloading Java")); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); auto files = std::make_shared(); - download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); + download->addNetAction(Net::Download::makeByteArray(m_url, files)); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download.get(), &NetJob::progress, this, &MojangJavaDownloader::progress); - connect(download.get(), &NetJob::failed, this, &MojangJavaDownloader::emitFailed); + connect(download.get(), &NetJob::progress, this, &ManifestJavaDownloader::progress); + connect(download.get(), &NetJob::failed, this, &ManifestJavaDownloader::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); connect(download.get(), &NetJob::succeeded, [files, this] { @@ -129,7 +58,7 @@ void MojangJavaDownloader::parseManifest(const QJsonArray& versionArray) download->start(); }; -void MojangJavaDownloader::downloadJava(const QJsonDocument& doc) +void ManifestJavaDownloader::downloadJava(const QJsonDocument& doc) { // valid json doc, begin making jre spot FS::ensureFolderPathExists(m_final_path); @@ -176,10 +105,10 @@ void MojangJavaDownloader::downloadJava(const QJsonDocument& doc) disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); elementDownload->deleteLater(); }); - connect(elementDownload, &NetJob::progress, this, &MojangJavaDownloader::progress); - connect(elementDownload, &NetJob::failed, this, &MojangJavaDownloader::emitFailed); + connect(elementDownload, &NetJob::progress, this, &ManifestJavaDownloader::progress); + connect(elementDownload, &NetJob::failed, this, &ManifestJavaDownloader::emitFailed); connect(this, &Task::aborted, elementDownload, &NetJob::abort); connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); elementDownload->start(); -}; +}; \ No newline at end of file diff --git a/launcher/java/providers/MojanglJavaDownloader.h b/launcher/java/download/ManifestJavaDownloader.h similarity index 70% rename from launcher/java/providers/MojanglJavaDownloader.h rename to launcher/java/download/ManifestJavaDownloader.h index a1b5b1765..44e9ed9de 100644 --- a/launcher/java/providers/MojanglJavaDownloader.h +++ b/launcher/java/download/ManifestJavaDownloader.h @@ -18,20 +18,24 @@ #pragma once -#include "java/providers/BasicJavaDownloader.h" +#include +#include "tasks/Task.h" -class MojangJavaDownloader : public BasicJavaDownloader { +class ManifestJavaDownloader : public Task { Q_OBJECT public: + ManifestJavaDownloader(QUrl url, QString final_path); + virtual ~ManifestJavaDownloader() = default; + + [[nodiscard]] bool canAbort() const override { return true; } void executeTask() override; - virtual QString name() const override { return "Mojang"; }; - virtual bool isSupported() const override { return !getOS().isEmpty(); }; private slots: - void downloadJavaList(); - void parseManifest(const QJsonArray& versionArray); void downloadJava(const QJsonDocument& doc); - private: - QString getOS() const; + protected: + QUrl m_url; + QString m_final_path; + + Task::Ptr m_current_task; }; \ No newline at end of file diff --git a/launcher/java/providers/AdoptiumJavaDownloader.h b/launcher/java/providers/AdoptiumJavaDownloader.h deleted file mode 100644 index f0ae6239b..000000000 --- a/launcher/java/providers/AdoptiumJavaDownloader.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "java/providers/BasicJavaDownloader.h" - -class AdoptiumJavaDownloader : public BasicJavaDownloader { - Q_OBJECT - public: - void executeTask() override; - - virtual QString name() const override { return "Adoptium"; }; - virtual bool isSupported() const override; - private slots: - void downloadJava(); - void extractJava(QString input); - - private: - QString getArch() const; -}; \ No newline at end of file diff --git a/launcher/java/providers/AzulJavaDownloader.cpp b/launcher/java/providers/AzulJavaDownloader.cpp deleted file mode 100644 index 674da592e..000000000 --- a/launcher/java/providers/AzulJavaDownloader.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "java/providers/AzulJavaDownloader.h" -#include -#include "MMCZip.h" - -#include "Application.h" -#include "Json.h" -#include "net/NetJob.h" -#include "tasks/Task.h" - -void AzulJavaDownloader::executeTask() -{ - downloadJavaList(); -}; - -void AzulJavaDownloader::downloadJavaList() -{ - setStatus(tr("Querying Azul meta")); - - auto javaVersion = m_is_legacy ? QString("8.0") : QString("17.0"); - auto azulOS = m_os_name == "osx" ? "macos" : m_os_name; - auto arch = getArch(); - auto metaResponse = std::make_shared(); - auto downloadJob = makeShared(QString("JRE::QueryAzulMeta"), APPLICATION->network()); - downloadJob->addNetAction(Net::Download::makeByteArray(QString("https://api.azul.com/metadata/v1/zulu/packages/?" - "java_version=%1" - "&os=%2" - "&arch=%3" - "&archive_type=zip" - "&java_package_type=jre" - "&support_term=lts" - "&latest=true" - "status=ga" - "&availability_types=CA" - "&page=1" - "&page_size=1") - .arg(javaVersion, azulOS, arch), - metaResponse)); - connect(downloadJob.get(), &NetJob::finished, - [downloadJob, metaResponse, this] { disconnect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); }); - connect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); - connect(downloadJob.get(), &NetJob::failed, this, &AzulJavaDownloader::emitFailed); - connect(downloadJob.get(), &NetJob::progress, this, &AzulJavaDownloader::progress); - connect(downloadJob.get(), &NetJob::succeeded, [metaResponse, this] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *metaResponse; - return; - } - auto array = Json::ensureArray(doc.array()); - if (!array.empty()) { - downloadJava(array); - } else { - emitFailed(tr("No suitable JRE found")); - } - }); - downloadJob->start(); -}; - -QString AzulJavaDownloader::getArch() const -{ - if (m_os_arch == "arm64") - return "aarch64"; - if (m_os_arch == "arm") - return "aarch32"; - if (m_os_arch.isEmpty()) - return "x86"; - return m_os_arch; -} - -void AzulJavaDownloader::downloadJava(const QJsonArray& array) -{ - // JRE found ! download the zip - setStatus(tr("Downloading Java from Azul")); - - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", "azulJRE.zip"); - - auto downloadURL = QUrl(array[0].toObject()["url"].toString()); - auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); - download->addNetAction(Net::Download::makeCached(downloadURL, entry)); - auto fullPath = entry->getFullPath(); - - connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - // connect(download.get(), &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); - connect(download.get(), &NetJob::progress, this, &AzulJavaDownloader::progress); - connect(download.get(), &NetJob::failed, this, &AzulJavaDownloader::emitFailed); - connect(this, &Task::aborted, download.get(), &NetJob::abort); - connect(download.get(), &NetJob::succeeded, [downloadURL, this, fullPath] { - // This should do all of the extracting and creating folders - extractJava(fullPath, downloadURL.fileName().chopped(4)); - }); - download->start(); -}; - -void AzulJavaDownloader::extractJava(QString input, QString subdirectory) -{ - setStatus(tr("Extracting java")); - auto zipTask = makeShared(input, m_final_path, subdirectory); - - auto progressStep = std::make_shared(); - connect(zipTask.get(), &Task::finished, this, [this, progressStep] { - progressStep->state = TaskStepState::Succeeded; - stepProgress(*progressStep); - }); - - connect(this, &Task::aborted, zipTask.get(), &Task::abort); - connect(zipTask.get(), &Task::finished, [zipTask, this] { disconnect(this, &Task::aborted, zipTask.get(), &Task::abort); }); - - connect(zipTask.get(), &Task::succeeded, this, &AzulJavaDownloader::emitSucceeded); - connect(zipTask.get(), &Task::aborted, this, &AzulJavaDownloader::emitAborted); - connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { - progressStep->state = TaskStepState::Failed; - stepProgress(*progressStep); - emitFailed(reason); - }); - connect(zipTask.get(), &Task::stepProgress, this, &AzulJavaDownloader::propagateStepProgress); - - connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { - progressStep->update(current, total); - stepProgress(*progressStep); - }); - connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { - progressStep->status = status; - stepProgress(*progressStep); - }); - zipTask->start(); -}; - -static const QStringList supportedOs = { - "macos", "linux", "windows", "linux-musl", "linux-glibc", "qnx", "solaris", "aix", -}; - -static const QStringList supportedArch = { - "x86", "x64", "amd64", "i686", "arm", "aarch64", "aarch32", "aarch32sf", "aarch32hf", "ppc", - "ppc64", "ppc32", "ppc32hf", "ppc32spe", "sparc", "sparc64", "sparc32", "sparcv9", "sparcv9-64", "sparcv9-32", -}; - -bool AzulJavaDownloader::isSupported() const -{ - return supportedOs.contains(m_os_name == "osx" ? "macos" : m_os_name) && supportedArch.contains(getArch()); -}; \ No newline at end of file diff --git a/launcher/java/providers/AzulJavaDownloader.h b/launcher/java/providers/AzulJavaDownloader.h deleted file mode 100644 index c4bc8c761..000000000 --- a/launcher/java/providers/AzulJavaDownloader.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "java/providers/BasicJavaDownloader.h" - -class AzulJavaDownloader : public BasicJavaDownloader { - Q_OBJECT - public: - void executeTask() override; - - virtual QString name() const override { return "Azul"; }; - virtual bool isSupported() const override; - private slots: - void downloadJavaList(); - void downloadJava(const QJsonArray& doc); - void extractJava(QString input, QString subdirectory); - - private: - QString getArch() const; -}; \ No newline at end of file diff --git a/launcher/java/providers/BasicJavaDownloader.cpp b/launcher/java/providers/BasicJavaDownloader.cpp deleted file mode 100644 index 9f353d9c1..000000000 --- a/launcher/java/providers/BasicJavaDownloader.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include "java/providers/BasicJavaDownloader.h" - -#include "SysInfo.h" -#include "tasks/Task.h" - -BasicJavaDownloader::BasicJavaDownloader(QString final_path, bool m_is_legacy, QObject* parent) - : Task(parent) - , m_os_name(SysInfo::currentSystem()) - , m_os_arch(SysInfo::useQTForArch()) - , m_final_path(final_path) - , m_is_legacy(m_is_legacy) -{} From 81282bf7e0189ede29d2db14f85ef2c886d8b583 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Jan 2024 00:22:05 +0200 Subject: [PATCH 213/755] Autodetect Java? Signed-off-by: Trial97 --- launcher/java/JavaRuntime.cpp | 19 +++++++ launcher/java/JavaRuntime.h | 19 +++++++ launcher/java/JavaUtils.cpp | 1 + .../java/download/ArchiveJavaDownloader.cpp | 18 +++++-- .../java/download/ArchiveJavaDownloader.h | 4 +- .../java/download/ManifestJavaDownloader.cpp | 18 +++++-- .../java/download/ManifestJavaDownloader.h | 4 +- .../minecraft/launch/VerifyJavaInstall.cpp | 49 ++++++++++++++----- 8 files changed, 112 insertions(+), 20 deletions(-) diff --git a/launcher/java/JavaRuntime.cpp b/launcher/java/JavaRuntime.cpp index 0de8c839b..78651e991 100644 --- a/launcher/java/JavaRuntime.cpp +++ b/launcher/java/JavaRuntime.cpp @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "java/JavaRuntime.h" #include @@ -23,6 +41,7 @@ QString downloadTypeToString(DownloadType javaDownload) case DownloadType::Archive: return "archive"; } + return ""; } MetaPtr parseJavaMeta(const QJsonObject& in) { diff --git a/launcher/java/JavaRuntime.h b/launcher/java/JavaRuntime.h index 1ea5d2e48..b44b546b6 100644 --- a/launcher/java/JavaRuntime.h +++ b/launcher/java/JavaRuntime.h @@ -1,8 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once #include #include +#include + #include "java/JavaVersion.h" namespace JavaRuntime { diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 074bf54df..67f1fd3a7 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -393,6 +393,7 @@ QList JavaUtils::FindJavaPaths() scanJavaDir(snap + dirPath); } }; + scanJavaDir(FS::PathCombine(APPLICATION->dataRoot(), "java")); // oracle RPMs scanJavaDirs("/usr/java"); // general locations used by distro packaging diff --git a/launcher/java/download/ArchiveJavaDownloader.cpp b/launcher/java/download/ArchiveJavaDownloader.cpp index 844be1dce..32a3c39d2 100644 --- a/launcher/java/download/ArchiveJavaDownloader.cpp +++ b/launcher/java/download/ArchiveJavaDownloader.cpp @@ -21,9 +21,13 @@ #include "MMCZip.h" #include "Application.h" +#include "net/ChecksumValidator.h" #include "net/NetJob.h" #include "tasks/Task.h" +ArchiveJavaDownloader::ArchiveJavaDownloader(QUrl url, QString final_path, QString checksumType, QString checksumHash) + : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash){}; + void ArchiveJavaDownloader::executeTask() { // JRE found ! download the zip @@ -32,7 +36,15 @@ void ArchiveJavaDownloader::executeTask() MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.toLocalFile()); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); - download->addNetAction(Net::Download::makeCached(m_url, entry)); + auto action = Net::Download::makeCached(m_url, entry); + if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) { + auto hashType = QCryptographicHash::Algorithm::Sha1; + if (m_checksum_type == "sha256") { + hashType = QCryptographicHash::Algorithm::Sha256; + } + action->addValidator(new Net::ChecksumValidator(hashType, m_checksum_hash.toLatin1())); + } + download->addNetAction(action); auto fullPath = entry->getFullPath(); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); @@ -45,7 +57,7 @@ void ArchiveJavaDownloader::executeTask() extractJava(fullPath); }); download->start(); -}; +} void ArchiveJavaDownloader::extractJava(QString input) { @@ -85,4 +97,4 @@ void ArchiveJavaDownloader::extractJava(QString input) stepProgress(*progressStep); }); zipTask->start(); -}; +} \ No newline at end of file diff --git a/launcher/java/download/ArchiveJavaDownloader.h b/launcher/java/download/ArchiveJavaDownloader.h index d17657086..230f62b4d 100644 --- a/launcher/java/download/ArchiveJavaDownloader.h +++ b/launcher/java/download/ArchiveJavaDownloader.h @@ -24,7 +24,7 @@ class ArchiveJavaDownloader : public Task { Q_OBJECT public: - ArchiveJavaDownloader(QUrl url, QString final_path); + ArchiveJavaDownloader(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); virtual ~ArchiveJavaDownloader() = default; [[nodiscard]] bool canAbort() const override { return true; } @@ -36,6 +36,8 @@ class ArchiveJavaDownloader : public Task { protected: QUrl m_url; QString m_final_path; + QString m_checksum_type; + QString m_checksum_hash; Task::Ptr m_current_task; }; \ No newline at end of file diff --git a/launcher/java/download/ManifestJavaDownloader.cpp b/launcher/java/download/ManifestJavaDownloader.cpp index 4f7dab94e..08321ca7f 100644 --- a/launcher/java/download/ManifestJavaDownloader.cpp +++ b/launcher/java/download/ManifestJavaDownloader.cpp @@ -30,14 +30,24 @@ struct File { bool isExec; }; -ManifestJavaDownloader::ManifestJavaDownloader(QUrl url, QString final_path) : m_url(url), m_final_path(final_path){}; +ManifestJavaDownloader::ManifestJavaDownloader(QUrl url, QString final_path, QString checksumType, QString checksumHash) + : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash){}; + void ManifestJavaDownloader::executeTask() { setStatus(tr("Downloading Java")); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); auto files = std::make_shared(); - download->addNetAction(Net::Download::makeByteArray(m_url, files)); + auto action = Net::Download::makeByteArray(m_url, files); + if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) { + auto hashType = QCryptographicHash::Algorithm::Sha1; + if (m_checksum_type == "sha256") { + hashType = QCryptographicHash::Algorithm::Sha256; + } + action->addValidator(new Net::ChecksumValidator(hashType, m_checksum_hash.toLatin1())); + } + download->addNetAction(action); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); connect(download.get(), &NetJob::progress, this, &ManifestJavaDownloader::progress); @@ -56,7 +66,7 @@ void ManifestJavaDownloader::executeTask() downloadJava(doc); }); download->start(); -}; +} void ManifestJavaDownloader::downloadJava(const QJsonDocument& doc) { @@ -111,4 +121,4 @@ void ManifestJavaDownloader::downloadJava(const QJsonDocument& doc) connect(this, &Task::aborted, elementDownload, &NetJob::abort); connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); elementDownload->start(); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/launcher/java/download/ManifestJavaDownloader.h b/launcher/java/download/ManifestJavaDownloader.h index 44e9ed9de..d7114f4bd 100644 --- a/launcher/java/download/ManifestJavaDownloader.h +++ b/launcher/java/download/ManifestJavaDownloader.h @@ -24,7 +24,7 @@ class ManifestJavaDownloader : public Task { Q_OBJECT public: - ManifestJavaDownloader(QUrl url, QString final_path); + ManifestJavaDownloader(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); virtual ~ManifestJavaDownloader() = default; [[nodiscard]] bool canAbort() const override { return true; } @@ -36,6 +36,8 @@ class ManifestJavaDownloader : public Task { protected: QUrl m_url; QString m_final_path; + QString m_checksum_type; + QString m_checksum_hash; Task::Ptr m_current_task; }; \ No newline at end of file diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index cdd1f7fd1..218811290 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -34,7 +34,11 @@ */ #include "VerifyJavaInstall.h" +#include +#include "Application.h" +#include "java/JavaInstall.h" +#include "java/JavaInstallList.h" #include "java/JavaVersion.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" @@ -46,6 +50,7 @@ void VerifyJavaInstall::executeTask() auto settings = instance->settings(); auto storedVersion = settings->get("JavaVersion").toString(); auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool(); + auto automaticJavaSwitch = settings->get("AutomaticJavaSwitch").toBool(); auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors(); @@ -62,16 +67,38 @@ void VerifyJavaInstall::executeTask() return; } - emit logLine(tr("This instance is not compatible with Java version %1.\n" - "Please switch to one of the following Java versions for this instance:") - .arg(javaVersion.major()), - MessageLevel::Error); - for (auto major : compatibleMajors) { - emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); - } - emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " - "you're doing."), - MessageLevel::Error); + auto logFail = [this, &javaVersion, compatibleMajors] { + emit logLine(tr("This instance is not compatible with Java version %1.\n" + "Please switch to one of the following Java versions for this instance:") + .arg(javaVersion.major()), + MessageLevel::Error); + for (auto major : compatibleMajors) { + emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); + } + emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " + "you're doing."), + MessageLevel::Error); - emitFailed(QString("Incompatible Java major version")); + emitFailed(QString("Incompatible Java major version")); + }; + + if (automaticJavaSwitch || true) { + settings->set("OverrideJava", true); + auto javas = APPLICATION->javalist().get(); + auto task = javas->getLoadTask(); + connect(task.get(), &Task::finished, this, [this, javas, compatibleMajors, settings, &logFail] { + for (auto i = 0; i < javas->count(); i++) { + auto java = std::dynamic_pointer_cast(javas->at(i)); + if (java && compatibleMajors.contains(java->id.major())) { + settings->set("OverrideJavaLocation", true); + settings->set("JavaPath", java->path); + emitSucceeded(); + return; + } + } + logFail(); + }); + } else { + logFail(); + } } From 4cd236ed807e44666e6775126d27fef237eef168 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Jan 2024 00:31:43 +0200 Subject: [PATCH 214/755] missing header Signed-off-by: Trial97 --- launcher/java/JavaChecker.cpp | 3 ++- launcher/java/JavaRuntime.h | 1 + launcher/java/download/ArchiveJavaDownloader.cpp | 3 ++- launcher/java/download/ManifestJavaDownloader.cpp | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp index 7271c0d09..18f93d256 100644 --- a/launcher/java/JavaChecker.cpp +++ b/launcher/java/JavaChecker.cpp @@ -44,7 +44,8 @@ #include "java/JavaUtils.h" JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id, QObject* parent) - : Task(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id){}; + : Task(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id) +{} void JavaChecker::executeTask() { diff --git a/launcher/java/JavaRuntime.h b/launcher/java/JavaRuntime.h index b44b546b6..4c4efa288 100644 --- a/launcher/java/JavaRuntime.h +++ b/launcher/java/JavaRuntime.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include diff --git a/launcher/java/download/ArchiveJavaDownloader.cpp b/launcher/java/download/ArchiveJavaDownloader.cpp index 32a3c39d2..5350fc4e6 100644 --- a/launcher/java/download/ArchiveJavaDownloader.cpp +++ b/launcher/java/download/ArchiveJavaDownloader.cpp @@ -26,7 +26,8 @@ #include "tasks/Task.h" ArchiveJavaDownloader::ArchiveJavaDownloader(QUrl url, QString final_path, QString checksumType, QString checksumHash) - : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash){}; + : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash) +{} void ArchiveJavaDownloader::executeTask() { diff --git a/launcher/java/download/ManifestJavaDownloader.cpp b/launcher/java/download/ManifestJavaDownloader.cpp index 08321ca7f..368d6431c 100644 --- a/launcher/java/download/ManifestJavaDownloader.cpp +++ b/launcher/java/download/ManifestJavaDownloader.cpp @@ -31,7 +31,8 @@ struct File { }; ManifestJavaDownloader::ManifestJavaDownloader(QUrl url, QString final_path, QString checksumType, QString checksumHash) - : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash){}; + : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash) +{} void ManifestJavaDownloader::executeTask() { From ab4974629c8e4087e804297e156c7bbc889a840e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 07:12:13 +0000 Subject: [PATCH 215/755] chore(deps): update hendrikmuhs/ccache-action action to v1.2.12 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a0a8d6db9..b9b3597c3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,7 +160,7 @@ jobs: - name: Setup ccache if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' - uses: hendrikmuhs/ccache-action@v1.2.11 + uses: hendrikmuhs/ccache-action@v1.2.12 with: key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} From ad98f600d5666396577295a3915e039314767f60 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Jan 2024 18:36:31 +0200 Subject: [PATCH 216/755] corecly display core mods tab Signed-off-by: Trial97 --- launcher/ui/pages/instance/ModFolderPage.cpp | 31 +++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index aba87942a..4b96338ae 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -66,6 +66,7 @@ #include "Version.h" #include "tasks/ConcurrentTask.h" +#include "tasks/Task.h" #include "ui/dialogs/ProgressDialog.h" ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) @@ -320,21 +321,23 @@ bool CoreModFolderPage::shouldDisplay() const return true; auto version = inst->getPackProfile(); - - ProgressDialog loadDialog(parentWidget()); - auto update = inst->createUpdateTask(Net::Mode::Offline); - if (update) { - loadDialog.setSkipButton(true, tr("Abort")); - loadDialog.execWithTask(update.get()); + if (!version || !version->getComponent("net.minecraftforge") || !version->getComponent("net.minecraft")) + return false; + auto minecraftCmp = version->getComponent("net.minecraft"); + if (!minecraftCmp->m_loaded) { + version->reload(Net::Mode::Offline); + auto update = version->getCurrentTask(); + if (update) { + connect(update.get(), &Task::finished, this, [this] { + if (m_container) { + m_container->refreshContainer(); + } + }); + update->start(); + } + return false; } - if (!version) - return true; - if (!version->getComponent("net.minecraftforge")) - return false; - if (!version->getComponent("net.minecraft")) - return false; - if (version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate) - return true; + return minecraftCmp->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate; } return false; } From b54410b48cf10821fc3229d5d6989645e95c66f0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Jan 2024 19:10:39 +0200 Subject: [PATCH 217/755] Added gameVersion for curseforge mod packs Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f1fd9b5d8..0074a9225 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -178,7 +178,11 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde for (auto version : current.versions) { 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)); + auto mcVersion = !version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion) + ? QString(" for %1").arg(version.mcVersion) + : ""; + ui->versionSelectionBox->addItem(QString("%1%2%3").arg(version.version, mcVersion, release_type), + QVariant(version.downloadUrl)); } QVariant current_updated; From 7317105e4d8c7a65f4a722dbebf8d5e3247f80dc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 27 Jan 2024 22:47:28 +0200 Subject: [PATCH 218/755] Code spaghetti and more chaos Signed-off-by: Trial97 --- launcher/BaseVersionList.cpp | 1 + launcher/BaseVersionList.h | 1 + launcher/CMakeLists.txt | 8 +- launcher/JavaDownloader.cpp | 332 ------------------ launcher/JavaDownloader.h | 28 -- launcher/SysInfo.cpp | 43 ++- launcher/SysInfo.h | 1 + launcher/VersionProxyModel.cpp | 9 + launcher/VersionProxyModel.h | 2 +- launcher/java/JavaRuntime.cpp | 2 +- launcher/java/JavaVersion.cpp | 21 +- .../java/download/ArchiveJavaDownloader.cpp | 2 +- .../java/download/ManifestJavaDownloader.cpp | 2 +- launcher/ui/java/JavaDownload.cpp | 103 ++++++ launcher/ui/java/JavaDownload.h | 48 +++ launcher/ui/java/JavaDownload.ui | 100 ++++++ launcher/ui/java/ListModel.cpp | 154 ++++++++ launcher/ui/java/ListModel.h | 132 +++++++ launcher/ui/pages/global/JavaPage.cpp | 5 +- .../pages/instance/InstanceSettingsPage.cpp | 5 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 5 +- 21 files changed, 613 insertions(+), 391 deletions(-) delete mode 100644 launcher/JavaDownloader.cpp delete mode 100644 launcher/JavaDownloader.h create mode 100644 launcher/ui/java/JavaDownload.cpp create mode 100644 launcher/ui/java/JavaDownload.h create mode 100644 launcher/ui/java/JavaDownload.ui create mode 100644 launcher/ui/java/ListModel.cpp create mode 100644 launcher/ui/java/ListModel.h diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index e11560d5e..576c3a413 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -110,6 +110,7 @@ QHash BaseVersionList::roleNames() const roles.insert(TypeRole, "type"); roles.insert(BranchRole, "branch"); roles.insert(PathRole, "path"); + roles.insert(AliasRole, "alias"); roles.insert(ArchitectureRole, "architecture"); return roles; } diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index 231887c4e..c59d70570 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -48,6 +48,7 @@ class BaseVersionList : public QAbstractListModel { TypeRole, BranchRole, PathRole, + AliasRole, ArchitectureRole, SortRole }; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index ac3f8eb68..d703b8263 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -437,6 +437,11 @@ set(JAVA_SOURCES java/download/ArchiveJavaDownloader.h java/download/ManifestJavaDownloader.cpp java/download/ManifestJavaDownloader.h + + ui/java/JavaDownload.h + ui/java/JavaDownload.cpp + ui/java/ListModel.h + ui/java/ListModel.cpp ) set(TRANSLATIONS_SOURCES @@ -1107,8 +1112,6 @@ SET(LAUNCHER_SOURCES ui/instanceview/InstanceDelegate.h ui/instanceview/VisualGroup.cpp ui/instanceview/VisualGroup.h - JavaDownloader.cpp - JavaDownloader.h ) if (NOT Apple) @@ -1189,6 +1192,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ScrollMessageBox.ui ui/dialogs/BlockedModsDialog.ui ui/dialogs/ChooseProviderDialog.ui + ui/java/JavaDownload.ui ) qt_wrap_ui(PRISM_UPDATE_UI diff --git a/launcher/JavaDownloader.cpp b/launcher/JavaDownloader.cpp deleted file mode 100644 index e4f4b5f09..000000000 --- a/launcher/JavaDownloader.cpp +++ /dev/null @@ -1,332 +0,0 @@ -#include "JavaDownloader.h" -#include -#include -#include -#include -#include "Application.h" -#include "FileSystem.h" -#include "InstanceList.h" -#include "Json.h" -#include "MMCZip.h" -#include "SysInfo.h" -#include "net/ChecksumValidator.h" -#include "net/NetJob.h" -#include "ui/dialogs/ProgressDialog.h" - -// Quick & dirty struct to store files -struct File { - QString path; - QString url; - QByteArray hash; - bool isExec; -}; - -void JavaDownloader::executeTask() -{ - auto OS = m_OS; - auto isLegacy = m_isLegacy; - - downloadMojangJavaList(OS, isLegacy); -} -void JavaDownloader::downloadMojangJavaList(const QString& OS, bool isLegacy) -{ - auto netJob = makeShared(QString("JRE::QueryVersions"), APPLICATION->network()); - auto response = std::make_shared(); - setStatus(tr("Querying mojang meta")); - netJob->addNetAction(Net::Download::makeByteArray( - QUrl("https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"), response)); - - connect(this, &Task::aborted, [isLegacy] { - QDir(FS::PathCombine(QCoreApplication::applicationDirPath(), "java", (isLegacy ? "java-legacy" : "java-current"))) - .removeRecursively(); - }); - - connect(netJob.get(), &NetJob::finished, [netJob, response, this] { - // delete so that it's not called on a deleted job - // FIXME: is this needed? qt should handle this - disconnect(this, &Task::aborted, netJob.get(), &NetJob::abort); - }); - connect(netJob.get(), &NetJob::progress, this, &JavaDownloader::progress); - connect(netJob.get(), &NetJob::failed, this, &JavaDownloader::emitFailed); - - connect(this, &Task::aborted, netJob.get(), &NetJob::abort); - - connect(netJob.get(), &NetJob::succeeded, [response, OS, isLegacy, this, netJob] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *response; - return; - } - auto versionArray = Json::ensureArray(Json::ensureObject(doc.object(), OS), isLegacy ? "jre-legacy" : "java-runtime-gamma"); - if (!versionArray.empty()) { - parseMojangManifest(isLegacy, versionArray); - - } else { - // mojang does not have a JRE for us, let's get azul zulu - downloadAzulMeta(OS, isLegacy, netJob.get()); - } - }); - - netJob->start(); -} -void JavaDownloader::parseMojangManifest(bool isLegacy, const QJsonArray& versionArray) -{ - setStatus(tr("Downloading Java from Mojang")); - auto url = Json::ensureString(Json::ensureObject(Json::ensureObject(versionArray[0]), "manifest"), "url"); - auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); - auto files = std::make_shared(); - - download->addNetAction(Net::Download::makeByteArray(QUrl(url), files)); - - connect(download.get(), &NetJob::finished, - [download, files, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download.get(), &NetJob::progress, this, &JavaDownloader::progress); - connect(download.get(), &NetJob::failed, this, &JavaDownloader::emitFailed); - connect(this, &Task::aborted, download.get(), &NetJob::abort); - - connect(download.get(), &NetJob::succeeded, [files, isLegacy, this] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *files; - return; - } - downloadMojangJava(isLegacy, doc); - }); - download->start(); -} -void JavaDownloader::downloadMojangJava(bool isLegacy, const QJsonDocument& doc) -{ // valid json doc, begin making jre spot - auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java"), (isLegacy ? "java-legacy" : "java-current")); - FS::ensureFolderPathExists(output); - std::vector toDownload; - auto list = Json::ensureObject(Json::ensureObject(doc.object()), "files"); - for (const auto& paths : list.keys()) { - auto file = FS::PathCombine(output, paths); - - const QJsonObject& meta = Json::ensureObject(list, paths); - auto type = Json::ensureString(meta, "type"); - if (type == "directory") { - FS::ensureFolderPathExists(file); - } else if (type == "link") { - // this is linux only ! - auto path = Json::ensureString(meta, "target"); - if (!path.isEmpty()) { - auto target = FS::PathCombine(file, "../" + path); - QFile(target).link(file); - } - } else if (type == "file") { - // TODO download compressed version if it exists ? - auto raw = Json::ensureObject(Json::ensureObject(meta, "downloads"), "raw"); - auto isExec = Json::ensureBoolean(meta, "executable", false); - auto url = Json::ensureString(raw, "url"); - if (!url.isEmpty() && QUrl(url).isValid()) { - auto f = File{ file, url, QByteArray::fromHex(Json::ensureString(raw, "sha1").toLatin1()), isExec }; - toDownload.push_back(f); - } - } - } - auto elementDownload = new NetJob("JRE::FileDownload", APPLICATION->network()); - for (const auto& file : toDownload) { - auto dl = Net::Download::makeFile(file.url, file.path); - if (!file.hash.isEmpty()) { - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); - } - if (file.isExec) { - connect(dl.get(), &Net::Download::succeeded, - [file] { QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); }); - } - elementDownload->addNetAction(dl); - } - connect(elementDownload, &NetJob::finished, [elementDownload, this] { - disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); - elementDownload->deleteLater(); - }); - connect(elementDownload, &NetJob::progress, this, &JavaDownloader::progress); - connect(elementDownload, &NetJob::failed, this, &JavaDownloader::emitFailed); - - connect(this, &Task::aborted, elementDownload, &NetJob::abort); - connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); - elementDownload->start(); -} -void JavaDownloader::downloadAzulMeta(const QString& OS, bool isLegacy, const NetJob* netJob) -{ - setStatus(tr("Querying Azul meta")); - QString javaVersion = isLegacy ? QString("8.0") : QString("17.0"); - - QString azulOS; - QString arch; - QString bitness; - - mojangOStoAzul(OS, azulOS, arch, bitness); - auto metaResponse = std::make_shared(); - auto downloadJob = makeShared(QString("JRE::QueryAzulMeta"), APPLICATION->network()); - downloadJob->addNetAction( - Net::Download::makeByteArray(QString("https://api.azul.com/zulu/download/community/v1.0/bundles/?" - "java_version=%1" - "&os=%2" - "&arch=%3" - "&hw_bitness=%4" - "&ext=zip" // as a zip for all os, even linux NOTE !! Linux ARM is .deb or .tar.gz only !! - "&bundle_type=jre" // jre only - "&latest=true" // only get the one latest entry - ) - .arg(javaVersion, azulOS, arch, bitness), - metaResponse)); - connect(downloadJob.get(), &NetJob::finished, - [downloadJob, metaResponse, this] { disconnect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); }); - connect(this, &Task::aborted, downloadJob.get(), &NetJob::abort); - connect(netJob, &NetJob::failed, this, &JavaDownloader::emitFailed); - connect(downloadJob.get(), &NetJob::progress, this, &JavaDownloader::progress); - connect(downloadJob.get(), &NetJob::succeeded, [metaResponse, isLegacy, this] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*metaResponse, &parse_error); - if (parse_error.error != QJsonParseError::NoError) { - qWarning() << "Error while parsing JSON response at " << parse_error.offset << " reason: " << parse_error.errorString(); - qWarning() << *metaResponse; - return; - } - auto array = Json::ensureArray(doc.array()); - if (!array.empty()) { - downloadAzulJava(isLegacy, array); - } else { - emitFailed(tr("No suitable JRE found")); - } - }); - downloadJob->start(); -} -void JavaDownloader::mojangOStoAzul(const QString& OS, QString& azulOS, QString& arch, QString& bitness) -{ - if (OS == "mac-os-arm64") { - // macos arm64 - azulOS = "macos"; - arch = "arm"; - bitness = "64"; - } else if (OS == "linux-arm64") { - // linux arm64 - azulOS = "linux"; - arch = "arm"; - bitness = "64"; - } else if (OS == "linux-arm") { - // linux arm (32) - azulOS = "linux"; - arch = "arm"; - bitness = "32"; - } else if (OS == "linux") { - // linux x86 64 (used for debugging, should never reach here) - azulOS = "linux"; - arch = "x86"; - bitness = "64"; - } -} -void JavaDownloader::downloadAzulJava(bool isLegacy, const QJsonArray& array) -{ // JRE found ! download the zip - setStatus(tr("Downloading Java from Azul")); - auto downloadURL = QUrl(array[0].toObject()["url"].toString()); - auto download = new NetJob(QString("JRE::DownloadJava"), APPLICATION->network()); - auto path = APPLICATION->instances()->getStagedInstancePath(); - auto temp = FS::PathCombine(path, "azulJRE.zip"); - - download->addNetAction(Net::Download::makeFile(downloadURL, temp)); - connect(download, &NetJob::finished, [download, this] { - disconnect(this, &Task::aborted, download, &NetJob::abort); - download->deleteLater(); - }); - connect(download, &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); - connect(download, &NetJob::progress, this, &JavaDownloader::progress); - connect(download, &NetJob::failed, this, [this, path](QString reason) { - APPLICATION->instances()->destroyStagingPath(path); - emitFailed(std::move(reason)); - }); - connect(this, &Task::aborted, download, &NetJob::abort); - connect(download, &NetJob::succeeded, [isLegacy, temp, downloadURL, path, this] { - setStatus(tr("Extracting java")); - auto output = FS::PathCombine(QCoreApplication::applicationDirPath(), "java", isLegacy ? "java-legacy" : "java-current"); - // This should do all of the extracting and creating folders - MMCZip::extractDir(temp, downloadURL.fileName().chopped(4), output); - APPLICATION->instances()->destroyStagingPath(path); - emitSucceeded(); - }); - download->start(); -} -void JavaDownloader::showPrompts(QWidget* parent) -{ - QString sys = SysInfo::currentSystem(); - if (sys == "osx") { - sys = "mac-os"; - } - QString arch = SysInfo::useQTForArch(); - QString version; - if (sys == "windows") { - if (arch == "x86_64") { - version = "windows-x64"; - } else if (arch == "i386") { - version = "windows-x86"; - } else { - // Unknown, maybe arm, appending arch for downloader - version = "windows-" + arch; - } - } else if (sys == "mac-os") { - if (arch == "arm64") { - version = "mac-os-arm64"; - } else { - version = "mac-os"; - } - } else if (sys == "linux") { - if (arch == "x86_64") { - version = "linux"; - } else { - // will work for i386, and arm(64) - version = "linux-" + arch; - } - } else { - // ? ? ? ? ? unknown os, at least it won't have a java version on mojang or azul, display warning - QMessageBox::warning(parent, tr("Unknown OS"), - tr("The OS you are running is not supported by Mojang or Azul. Please install Java manually.")); - return; - } - // Selection using QMessageBox for java 8 or 17 - QMessageBox box( - QMessageBox::Icon::Question, tr("Java version"), - tr("Do you want to download Java version 8 or 17?\n Java 8 is recommended for older Minecraft versions, below 1.17\n Java 17 " - "is recommended for newer Minecraft versions, starting from 1.17"), - QMessageBox::NoButton, parent); - auto yes = box.addButton("Java 17", QMessageBox::AcceptRole); - auto no = box.addButton("Java 8", QMessageBox::AcceptRole); - auto both = box.addButton(tr("Download both"), QMessageBox::AcceptRole); - auto cancel = box.addButton(QMessageBox::Cancel); - - if (QFileInfo::exists(FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java"), "java-legacy"))) { - no->setEnabled(false); - } - if (QFileInfo::exists(FS::PathCombine(QCoreApplication::applicationDirPath(), QString("java"), "java-current"))) { - yes->setEnabled(false); - } - if (!yes->isEnabled() || !no->isEnabled()) { - both->setEnabled(false); - } - if (!yes->isEnabled() && !no->isEnabled()) { - QMessageBox::information(parent, tr("Already installed!"), tr("Both versions of Java are already installed!")); - return; - } - box.exec(); - if (box.clickedButton() == nullptr || box.clickedButton() == cancel) { - return; - } - bool isLegacy = box.clickedButton() == no; - - auto down = new JavaDownloader(isLegacy, version); - ProgressDialog dialog(parent); - dialog.setSkipButton(true, tr("Abort")); - bool finished_successfully = dialog.execWithTask(down); - // Run another download task for the other option as well! - if (finished_successfully && box.clickedButton() == both) { - auto dwn = new JavaDownloader(false, version); - ProgressDialog dg(parent); - dg.setSkipButton(true, tr("Abort")); - dg.execWithTask(dwn); - } -} diff --git a/launcher/JavaDownloader.h b/launcher/JavaDownloader.h deleted file mode 100644 index 54f058970..000000000 --- a/launcher/JavaDownloader.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include "net/NetJob.h" -#include "tasks/Task.h" - -class JavaDownloader : public Task { - Q_OBJECT - public: - /*Downloads the java to the runtimes folder*/ - explicit JavaDownloader(bool isLegacy, const QString& OS) : m_isLegacy(isLegacy), m_OS(OS) {} - - void executeTask() override; - [[nodiscard]] bool canAbort() const override { return true; } - static void showPrompts(QWidget* parent = nullptr); - - private: - bool m_isLegacy; - const QString& m_OS; - - void downloadMojangJavaList(const QString& OS, bool isLegacy); - void parseMojangManifest(bool isLegacy, const QJsonArray& versionArray); - void downloadMojangJava(bool isLegacy, const QJsonDocument& doc); - - static void mojangOStoAzul(const QString& OS, QString& azulOS, QString& arch, QString& bitness); - void downloadAzulMeta(const QString& OS, bool isLegacy, const NetJob* netJob); - void downloadAzulJava(bool isLegacy, const QJsonArray& array); -}; diff --git a/launcher/SysInfo.cpp b/launcher/SysInfo.cpp index f15dde0e4..0dfa74de7 100644 --- a/launcher/SysInfo.cpp +++ b/launcher/SysInfo.cpp @@ -17,13 +17,7 @@ bool rosettaDetect() if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) { return false; } - if (ret == 0) { - return false; - } - if (ret == 1) { - return true; - } - return false; + return ret == 1; } #endif @@ -47,7 +41,6 @@ QString currentSystem() QString useQTForArch() { - auto qtArch = QSysInfo::currentCpuArchitecture(); #if defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM) if (rosettaDetect()) { return "arm64"; @@ -55,7 +48,7 @@ QString useQTForArch() return "x86_64"; } #endif - return qtArch; + return QSysInfo::currentCpuArchitecture(); } int suitableMaxMem() @@ -71,4 +64,36 @@ int suitableMaxMem() return maxMemoryAlloc; } + +QString getSupportedJavaArchitecture() +{ + auto sys = currentSystem(); + auto arch = useQTForArch(); + if (sys == "windows") { + if (arch == "x86_64") + return "windows-x64"; + if (arch == "i386") + return "windows-x86"; + // Unknown, maybe arm, appending arch + return "windows-" + arch; + } + if (sys == "osx") { + if (arch == "arm64") + return "mac-os-arm64"; + if (arch.contains("64")) + return "mac-os-64"; + if (arch.contains("86")) + return "mac-os-86"; + // Unknown, maybe something new, appending arch + return "mac-os-" + arch; + } else if (sys == "linux") { + if (arch == "x86_64") + return "linux-x64"; + if (arch == "i386") + return "linux-x86"; + // will work for arm32 arm(64) + return "linux-" + arch; + } + return {}; +} } // namespace SysInfo diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h index 499c3b1dd..f3688d60d 100644 --- a/launcher/SysInfo.h +++ b/launcher/SysInfo.h @@ -3,5 +3,6 @@ namespace SysInfo { QString currentSystem(); QString useQTForArch(); +QString getSupportedJavaArchitecture(); int suitableMaxMem(); } // namespace SysInfo diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 0ab9ae2c3..f1218f162 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -118,6 +118,8 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("Architecture"); case Path: return tr("Path"); + case Alias: + return tr("Alias"); case Time: return tr("Released"); } @@ -135,6 +137,8 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("CPU Architecture"); case Path: return tr("Filesystem path to this version"); + case Alias: + return tr("The alternative name of the java version"); case Time: return tr("Release date of this version"); } @@ -169,6 +173,8 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole); case Path: return sourceModel()->data(parentIndex, BaseVersionList::PathRole); + case Alias: + return sourceModel()->data(parentIndex, BaseVersionList::AliasRole); case Time: return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate(); default: @@ -314,6 +320,9 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) if (roles.contains(BaseVersionList::PathRole)) { m_columns.push_back(Path); } + if (roles.contains(BaseVersionList::AliasRole)) { + m_columns.push_back(Alias); + } if (roles.contains(Meta::VersionList::TimeRole)) { m_columns.push_back(Time); } diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index 0863a7c80..2dc35a625 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -9,7 +9,7 @@ class VersionFilterModel; class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: - enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; + enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time, Alias }; using FilterMap = QHash>; public: diff --git a/launcher/java/JavaRuntime.cpp b/launcher/java/JavaRuntime.cpp index 78651e991..be13f47e4 100644 --- a/launcher/java/JavaRuntime.cpp +++ b/launcher/java/JavaRuntime.cpp @@ -62,7 +62,7 @@ MetaPtr parseJavaMeta(const QJsonObject& in) } if (in.contains("version")) { - auto obj = Json::requireObject(in, "checksum"); + auto obj = Json::requireObject(in, "version"); auto name = Json::ensureString(obj, "name", ""); auto major = Json::ensureInteger(obj, "major", 0); auto minor = Json::ensureInteger(obj, "minor", 0); diff --git a/launcher/java/JavaVersion.cpp b/launcher/java/JavaVersion.cpp index 3de6f5ad6..f3ded9709 100644 --- a/launcher/java/JavaVersion.cpp +++ b/launcher/java/JavaVersion.cpp @@ -113,19 +113,20 @@ bool JavaVersion::operator>(const JavaVersion& rhs) JavaVersion::JavaVersion(int major, int minor, int security, int build, QString name) : m_major(major), m_minor(minor), m_security(security), m_name(name), m_parseable(true) { + QStringList versions; if (build != 0) { m_prerelease = QString::number(build); - m_string = m_prerelease; + versions.push_front(m_prerelease); } if (m_security != 0) - m_string = QString::number(m_security) + "." + m_string; - else if (!m_string.isEmpty()) { - m_string = "0." + m_string; - } + versions.push_front(QString::number(m_security)); + else if (!versions.isEmpty()) + versions.push_front("0"); + if (m_minor != 0) - m_string = QString::number(m_minor) + "." + m_string; - else if (!m_string.isEmpty()) { - m_string = "0." + m_string; - } - m_string = QString::number(m_major) + "." + m_string; + versions.push_front(QString::number(m_minor)); + else if (!versions.isEmpty()) + versions.push_front("0"); + versions.push_front(QString::number(m_major)); + m_string = versions.join("."); } diff --git a/launcher/java/download/ArchiveJavaDownloader.cpp b/launcher/java/download/ArchiveJavaDownloader.cpp index 5350fc4e6..bee65cf91 100644 --- a/launcher/java/download/ArchiveJavaDownloader.cpp +++ b/launcher/java/download/ArchiveJavaDownloader.cpp @@ -43,7 +43,7 @@ void ArchiveJavaDownloader::executeTask() if (m_checksum_type == "sha256") { hashType = QCryptographicHash::Algorithm::Sha256; } - action->addValidator(new Net::ChecksumValidator(hashType, m_checksum_hash.toLatin1())); + action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8()))); } download->addNetAction(action); auto fullPath = entry->getFullPath(); diff --git a/launcher/java/download/ManifestJavaDownloader.cpp b/launcher/java/download/ManifestJavaDownloader.cpp index 368d6431c..c0b7942a7 100644 --- a/launcher/java/download/ManifestJavaDownloader.cpp +++ b/launcher/java/download/ManifestJavaDownloader.cpp @@ -46,7 +46,7 @@ void ManifestJavaDownloader::executeTask() if (m_checksum_type == "sha256") { hashType = QCryptographicHash::Algorithm::Sha256; } - action->addValidator(new Net::ChecksumValidator(hashType, m_checksum_hash.toLatin1())); + action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8()))); } download->addNetAction(action); diff --git a/launcher/ui/java/JavaDownload.cpp b/launcher/ui/java/JavaDownload.cpp new file mode 100644 index 000000000..c03fb10e8 --- /dev/null +++ b/launcher/ui/java/JavaDownload.cpp @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "JavaDownload.h" +#include +#include +#include +#include +#include +#include "Application.h" +#include "BaseVersionList.h" +#include "FileSystem.h" +#include "QObjectPtr.h" +#include "SysInfo.h" +#include "java/JavaInstallList.h" +#include "java/download/ArchiveJavaDownloader.h" +#include "java/download/ManifestJavaDownloader.h" +#include "meta/Index.h" +#include "meta/Version.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/java/ListModel.h" +#include "ui_JavaDownload.h" + +JavaDownload::JavaDownload(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownload) +{ + ui->setupUi(this); + ui->widget->initialize(new Java::JavaBaseVersionList("net.minecraft.java")); + ui->widget->selectCurrent(); + connect(ui->widget, &VersionSelectWidget::selectedVersionChanged, this, &JavaDownload::setSelectedVersion); + auto reset = ui->buttonBox->button(QDialogButtonBox::Reset); + connect(reset, &QPushButton::clicked, this, &JavaDownload::refresh); + connect(ui->widget_2, &VersionSelectWidget::selectedVersionChanged, this, &JavaDownload::setSelectedVersion2); +} + +JavaDownload::~JavaDownload() +{ + delete ui; +} + +void JavaDownload::setSelectedVersion(BaseVersion::Ptr version) +{ + if (!version) + return; + auto dcast = std::dynamic_pointer_cast(version); + if (!dcast) { + return; + } + ui->widget_2->initialize(new Java::InstallList(dcast, this)); + ui->widget_2->selectCurrent(); +} + +void JavaDownload::setSelectedVersion2(BaseVersion::Ptr version) +{ + if (!version) + return; + m_selectedVersion = std::dynamic_pointer_cast(ui->widget_2->selectedVersion()); +} +void JavaDownload::accept() +{ + if (!m_selectedVersion) { + m_selectedVersion = std::dynamic_pointer_cast(ui->widget_2->selectedVersion()); + qDebug() << "=========?" << (ui->widget_2->selectedVersion() != nullptr); + } + if (!m_selectedVersion) { + qDebug() << "faillllllllllllllllllllllllllll"; + return; + } + auto meta = m_selectedVersion->meta; + Task::Ptr task; + auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->name); + qDebug() << "===============>>=>>" << meta->checksumType << meta->checksumHash; + switch (meta->downloadType) { + case JavaRuntime::DownloadType::Manifest: + task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); + break; + case JavaRuntime::DownloadType::Archive: + task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); + break; + } + ProgressDialog pg(this); + pg.execWithTask(task.get()); + QDialog::accept(); +} + +void JavaDownload::refresh() +{ + ui->widget->loadList(); +} diff --git a/launcher/ui/java/JavaDownload.h b/launcher/ui/java/JavaDownload.h new file mode 100644 index 000000000..916d6b495 --- /dev/null +++ b/launcher/ui/java/JavaDownload.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "BaseVersion.h" +#include "ui/java/ListModel.h" + +namespace Ui { +class JavaDownload; +} + +class JavaDownload : public QDialog { + Q_OBJECT + + public: + explicit JavaDownload(QWidget* parent = 0); + ~JavaDownload(); + + void accept(); + + public slots: + void refresh(); + + protected slots: + void setSelectedVersion(BaseVersion::Ptr version); + void setSelectedVersion2(BaseVersion::Ptr version); + + private: + Ui::JavaDownload* ui; + Java::JavaRuntimePtr m_selectedVersion; +}; diff --git a/launcher/ui/java/JavaDownload.ui b/launcher/ui/java/JavaDownload.ui new file mode 100644 index 000000000..bbc638e9b --- /dev/null +++ b/launcher/ui/java/JavaDownload.ui @@ -0,0 +1,100 @@ + + + JavaDownload + + + + 0 + 0 + 821 + 593 + + + + Dialog + + + + + + + + Major + + + + + + + + + + + + Runtime + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry + + + + + + + + VersionSelectWidget + QWidget +
    ui/widgets/VersionSelectWidget.h
    + 1 +
    +
    + + + + buttonBox + accepted() + JavaDownload + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + JavaDownload + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/launcher/ui/java/ListModel.cpp b/launcher/ui/java/ListModel.cpp new file mode 100644 index 000000000..71f8765eb --- /dev/null +++ b/launcher/ui/java/ListModel.cpp @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ListModel.h" +#include +#include +#include "BaseVersionList.h" +#include "StringUtils.h" +#include "SysInfo.h" + +namespace Java { + +InstallList::InstallList(Meta::Version::Ptr version, QObject* parent) : BaseVersionList(parent), m_version(version) +{ + if (version->isLoaded()) + sortVersions(); +} + +Task::Ptr InstallList::getLoadTask() +{ + m_version->load(Net::Mode::Online); + auto task = m_version->getCurrentTask(); + connect(task.get(), &Task::finished, this, &InstallList::sortVersions); + return task; +} + +const BaseVersion::Ptr InstallList::at(int i) const +{ + return m_vlist.at(i); +} + +bool InstallList::isLoaded() +{ + return m_version->isLoaded(); +} + +int InstallList::count() const +{ + return m_vlist.count(); +} + +QVariant InstallList::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + auto version = (m_vlist[index.row()]); + switch (role) { + case SortRole: + return -index.row(); + case VersionPointerRole: + return QVariant::fromValue(std::dynamic_pointer_cast(m_vlist[index.row()])); + case VersionIdRole: + return version->descriptor(); + case VersionRole: + return version->meta->version.toString(); + case RecommendedRole: + return version->meta->recommended; + case AliasRole: + return version->meta->name; + case ArchitectureRole: + return version->meta->vendor; + default: + return QVariant(); + } +} + +BaseVersionList::RoleList InstallList::providesRoles() const +{ + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, AliasRole, ArchitectureRole }; +} + +bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) +{ + auto rleft = std::dynamic_pointer_cast(right); + auto rright = std::dynamic_pointer_cast(left); + return (*rleft) > (*rright); +} + +void InstallList::sortVersions() +{ + QString versionStr = SysInfo::getSupportedJavaArchitecture(); + beginResetModel(); + auto runtimes = m_version->data()->runtimes; + if (versionStr.isEmpty() || !runtimes.contains(versionStr)) { + return; + } + auto javaruntimes = runtimes.value(versionStr); + for (auto v : javaruntimes) { + m_vlist.append(std::make_shared(v)); + } + std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); + endResetModel(); +} + +bool JavaRuntime2::operator<(const JavaRuntime2& rhs) +{ + auto id = meta->version; + if (id < rhs.meta->version) { + return true; + } + if (id > rhs.meta->version) { + return false; + } + return StringUtils::naturalCompare(meta->name, rhs.meta->name, Qt::CaseInsensitive) < 0; +} + +bool JavaRuntime2::operator==(const JavaRuntime2& rhs) +{ + return meta->version == rhs.meta->version && meta->name == rhs.meta->name; +} + +bool JavaRuntime2::operator>(const JavaRuntime2& rhs) +{ + return (!operator<(rhs)) && (!operator==(rhs)); +} + +bool JavaRuntime2::operator<(BaseVersion& a) +{ + try { + return operator<(dynamic_cast(a)); + } catch (const std::bad_cast& e) { + return BaseVersion::operator<(a); + } +} + +bool JavaRuntime2::operator>(BaseVersion& a) +{ + try { + return operator>(dynamic_cast(a)); + } catch (const std::bad_cast& e) { + return BaseVersion::operator>(a); + } +} + +} // namespace Java diff --git a/launcher/ui/java/ListModel.h b/launcher/ui/java/ListModel.h new file mode 100644 index 000000000..3685d611d --- /dev/null +++ b/launcher/ui/java/ListModel.h @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include "java/JavaRuntime.h" +#include "meta/VersionList.h" + +namespace Java { + +class JavaBaseVersionList : public Meta::VersionList { + Q_OBJECT + public: + explicit JavaBaseVersionList(const QString& uid, QObject* parent = nullptr) : VersionList(uid, parent) {} + BaseVersionList::RoleList providesRoles() const { return { VersionRole, RecommendedRole, VersionPointerRole }; } +}; + +struct JavaRuntime2 : public BaseVersion { + JavaRuntime2() {} + JavaRuntime2(JavaRuntime::MetaPtr m) : meta(m) {} + virtual QString descriptor() override { return meta->version.toString(); } + + virtual QString name() override { return meta->name; } + + virtual QString typeString() const override { return meta->vendor; } + + virtual bool operator<(BaseVersion& a) override; + virtual bool operator>(BaseVersion& a) override; + bool operator<(const JavaRuntime2& rhs); + bool operator==(const JavaRuntime2& rhs); + bool operator>(const JavaRuntime2& rhs); + + JavaRuntime::MetaPtr meta; +}; + +using JavaRuntimePtr = std::shared_ptr; + +class InstallList : public BaseVersionList { + Q_OBJECT + + public: + explicit InstallList(Meta::Version::Ptr m_version, QObject* parent = 0); + + Task::Ptr getLoadTask() override; + bool isLoaded() override; + const BaseVersion::Ptr at(int i) const override; + int count() const override; + void sortVersions() override; + + QVariant data(const QModelIndex& index, int role) const override; + RoleList providesRoles() const override; + + protected slots: + void updateListData(QList) override {} + + protected: + Meta::Version::Ptr m_version; + QList m_vlist; +}; + +} // namespace Java +// class FilterModel : public QSortFilterProxyModel { +// Q_OBJECT +// public: +// FilterModel(QObject* parent = Q_NULLPTR); +// enum Sorting { ByName, ByGameVersion }; +// const QMap getAvailableSortings(); +// QString translateCurrentSorting(); +// void setSorting(Sorting sorting); +// Sorting getCurrentSorting(); +// void setSearchTerm(QString term); + +// protected: +// bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; +// bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; + +// private: +// QMap sortings; +// Sorting currentSorting; +// QString searchTerm; +// }; + +// class ListModel : public QAbstractListModel { +// Q_OBJECT +// private: +// ModpackList modpacks; +// QStringList m_failedLogos; +// QStringList m_loadingLogos; +// FTBLogoMap m_logoMap; +// QMap waitingCallbacks; + +// void requestLogo(QString file); +// QString translatePackType(PackType type) const; + +// private slots: +// void logoFailed(QString logo); +// void logoLoaded(QString logo, QIcon out); + +// public: +// ListModel(QObject* parent); +// ~ListModel(); +// int rowCount(const QModelIndex& parent) const override; +// int columnCount(const QModelIndex& parent) const override; +// QVariant data(const QModelIndex& index, int role) const override; +// Qt::ItemFlags flags(const QModelIndex& index) const override; + +// void fill(ModpackList modpacks); +// void addPack(Modpack modpack); +// void clear(); +// void remove(int row); + +// Modpack at(int row); +// void getLogo(const QString& logo, LogoCallback callback); +// }; diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 0f05b6c8f..09c951a3c 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -36,6 +36,7 @@ #include "JavaPage.h" #include "JavaCommon.h" +#include "ui/java/JavaDownload.h" #include "ui_JavaPage.h" #include @@ -51,7 +52,6 @@ #include #include #include "Application.h" -#include "JavaDownloader.h" #include "settings/SettingsObject.h" JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) @@ -169,7 +169,8 @@ void JavaPage::on_javaTestBtn_clicked() void JavaPage::on_javaDownloadBtn_clicked() { - JavaDownloader::showPrompts(this); + auto jdialog = new JavaDownload(this); + jdialog->exec(); } void JavaPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index a386a3bfe..df380bfb3 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -36,6 +36,7 @@ */ #include "InstanceSettingsPage.h" +#include "ui/java/JavaDownload.h" #include "ui_InstanceSettingsPage.h" #include @@ -53,7 +54,6 @@ #include "minecraft/auth/AccountList.h" #include "FileSystem.h" -#include "JavaDownloader.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" @@ -387,7 +387,8 @@ void InstanceSettingsPage::loadSettings() void InstanceSettingsPage::on_javaDownloadBtn_clicked() { - JavaDownloader::showPrompts(this); + auto jdialog = new JavaDownload(this); + jdialog->exec(); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 909ad7a01..7405ad114 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -13,13 +13,13 @@ #include "FileSystem.h" #include "JavaCommon.h" -#include "JavaDownloader.h" #include "java/JavaChecker.h" #include "java/JavaInstall.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/java/JavaDownload.h" #include "ui/widgets/VersionSelectWidget.h" #include "Application.h" @@ -274,7 +274,8 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() } void JavaSettingsWidget::on_javaDownloadBtn_clicked() { - JavaDownloader::showPrompts(this); + auto jdialog = new JavaDownload(this); + jdialog->exec(); } void JavaSettingsWidget::on_javaStatusBtn_clicked() { From 901cc4e4dd051459040ac93c6ab762e0a4b219bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 28 Jan 2024 00:19:08 +0000 Subject: [PATCH 219/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/e5d1c87f5813afde2dda384ac807c57a105721cc' (2024-01-19) → 'github:nixos/nixpkgs/4fddc9be4eaf195d631333908f2a454b03628ee5' (2024-01-25) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 79075c3c7..bbbab1e45 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1705697961, - "narHash": "sha256-XepT3WS516evSFYkme3GrcI3+7uwXHqtHbip+t24J7E=", + "lastModified": 1706173671, + "narHash": "sha256-lciR7kQUK2FCAYuszyd7zyRRmTaXVeoZsCyK6QFpGdk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e5d1c87f5813afde2dda384ac807c57a105721cc", + "rev": "4fddc9be4eaf195d631333908f2a454b03628ee5", "type": "github" }, "original": { From 5afe75e821516dbbde60d1b2d3a627a0d243b1fe Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 30 Jan 2024 12:37:34 +0200 Subject: [PATCH 220/755] Fixed some codeql warnings Signed-off-by: Trial97 --- launcher/JavaCommon.cpp | 4 ++-- launcher/JavaCommon.h | 4 ++-- launcher/java/JavaChecker.h | 2 +- launcher/java/JavaInstallList.cpp | 4 ++-- launcher/java/JavaVersion.cpp | 4 ++-- launcher/java/JavaVersion.h | 4 ++-- launcher/java/download/ArchiveJavaDownloader.cpp | 1 - launcher/launch/steps/CheckJava.cpp | 2 +- launcher/launch/steps/CheckJava.h | 2 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 2 +- launcher/ui/widgets/JavaSettingsWidget.h | 2 +- 11 files changed, 15 insertions(+), 16 deletions(-) diff --git a/launcher/JavaCommon.cpp b/launcher/JavaCommon.cpp index cfc3cfe42..3cbf9f9d5 100644 --- a/launcher/JavaCommon.cpp +++ b/launcher/JavaCommon.cpp @@ -121,7 +121,7 @@ void JavaCommon::TestCheck::run() checker->start(); } -void JavaCommon::TestCheck::checkFinished(JavaChecker::Result result) +void JavaCommon::TestCheck::checkFinished(const JavaChecker::Result& result) { if (result.validity != JavaChecker::Result::Validity::Valid) { javaBinaryWasBad(m_parent, result); @@ -133,7 +133,7 @@ void JavaCommon::TestCheck::checkFinished(JavaChecker::Result result) checker->start(); } -void JavaCommon::TestCheck::checkFinishedWithArgs(JavaChecker::Result result) +void JavaCommon::TestCheck::checkFinishedWithArgs(const JavaChecker::Result& result) { if (result.validity == JavaChecker::Result::Validity::Valid) { javaWasOk(m_parent, result); diff --git a/launcher/JavaCommon.h b/launcher/JavaCommon.h index 7c5510efc..ca208b593 100644 --- a/launcher/JavaCommon.h +++ b/launcher/JavaCommon.h @@ -32,8 +32,8 @@ class TestCheck : public QObject { void finished(); private slots: - void checkFinished(JavaChecker::Result result); - void checkFinishedWithArgs(JavaChecker::Result result); + void checkFinished(const JavaChecker::Result& result); + void checkFinishedWithArgs(const JavaChecker::Result& result); private: JavaChecker::Ptr checker; diff --git a/launcher/java/JavaChecker.h b/launcher/java/JavaChecker.h index 0c6191c21..171a18b76 100644 --- a/launcher/java/JavaChecker.h +++ b/launcher/java/JavaChecker.h @@ -29,7 +29,7 @@ class JavaChecker : public Task { explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0, QObject* parent = 0); signals: - void checkFinished(Result result); + void checkFinished(const Result& result); protected: virtual void executeTask() override; diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index ef99d6853..c14056964 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -170,7 +170,7 @@ void JavaListLoadTask::executeTask() int id = 0; for (QString candidate : candidate_paths) { auto checker = new JavaChecker(candidate, "", 0, 0, 0, id, this); - connect(checker, &JavaChecker::checkFinished, [this](JavaChecker::Result result) { m_results << result; }); + connect(checker, &JavaChecker::checkFinished, [this](const JavaChecker::Result& result) { m_results << result; }); job->addTask(Task::Ptr(checker)); id++; } @@ -181,7 +181,7 @@ void JavaListLoadTask::executeTask() void JavaListLoadTask::javaCheckerFinished() { QList candidates; - std::sort(m_results.begin(), m_results.end(), [](JavaChecker::Result a, JavaChecker::Result b) { return a.id < b.id; }); + std::sort(m_results.begin(), m_results.end(), [](const JavaChecker::Result& a, const JavaChecker::Result& b) { return a.id < b.id; }); qDebug() << "Found the following valid Java installations:"; for (auto result : m_results) { diff --git a/launcher/java/JavaVersion.cpp b/launcher/java/JavaVersion.cpp index f3ded9709..2dc23472e 100644 --- a/launcher/java/JavaVersion.cpp +++ b/launcher/java/JavaVersion.cpp @@ -43,12 +43,12 @@ QString JavaVersion::toString() const return m_string; } -bool JavaVersion::requiresPermGen() +bool JavaVersion::requiresPermGen() const { return !m_parseable || m_major < 8; } -bool JavaVersion::isModular() +bool JavaVersion::isModular() const { return m_parseable && m_major >= 9; } diff --git a/launcher/java/JavaVersion.h b/launcher/java/JavaVersion.h index eee992346..92c743bfb 100644 --- a/launcher/java/JavaVersion.h +++ b/launcher/java/JavaVersion.h @@ -24,9 +24,9 @@ class JavaVersion { bool operator==(const JavaVersion& rhs); bool operator>(const JavaVersion& rhs); - bool requiresPermGen(); + bool requiresPermGen() const; - bool isModular(); + bool isModular() const; QString toString() const; diff --git a/launcher/java/download/ArchiveJavaDownloader.cpp b/launcher/java/download/ArchiveJavaDownloader.cpp index bee65cf91..cb642357a 100644 --- a/launcher/java/download/ArchiveJavaDownloader.cpp +++ b/launcher/java/download/ArchiveJavaDownloader.cpp @@ -49,7 +49,6 @@ void ArchiveJavaDownloader::executeTask() auto fullPath = entry->getFullPath(); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - // connect(download.get(), &NetJob::aborted, [path] { APPLICATION->instances()->destroyStagingPath(path); }); connect(download.get(), &NetJob::progress, this, &ArchiveJavaDownloader::progress); connect(download.get(), &NetJob::failed, this, &ArchiveJavaDownloader::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); diff --git a/launcher/launch/steps/CheckJava.cpp b/launcher/launch/steps/CheckJava.cpp index c776092fe..a8c7305ab 100644 --- a/launcher/launch/steps/CheckJava.cpp +++ b/launcher/launch/steps/CheckJava.cpp @@ -106,7 +106,7 @@ void CheckJava::executeTask() emitSucceeded(); } -void CheckJava::checkJavaFinished(JavaChecker::Result result) +void CheckJava::checkJavaFinished(const JavaChecker::Result& result) { switch (result.validity) { case JavaChecker::Result::Validity::Errored: { diff --git a/launcher/launch/steps/CheckJava.h b/launcher/launch/steps/CheckJava.h index 5ba9e4018..62b0c6bbb 100644 --- a/launcher/launch/steps/CheckJava.h +++ b/launcher/launch/steps/CheckJava.h @@ -28,7 +28,7 @@ class CheckJava : public LaunchStep { virtual void executeTask(); virtual bool canAbort() const { return false; } private slots: - void checkJavaFinished(JavaChecker::Result result); + void checkJavaFinished(const JavaChecker::Result& result); private: void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor); diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 7405ad114..d6fbf00a3 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -377,7 +377,7 @@ void JavaSettingsWidget::checkJavaPath(const QString& path) m_checker->start(); } -void JavaSettingsWidget::checkFinished(JavaChecker::Result result) +void JavaSettingsWidget::checkFinished(const JavaChecker::Result& result) { m_result = result; switch (result.validity) { diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index d3cd2c5a7..b7db955a4 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -51,7 +51,7 @@ class JavaSettingsWidget : public QWidget { void on_javaBrowseBtn_clicked(); void on_javaStatusBtn_clicked(); void on_javaDownloadBtn_clicked(); - void checkFinished(JavaChecker::Result result); + void checkFinished(const JavaChecker::Result& result); protected: /* methods */ void checkJavaPathOnEdit(const QString& path); From 6c5bb3817bfe1a51924127f77965ea72506452aa Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 31 Jan 2024 18:07:28 +0200 Subject: [PATCH 221/755] Cleaned some code Signed-off-by: Trial97 --- launcher/java/JavaRuntime.cpp | 44 ++++++++++++++++- launcher/java/JavaRuntime.h | 18 ++++++- launcher/ui/java/JavaDownload.cpp | 35 +++++--------- launcher/ui/java/JavaDownload.h | 3 -- launcher/ui/java/ListModel.cpp | 69 ++++++--------------------- launcher/ui/java/ListModel.h | 78 +------------------------------ 6 files changed, 85 insertions(+), 162 deletions(-) diff --git a/launcher/java/JavaRuntime.cpp b/launcher/java/JavaRuntime.cpp index be13f47e4..e88955772 100644 --- a/launcher/java/JavaRuntime.cpp +++ b/launcher/java/JavaRuntime.cpp @@ -21,6 +21,7 @@ #include #include "Json.h" +#include "StringUtils.h" #include "java/JavaVersion.h" #include "minecraft/ParseUtils.h" @@ -47,7 +48,7 @@ MetaPtr parseJavaMeta(const QJsonObject& in) { auto meta = std::make_shared(); - meta->name = Json::ensureString(in, "name", ""); + meta->m_name = Json::ensureString(in, "name", ""); meta->vendor = Json::ensureString(in, "vendor", ""); meta->url = Json::ensureString(in, "url", ""); meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", "")); @@ -72,4 +73,45 @@ MetaPtr parseJavaMeta(const QJsonObject& in) } return meta; } + +bool Meta::operator<(const Meta& rhs) +{ + auto id = version; + if (id < rhs.version) { + return true; + } + if (id > rhs.version) { + return false; + } + return StringUtils::naturalCompare(m_name, rhs.m_name, Qt::CaseInsensitive) < 0; +} + +bool Meta::operator==(const Meta& rhs) +{ + return version == rhs.version && m_name == rhs.m_name; +} + +bool Meta::operator>(const Meta& rhs) +{ + return (!operator<(rhs)) && (!operator==(rhs)); +} + +bool Meta::operator<(BaseVersion& a) +{ + try { + return operator<(dynamic_cast(a)); + } catch (const std::bad_cast& e) { + return BaseVersion::operator<(a); + } +} + +bool Meta::operator>(BaseVersion& a) +{ + try { + return operator>(dynamic_cast(a)); + } catch (const std::bad_cast& e) { + return BaseVersion::operator>(a); + } +} + } // namespace JavaRuntime diff --git a/launcher/java/JavaRuntime.h b/launcher/java/JavaRuntime.h index 4c4efa288..654ba030a 100644 --- a/launcher/java/JavaRuntime.h +++ b/launcher/java/JavaRuntime.h @@ -23,14 +23,28 @@ #include +#include "BaseVersion.h" #include "java/JavaVersion.h" namespace JavaRuntime { enum class DownloadType { Manifest, Archive }; -struct Meta { - QString name; +class Meta : public BaseVersion { + public: + virtual QString descriptor() override { return version.toString(); } + + virtual QString name() override { return m_name; } + + virtual QString typeString() const override { return vendor; } + + virtual bool operator<(BaseVersion& a) override; + virtual bool operator>(BaseVersion& a) override; + bool operator<(const Meta& rhs); + bool operator==(const Meta& rhs); + bool operator>(const Meta& rhs); + + QString m_name; QString vendor; QString url; QDateTime releaseTime; diff --git a/launcher/ui/java/JavaDownload.cpp b/launcher/ui/java/JavaDownload.cpp index c03fb10e8..bae61a060 100644 --- a/launcher/ui/java/JavaDownload.cpp +++ b/launcher/ui/java/JavaDownload.cpp @@ -17,21 +17,23 @@ */ #include "JavaDownload.h" -#include -#include -#include -#include + +#include + #include + #include "Application.h" -#include "BaseVersionList.h" #include "FileSystem.h" #include "QObjectPtr.h" #include "SysInfo.h" -#include "java/JavaInstallList.h" + +#include "java/JavaRuntime.h" #include "java/download/ArchiveJavaDownloader.h" #include "java/download/ManifestJavaDownloader.h" + #include "meta/Index.h" #include "meta/Version.h" + #include "ui/dialogs/ProgressDialog.h" #include "ui/java/ListModel.h" #include "ui_JavaDownload.h" @@ -44,7 +46,6 @@ JavaDownload::JavaDownload(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDo connect(ui->widget, &VersionSelectWidget::selectedVersionChanged, this, &JavaDownload::setSelectedVersion); auto reset = ui->buttonBox->button(QDialogButtonBox::Reset); connect(reset, &QPushButton::clicked, this, &JavaDownload::refresh); - connect(ui->widget_2, &VersionSelectWidget::selectedVersionChanged, this, &JavaDownload::setSelectedVersion2); } JavaDownload::~JavaDownload() @@ -54,8 +55,6 @@ JavaDownload::~JavaDownload() void JavaDownload::setSelectedVersion(BaseVersion::Ptr version) { - if (!version) - return; auto dcast = std::dynamic_pointer_cast(version); if (!dcast) { return; @@ -64,26 +63,14 @@ void JavaDownload::setSelectedVersion(BaseVersion::Ptr version) ui->widget_2->selectCurrent(); } -void JavaDownload::setSelectedVersion2(BaseVersion::Ptr version) -{ - if (!version) - return; - m_selectedVersion = std::dynamic_pointer_cast(ui->widget_2->selectedVersion()); -} void JavaDownload::accept() { - if (!m_selectedVersion) { - m_selectedVersion = std::dynamic_pointer_cast(ui->widget_2->selectedVersion()); - qDebug() << "=========?" << (ui->widget_2->selectedVersion() != nullptr); - } - if (!m_selectedVersion) { - qDebug() << "faillllllllllllllllllllllllllll"; + auto meta = std::dynamic_pointer_cast(ui->widget_2->selectedVersion()); + if (!meta) { return; } - auto meta = m_selectedVersion->meta; Task::Ptr task; - auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->name); - qDebug() << "===============>>=>>" << meta->checksumType << meta->checksumHash; + auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->m_name); switch (meta->downloadType) { case JavaRuntime::DownloadType::Manifest: task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); diff --git a/launcher/ui/java/JavaDownload.h b/launcher/ui/java/JavaDownload.h index 916d6b495..de0916ae5 100644 --- a/launcher/ui/java/JavaDownload.h +++ b/launcher/ui/java/JavaDownload.h @@ -20,7 +20,6 @@ #include #include "BaseVersion.h" -#include "ui/java/ListModel.h" namespace Ui { class JavaDownload; @@ -40,9 +39,7 @@ class JavaDownload : public QDialog { protected slots: void setSelectedVersion(BaseVersion::Ptr version); - void setSelectedVersion2(BaseVersion::Ptr version); private: Ui::JavaDownload* ui; - Java::JavaRuntimePtr m_selectedVersion; }; diff --git a/launcher/ui/java/ListModel.cpp b/launcher/ui/java/ListModel.cpp index 71f8765eb..11cd01130 100644 --- a/launcher/ui/java/ListModel.cpp +++ b/launcher/ui/java/ListModel.cpp @@ -17,11 +17,12 @@ */ #include "ListModel.h" -#include + #include + #include "BaseVersionList.h" -#include "StringUtils.h" #include "SysInfo.h" +#include "java/JavaRuntime.h" namespace Java { @@ -71,13 +72,13 @@ QVariant InstallList::data(const QModelIndex& index, int role) const case VersionIdRole: return version->descriptor(); case VersionRole: - return version->meta->version.toString(); + return version->version.toString(); case RecommendedRole: - return version->meta->recommended; + return version->recommended; case AliasRole: - return version->meta->name; + return version->name(); case ArchitectureRole: - return version->meta->vendor; + return version->vendor; default: return QVariant(); } @@ -90,8 +91,8 @@ BaseVersionList::RoleList InstallList::providesRoles() const bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) { - auto rleft = std::dynamic_pointer_cast(right); - auto rright = std::dynamic_pointer_cast(left); + auto rleft = std::dynamic_pointer_cast(right); + auto rright = std::dynamic_pointer_cast(left); return (*rleft) > (*rright); } @@ -100,55 +101,13 @@ void InstallList::sortVersions() QString versionStr = SysInfo::getSupportedJavaArchitecture(); beginResetModel(); auto runtimes = m_version->data()->runtimes; - if (versionStr.isEmpty() || !runtimes.contains(versionStr)) { - return; + if (!versionStr.isEmpty() && runtimes.contains(versionStr)) { + m_vlist = runtimes.value(versionStr); + std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); + } else { + m_vlist = {}; } - auto javaruntimes = runtimes.value(versionStr); - for (auto v : javaruntimes) { - m_vlist.append(std::make_shared(v)); - } - std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); endResetModel(); } -bool JavaRuntime2::operator<(const JavaRuntime2& rhs) -{ - auto id = meta->version; - if (id < rhs.meta->version) { - return true; - } - if (id > rhs.meta->version) { - return false; - } - return StringUtils::naturalCompare(meta->name, rhs.meta->name, Qt::CaseInsensitive) < 0; -} - -bool JavaRuntime2::operator==(const JavaRuntime2& rhs) -{ - return meta->version == rhs.meta->version && meta->name == rhs.meta->name; -} - -bool JavaRuntime2::operator>(const JavaRuntime2& rhs) -{ - return (!operator<(rhs)) && (!operator==(rhs)); -} - -bool JavaRuntime2::operator<(BaseVersion& a) -{ - try { - return operator<(dynamic_cast(a)); - } catch (const std::bad_cast& e) { - return BaseVersion::operator<(a); - } -} - -bool JavaRuntime2::operator>(BaseVersion& a) -{ - try { - return operator>(dynamic_cast(a)); - } catch (const std::bad_cast& e) { - return BaseVersion::operator>(a); - } -} - } // namespace Java diff --git a/launcher/ui/java/ListModel.h b/launcher/ui/java/ListModel.h index 3685d611d..a157e0e8d 100644 --- a/launcher/ui/java/ListModel.h +++ b/launcher/ui/java/ListModel.h @@ -18,9 +18,6 @@ #pragma once -#include -#include -#include #include "java/JavaRuntime.h" #include "meta/VersionList.h" @@ -33,26 +30,6 @@ class JavaBaseVersionList : public Meta::VersionList { BaseVersionList::RoleList providesRoles() const { return { VersionRole, RecommendedRole, VersionPointerRole }; } }; -struct JavaRuntime2 : public BaseVersion { - JavaRuntime2() {} - JavaRuntime2(JavaRuntime::MetaPtr m) : meta(m) {} - virtual QString descriptor() override { return meta->version.toString(); } - - virtual QString name() override { return meta->name; } - - virtual QString typeString() const override { return meta->vendor; } - - virtual bool operator<(BaseVersion& a) override; - virtual bool operator>(BaseVersion& a) override; - bool operator<(const JavaRuntime2& rhs); - bool operator==(const JavaRuntime2& rhs); - bool operator>(const JavaRuntime2& rhs); - - JavaRuntime::MetaPtr meta; -}; - -using JavaRuntimePtr = std::shared_ptr; - class InstallList : public BaseVersionList { Q_OBJECT @@ -73,60 +50,7 @@ class InstallList : public BaseVersionList { protected: Meta::Version::Ptr m_version; - QList m_vlist; + QList m_vlist; }; } // namespace Java -// class FilterModel : public QSortFilterProxyModel { -// Q_OBJECT -// public: -// FilterModel(QObject* parent = Q_NULLPTR); -// enum Sorting { ByName, ByGameVersion }; -// const QMap getAvailableSortings(); -// QString translateCurrentSorting(); -// void setSorting(Sorting sorting); -// Sorting getCurrentSorting(); -// void setSearchTerm(QString term); - -// protected: -// bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; -// bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; - -// private: -// QMap sortings; -// Sorting currentSorting; -// QString searchTerm; -// }; - -// class ListModel : public QAbstractListModel { -// Q_OBJECT -// private: -// ModpackList modpacks; -// QStringList m_failedLogos; -// QStringList m_loadingLogos; -// FTBLogoMap m_logoMap; -// QMap waitingCallbacks; - -// void requestLogo(QString file); -// QString translatePackType(PackType type) const; - -// private slots: -// void logoFailed(QString logo); -// void logoLoaded(QString logo, QIcon out); - -// public: -// ListModel(QObject* parent); -// ~ListModel(); -// int rowCount(const QModelIndex& parent) const override; -// int columnCount(const QModelIndex& parent) const override; -// QVariant data(const QModelIndex& index, int role) const override; -// Qt::ItemFlags flags(const QModelIndex& index) const override; - -// void fill(ModpackList modpacks); -// void addPack(Modpack modpack); -// void clear(); -// void remove(int row); - -// Modpack at(int row); -// void getLogo(const QString& logo, LogoCallback callback); -// }; From 3c58fb06776000808029e8cc4d5207bfd0f57321 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 2 Feb 2024 15:51:32 +0200 Subject: [PATCH 222/755] Moving files around Signed-off-by: Trial97 --- launcher/BaseVersionList.cpp | 4 +- launcher/BaseVersionList.h | 4 +- launcher/CMakeLists.txt | 22 ++-- launcher/VersionProxyModel.cpp | 26 ++--- launcher/VersionProxyModel.h | 2 +- launcher/java/JavaInstallList.cpp | 4 +- .../{JavaRuntime.cpp => JavaMetadata.cpp} | 24 ++-- .../java/{JavaRuntime.h => JavaMetadata.h} | 16 +-- ...Downloader.cpp => ArchiveDownloadTask.cpp} | 22 ++-- ...JavaDownloader.h => ArchiveDownloadTask.h} | 12 +- ...ownloader.cpp => ManifestDownloadTask.cpp} | 20 ++-- ...avaDownloader.h => ManifestDownloadTask.h} | 13 ++- launcher/meta/VersionList.cpp | 8 +- launcher/meta/VersionList.h | 5 + launcher/minecraft/OneSixVersionFormat.cpp | 6 +- launcher/minecraft/VersionFile.h | 4 +- launcher/ui/java/JavaDownload.cpp | 90 --------------- launcher/ui/java/JavaDownloader.cpp | 109 ++++++++++++++++++ .../java/{JavaDownload.h => JavaDownloader.h} | 13 ++- .../{JavaDownload.ui => JavaDownloader.ui} | 12 +- .../java/{ListModel.cpp => VersionList.cpp} | 32 ++--- .../ui/java/{ListModel.h => VersionList.h} | 18 +-- launcher/ui/pages/global/JavaPage.cpp | 4 +- .../pages/instance/InstanceSettingsPage.cpp | 4 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 4 +- 25 files changed, 254 insertions(+), 224 deletions(-) rename launcher/java/{JavaRuntime.cpp => JavaMetadata.cpp} (86%) rename launcher/java/{JavaRuntime.h => JavaMetadata.h} (84%) rename launcher/java/download/{ArchiveJavaDownloader.cpp => ArchiveDownloadTask.cpp} (82%) rename launcher/java/download/{ArchiveJavaDownloader.h => ArchiveDownloadTask.h} (82%) rename launcher/java/download/{ManifestJavaDownloader.cpp => ManifestDownloadTask.cpp} (87%) rename launcher/java/download/{ManifestJavaDownloader.h => ManifestDownloadTask.h} (82%) delete mode 100644 launcher/ui/java/JavaDownload.cpp create mode 100644 launcher/ui/java/JavaDownloader.cpp rename launcher/ui/java/{JavaDownload.h => JavaDownloader.h} (84%) rename launcher/ui/java/{JavaDownload.ui => JavaDownloader.ui} (86%) rename launcher/ui/java/{ListModel.cpp => VersionList.cpp} (76%) rename launcher/ui/java/{ListModel.h => VersionList.h} (71%) diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index 576c3a413..afee8388a 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -110,7 +110,7 @@ QHash BaseVersionList::roleNames() const roles.insert(TypeRole, "type"); roles.insert(BranchRole, "branch"); roles.insert(PathRole, "path"); - roles.insert(AliasRole, "alias"); - roles.insert(ArchitectureRole, "architecture"); + roles.insert(JavaNameRole, "javaName"); + roles.insert(CPUArchitectureRole, "architecture"); return roles; } diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index c59d70570..bc37e9e53 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -48,8 +48,8 @@ class BaseVersionList : public QAbstractListModel { TypeRole, BranchRole, PathRole, - AliasRole, - ArchitectureRole, + JavaNameRole, + CPUArchitectureRole, SortRole }; using RoleList = QList; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d703b8263..c7945d1b8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -431,17 +431,17 @@ set(JAVA_SOURCES java/JavaVersion.h java/JavaVersion.cpp - java/JavaRuntime.h - java/JavaRuntime.cpp - java/download/ArchiveJavaDownloader.cpp - java/download/ArchiveJavaDownloader.h - java/download/ManifestJavaDownloader.cpp - java/download/ManifestJavaDownloader.h + java/JavaMetadata.h + java/JavaMetadata.cpp + java/download/ArchiveDownloadTask.cpp + java/download/ArchiveDownloadTask.h + java/download/ManifestDownloadTask.cpp + java/download/ManifestDownloadTask.h - ui/java/JavaDownload.h - ui/java/JavaDownload.cpp - ui/java/ListModel.h - ui/java/ListModel.cpp + ui/java/JavaDownloader.h + ui/java/JavaDownloader.cpp + ui/java/VersionList.h + ui/java/VersionList.cpp ) set(TRANSLATIONS_SOURCES @@ -1192,7 +1192,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ScrollMessageBox.ui ui/dialogs/BlockedModsDialog.ui ui/dialogs/ChooseProviderDialog.ui - ui/java/JavaDownload.ui + ui/java/JavaDownloader.ui ) qt_wrap_ui(PRISM_UPDATE_UI diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index f1218f162..070e952a4 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -114,12 +114,12 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("Branch"); case Type: return tr("Type"); - case Architecture: + case CPUArchitecture: return tr("Architecture"); case Path: return tr("Path"); - case Alias: - return tr("Alias"); + case JavaName: + return tr("Java Name"); case Time: return tr("Released"); } @@ -133,11 +133,11 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("The version's branch"); case Type: return tr("The version's type"); - case Architecture: + case CPUArchitecture: return tr("CPU Architecture"); case Path: return tr("Filesystem path to this version"); - case Alias: + case JavaName: return tr("The alternative name of the java version"); case Time: return tr("Release date of this version"); @@ -169,12 +169,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const return sourceModel()->data(parentIndex, BaseVersionList::BranchRole); case Type: return sourceModel()->data(parentIndex, BaseVersionList::TypeRole); - case Architecture: - return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole); + case CPUArchitecture: + return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole); case Path: return sourceModel()->data(parentIndex, BaseVersionList::PathRole); - case Alias: - return sourceModel()->data(parentIndex, BaseVersionList::AliasRole); + case JavaName: + return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole); case Time: return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate(); default: @@ -314,14 +314,14 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) m_columns.push_back(ParentVersion); } */ - if (roles.contains(BaseVersionList::ArchitectureRole)) { - m_columns.push_back(Architecture); + if (roles.contains(BaseVersionList::CPUArchitectureRole)) { + m_columns.push_back(CPUArchitecture); } if (roles.contains(BaseVersionList::PathRole)) { m_columns.push_back(Path); } - if (roles.contains(BaseVersionList::AliasRole)) { - m_columns.push_back(Alias); + if (roles.contains(BaseVersionList::JavaNameRole)) { + m_columns.push_back(JavaName); } if (roles.contains(Meta::VersionList::TimeRole)) { m_columns.push_back(Time); diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index 2dc35a625..cb55b7f14 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -9,7 +9,7 @@ class VersionFilterModel; class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: - enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time, Alias }; + enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName }; using FilterMap = QHash>; public: diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index c14056964..d99201f24 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -108,7 +108,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const return version->recommended; case PathRole: return version->path; - case ArchitectureRole: + case CPUArchitectureRole: return version->arch; default: return QVariant(); @@ -117,7 +117,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const BaseVersionList::RoleList JavaInstallList::providesRoles() const { - return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole }; + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, CPUArchitectureRole }; } void JavaInstallList::updateListData(QList versions) diff --git a/launcher/java/JavaRuntime.cpp b/launcher/java/JavaMetadata.cpp similarity index 86% rename from launcher/java/JavaRuntime.cpp rename to launcher/java/JavaMetadata.cpp index e88955772..b261122dc 100644 --- a/launcher/java/JavaRuntime.cpp +++ b/launcher/java/JavaMetadata.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include "java/JavaRuntime.h" +#include "java/JavaMetadata.h" #include @@ -25,7 +25,7 @@ #include "java/JavaVersion.h" #include "minecraft/ParseUtils.h" -namespace JavaRuntime { +namespace Java { DownloadType parseDownloadType(QString javaDownload) { @@ -44,9 +44,9 @@ QString downloadTypeToString(DownloadType javaDownload) } return ""; } -MetaPtr parseJavaMeta(const QJsonObject& in) +MetadataPtr parseJavaMeta(const QJsonObject& in) { - auto meta = std::make_shared(); + auto meta = std::make_shared(); meta->m_name = Json::ensureString(in, "name", ""); meta->vendor = Json::ensureString(in, "vendor", ""); @@ -74,7 +74,7 @@ MetaPtr parseJavaMeta(const QJsonObject& in) return meta; } -bool Meta::operator<(const Meta& rhs) +bool Metadata::operator<(const Metadata& rhs) { auto id = version; if (id < rhs.version) { @@ -86,32 +86,32 @@ bool Meta::operator<(const Meta& rhs) return StringUtils::naturalCompare(m_name, rhs.m_name, Qt::CaseInsensitive) < 0; } -bool Meta::operator==(const Meta& rhs) +bool Metadata::operator==(const Metadata& rhs) { return version == rhs.version && m_name == rhs.m_name; } -bool Meta::operator>(const Meta& rhs) +bool Metadata::operator>(const Metadata& rhs) { return (!operator<(rhs)) && (!operator==(rhs)); } -bool Meta::operator<(BaseVersion& a) +bool Metadata::operator<(BaseVersion& a) { try { - return operator<(dynamic_cast(a)); + return operator<(dynamic_cast(a)); } catch (const std::bad_cast& e) { return BaseVersion::operator<(a); } } -bool Meta::operator>(BaseVersion& a) +bool Metadata::operator>(BaseVersion& a) { try { - return operator>(dynamic_cast(a)); + return operator>(dynamic_cast(a)); } catch (const std::bad_cast& e) { return BaseVersion::operator>(a); } } -} // namespace JavaRuntime +} // namespace Java diff --git a/launcher/java/JavaRuntime.h b/launcher/java/JavaMetadata.h similarity index 84% rename from launcher/java/JavaRuntime.h rename to launcher/java/JavaMetadata.h index 654ba030a..dd3ae865c 100644 --- a/launcher/java/JavaRuntime.h +++ b/launcher/java/JavaMetadata.h @@ -26,11 +26,11 @@ #include "BaseVersion.h" #include "java/JavaVersion.h" -namespace JavaRuntime { +namespace Java { enum class DownloadType { Manifest, Archive }; -class Meta : public BaseVersion { +class Metadata : public BaseVersion { public: virtual QString descriptor() override { return version.toString(); } @@ -40,9 +40,9 @@ class Meta : public BaseVersion { virtual bool operator<(BaseVersion& a) override; virtual bool operator>(BaseVersion& a) override; - bool operator<(const Meta& rhs); - bool operator==(const Meta& rhs); - bool operator>(const Meta& rhs); + bool operator<(const Metadata& rhs); + bool operator==(const Metadata& rhs); + bool operator>(const Metadata& rhs); QString m_name; QString vendor; @@ -55,10 +55,10 @@ class Meta : public BaseVersion { QString packageType; JavaVersion version; }; -using MetaPtr = std::shared_ptr; +using MetadataPtr = std::shared_ptr; DownloadType parseDownloadType(QString javaDownload); QString downloadTypeToString(DownloadType javaDownload); -MetaPtr parseJavaMeta(const QJsonObject& libObj); +MetadataPtr parseJavaMeta(const QJsonObject& libObj); -} // namespace JavaRuntime \ No newline at end of file +} // namespace Java \ No newline at end of file diff --git a/launcher/java/download/ArchiveJavaDownloader.cpp b/launcher/java/download/ArchiveDownloadTask.cpp similarity index 82% rename from launcher/java/download/ArchiveJavaDownloader.cpp rename to launcher/java/download/ArchiveDownloadTask.cpp index cb642357a..847f3f364 100644 --- a/launcher/java/download/ArchiveJavaDownloader.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "java/download/ArchiveJavaDownloader.h" +#include "java/download/ArchiveDownloadTask.h" #include #include #include "MMCZip.h" @@ -25,11 +25,12 @@ #include "net/NetJob.h" #include "tasks/Task.h" -ArchiveJavaDownloader::ArchiveJavaDownloader(QUrl url, QString final_path, QString checksumType, QString checksumHash) +namespace Java { +ArchiveDownloadTask::ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash) : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash) {} -void ArchiveJavaDownloader::executeTask() +void ArchiveDownloadTask::executeTask() { // JRE found ! download the zip setStatus(tr("Downloading Java")); @@ -49,8 +50,8 @@ void ArchiveJavaDownloader::executeTask() auto fullPath = entry->getFullPath(); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download.get(), &NetJob::progress, this, &ArchiveJavaDownloader::progress); - connect(download.get(), &NetJob::failed, this, &ArchiveJavaDownloader::emitFailed); + connect(download.get(), &NetJob::progress, this, &ArchiveDownloadTask::progress); + connect(download.get(), &NetJob::failed, this, &ArchiveDownloadTask::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); connect(download.get(), &NetJob::succeeded, [this, fullPath] { // This should do all of the extracting and creating folders @@ -59,7 +60,7 @@ void ArchiveJavaDownloader::executeTask() download->start(); } -void ArchiveJavaDownloader::extractJava(QString input) +void ArchiveDownloadTask::extractJava(QString input) { setStatus(tr("Extracting java")); auto zip = std::make_shared(input); @@ -79,14 +80,14 @@ void ArchiveJavaDownloader::extractJava(QString input) connect(this, &Task::aborted, zipTask.get(), &Task::abort); connect(zipTask.get(), &Task::finished, [zipTask, this] { disconnect(this, &Task::aborted, zipTask.get(), &Task::abort); }); - connect(zipTask.get(), &Task::succeeded, this, &ArchiveJavaDownloader::emitSucceeded); - connect(zipTask.get(), &Task::aborted, this, &ArchiveJavaDownloader::emitAborted); + connect(zipTask.get(), &Task::succeeded, this, &ArchiveDownloadTask::emitSucceeded); + connect(zipTask.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted); connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); - connect(zipTask.get(), &Task::stepProgress, this, &ArchiveJavaDownloader::propagateStepProgress); + connect(zipTask.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress); connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); @@ -97,4 +98,5 @@ void ArchiveJavaDownloader::extractJava(QString input) stepProgress(*progressStep); }); zipTask->start(); -} \ No newline at end of file +} +} // namespace Java \ No newline at end of file diff --git a/launcher/java/download/ArchiveJavaDownloader.h b/launcher/java/download/ArchiveDownloadTask.h similarity index 82% rename from launcher/java/download/ArchiveJavaDownloader.h rename to launcher/java/download/ArchiveDownloadTask.h index 230f62b4d..88c0223d5 100644 --- a/launcher/java/download/ArchiveJavaDownloader.h +++ b/launcher/java/download/ArchiveDownloadTask.h @@ -21,11 +21,12 @@ #include #include "tasks/Task.h" -class ArchiveJavaDownloader : public Task { +namespace Java { +class ArchiveDownloadTask : public Task { Q_OBJECT public: - ArchiveJavaDownloader(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); - virtual ~ArchiveJavaDownloader() = default; + ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); + virtual ~ArchiveDownloadTask() = default; [[nodiscard]] bool canAbort() const override { return true; } void executeTask() override; @@ -38,6 +39,5 @@ class ArchiveJavaDownloader : public Task { QString m_final_path; QString m_checksum_type; QString m_checksum_hash; - - Task::Ptr m_current_task; -}; \ No newline at end of file +}; +} // namespace Java \ No newline at end of file diff --git a/launcher/java/download/ManifestJavaDownloader.cpp b/launcher/java/download/ManifestDownloadTask.cpp similarity index 87% rename from launcher/java/download/ManifestJavaDownloader.cpp rename to launcher/java/download/ManifestDownloadTask.cpp index c0b7942a7..1a3071530 100644 --- a/launcher/java/download/ManifestJavaDownloader.cpp +++ b/launcher/java/download/ManifestDownloadTask.cpp @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "java/download/ManifestJavaDownloader.h" +#include "java/download/ManifestDownloadTask.h" #include "Application.h" #include "FileSystem.h" @@ -30,11 +30,12 @@ struct File { bool isExec; }; -ManifestJavaDownloader::ManifestJavaDownloader(QUrl url, QString final_path, QString checksumType, QString checksumHash) +namespace Java { +ManifestDownloadTask::ManifestDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash) : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash) {} -void ManifestJavaDownloader::executeTask() +void ManifestDownloadTask::executeTask() { setStatus(tr("Downloading Java")); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); @@ -51,8 +52,8 @@ void ManifestJavaDownloader::executeTask() download->addNetAction(action); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download.get(), &NetJob::progress, this, &ManifestJavaDownloader::progress); - connect(download.get(), &NetJob::failed, this, &ManifestJavaDownloader::emitFailed); + connect(download.get(), &NetJob::progress, this, &ManifestDownloadTask::progress); + connect(download.get(), &NetJob::failed, this, &ManifestDownloadTask::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); connect(download.get(), &NetJob::succeeded, [files, this] { @@ -69,7 +70,7 @@ void ManifestJavaDownloader::executeTask() download->start(); } -void ManifestJavaDownloader::downloadJava(const QJsonDocument& doc) +void ManifestDownloadTask::downloadJava(const QJsonDocument& doc) { // valid json doc, begin making jre spot FS::ensureFolderPathExists(m_final_path); @@ -116,10 +117,11 @@ void ManifestJavaDownloader::downloadJava(const QJsonDocument& doc) disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); elementDownload->deleteLater(); }); - connect(elementDownload, &NetJob::progress, this, &ManifestJavaDownloader::progress); - connect(elementDownload, &NetJob::failed, this, &ManifestJavaDownloader::emitFailed); + connect(elementDownload, &NetJob::progress, this, &ManifestDownloadTask::progress); + connect(elementDownload, &NetJob::failed, this, &ManifestDownloadTask::emitFailed); connect(this, &Task::aborted, elementDownload, &NetJob::abort); connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); elementDownload->start(); -} \ No newline at end of file +} +} // namespace Java \ No newline at end of file diff --git a/launcher/java/download/ManifestJavaDownloader.h b/launcher/java/download/ManifestDownloadTask.h similarity index 82% rename from launcher/java/download/ManifestJavaDownloader.h rename to launcher/java/download/ManifestDownloadTask.h index d7114f4bd..dcfee4d4d 100644 --- a/launcher/java/download/ManifestJavaDownloader.h +++ b/launcher/java/download/ManifestDownloadTask.h @@ -21,11 +21,13 @@ #include #include "tasks/Task.h" -class ManifestJavaDownloader : public Task { +namespace Java { + +class ManifestDownloadTask : public Task { Q_OBJECT public: - ManifestJavaDownloader(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); - virtual ~ManifestJavaDownloader() = default; + ManifestDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); + virtual ~ManifestDownloadTask() = default; [[nodiscard]] bool canAbort() const override { return true; } void executeTask() override; @@ -38,6 +40,5 @@ class ManifestJavaDownloader : public Task { QString m_final_path; QString m_checksum_type; QString m_checksum_hash; - - Task::Ptr m_current_task; -}; \ No newline at end of file +}; +} // namespace Java \ No newline at end of file diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index 7b7ae1fa3..76b914b6a 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -101,10 +101,14 @@ QVariant VersionList::data(const QModelIndex& index, int role) const BaseVersionList::RoleList VersionList::providesRoles() const { - return { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole, - TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole }; + return m_provided_roles; } +void VersionList::setProvidedRoles(RoleList roles) +{ + m_provided_roles = roles; +}; + QHash VersionList::roleNames() const { QHash roles = BaseVersionList::roleNames(); diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index 2c5624701..0890caf60 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -47,6 +47,8 @@ class VersionList : public BaseVersionList, public BaseEntity { RoleList providesRoles() const override; QHash roleNames() const override; + void setProvidedRoles(RoleList roles); + QString localFilename() const override; QString uid() const { return m_uid; } @@ -79,6 +81,9 @@ class VersionList : public BaseVersionList, public BaseEntity { Version::Ptr m_recommended; + RoleList m_provided_roles = { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole, + TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole }; + void setupAddedVersion(int row, const Version::Ptr& version); }; } // namespace Meta diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index 5f3b4f2a2..56e9c8ca2 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -37,7 +37,7 @@ #include #include #include -#include "java/JavaRuntime.h" +#include "java/JavaMetadata.h" #include "minecraft/Agent.h" #include "minecraft/ParseUtils.h" @@ -261,9 +261,9 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc auto runtimes = requireObject(root, "runtimes"); out->runtimes = {}; for (auto key : runtimes.keys()) { - QList list; + QList list; for (auto runtime : ensureArray(runtimes, key)) { - list.append(JavaRuntime::parseJavaMeta(ensureObject(runtime))); + list.append(Java::parseJavaMeta(ensureObject(runtime))); } out->runtimes[key] = list; } diff --git a/launcher/minecraft/VersionFile.h b/launcher/minecraft/VersionFile.h index 297c19709..a9c1473d2 100644 --- a/launcher/minecraft/VersionFile.h +++ b/launcher/minecraft/VersionFile.h @@ -47,7 +47,7 @@ #include "Agent.h" #include "Library.h" #include "ProblemProvider.h" -#include "java/JavaRuntime.h" +#include "java/JavaMetadata.h" #include "minecraft/Rule.h" class PackProfile; @@ -155,7 +155,7 @@ class VersionFile : public ProblemContainer { /// is volatile -- may be removed as soon as it is no longer needed by something else bool m_volatile = false; - QHash> runtimes; + QHash> runtimes; public: // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more. diff --git a/launcher/ui/java/JavaDownload.cpp b/launcher/ui/java/JavaDownload.cpp deleted file mode 100644 index bae61a060..000000000 --- a/launcher/ui/java/JavaDownload.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2024 Trial97 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "JavaDownload.h" - -#include - -#include - -#include "Application.h" -#include "FileSystem.h" -#include "QObjectPtr.h" -#include "SysInfo.h" - -#include "java/JavaRuntime.h" -#include "java/download/ArchiveJavaDownloader.h" -#include "java/download/ManifestJavaDownloader.h" - -#include "meta/Index.h" -#include "meta/Version.h" - -#include "ui/dialogs/ProgressDialog.h" -#include "ui/java/ListModel.h" -#include "ui_JavaDownload.h" - -JavaDownload::JavaDownload(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownload) -{ - ui->setupUi(this); - ui->widget->initialize(new Java::JavaBaseVersionList("net.minecraft.java")); - ui->widget->selectCurrent(); - connect(ui->widget, &VersionSelectWidget::selectedVersionChanged, this, &JavaDownload::setSelectedVersion); - auto reset = ui->buttonBox->button(QDialogButtonBox::Reset); - connect(reset, &QPushButton::clicked, this, &JavaDownload::refresh); -} - -JavaDownload::~JavaDownload() -{ - delete ui; -} - -void JavaDownload::setSelectedVersion(BaseVersion::Ptr version) -{ - auto dcast = std::dynamic_pointer_cast(version); - if (!dcast) { - return; - } - ui->widget_2->initialize(new Java::InstallList(dcast, this)); - ui->widget_2->selectCurrent(); -} - -void JavaDownload::accept() -{ - auto meta = std::dynamic_pointer_cast(ui->widget_2->selectedVersion()); - if (!meta) { - return; - } - Task::Ptr task; - auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->m_name); - switch (meta->downloadType) { - case JavaRuntime::DownloadType::Manifest: - task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); - break; - case JavaRuntime::DownloadType::Archive: - task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); - break; - } - ProgressDialog pg(this); - pg.execWithTask(task.get()); - QDialog::accept(); -} - -void JavaDownload::refresh() -{ - ui->widget->loadList(); -} diff --git a/launcher/ui/java/JavaDownloader.cpp b/launcher/ui/java/JavaDownloader.cpp new file mode 100644 index 000000000..9121988da --- /dev/null +++ b/launcher/ui/java/JavaDownloader.cpp @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "JavaDownloader.h" + +#include + +#include + +#include "Application.h" +#include "BaseVersionList.h" +#include "FileSystem.h" +#include "QObjectPtr.h" +#include "SysInfo.h" + +#include "java/JavaMetadata.h" +#include "java/download/ArchiveDownloadTask.h" +#include "java/download/ManifestDownloadTask.h" + +#include "meta/Index.h" +#include "meta/Version.h" + +#include "meta/VersionList.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/java/VersionList.h" +#include "ui_JavaDownloader.h" + +namespace Java { + +Downloader::Downloader(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownloader) +{ + ui->setupUi(this); + auto versionList = new Meta::VersionList("net.minecraft.java", this); + versionList->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); + ui->majorVersionSelect->initialize(versionList); + ui->majorVersionSelect->selectCurrent(); + ui->majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta")); + ui->majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + + ui->javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); + ui->javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + + ui->buttonBox->button(QDialogButtonBox::Retry)->setText(tr("Refresh")); + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Download")); + + connect(ui->majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &Downloader::setSelectedVersion); + auto reset = ui->buttonBox->button(QDialogButtonBox::Reset); + connect(reset, &QPushButton::clicked, this, &Downloader::refresh); +} + +Downloader::~Downloader() +{ + delete ui; +} + +void Downloader::setSelectedVersion(BaseVersion::Ptr version) +{ + auto dcast = std::dynamic_pointer_cast(version); + if (!dcast) { + return; + } + ui->javaVersionSelect->initialize(new Java::VersionList(dcast, this)); + ui->javaVersionSelect->selectCurrent(); +} + +void Downloader::accept() +{ + auto meta = std::dynamic_pointer_cast(ui->javaVersionSelect->selectedVersion()); + if (!meta) { + return; + } + Task::Ptr task; + auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->m_name); + switch (meta->downloadType) { + case Java::DownloadType::Manifest: + task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); + break; + case Java::DownloadType::Archive: + task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); + break; + } + auto deletePath = [final_path] { FS::deletePath(final_path); }; + connect(task.get(), &Task::failed, this, deletePath); + connect(task.get(), &Task::aborted, this, deletePath); + ProgressDialog pg(this); + pg.execWithTask(task.get()); + QDialog::accept(); +} + +void Downloader::refresh() +{ + ui->majorVersionSelect->loadList(); +} +} // namespace Java diff --git a/launcher/ui/java/JavaDownload.h b/launcher/ui/java/JavaDownloader.h similarity index 84% rename from launcher/ui/java/JavaDownload.h rename to launcher/ui/java/JavaDownloader.h index de0916ae5..b8bdde41a 100644 --- a/launcher/ui/java/JavaDownload.h +++ b/launcher/ui/java/JavaDownloader.h @@ -22,15 +22,17 @@ #include "BaseVersion.h" namespace Ui { -class JavaDownload; +class JavaDownloader; } -class JavaDownload : public QDialog { +namespace Java { + +class Downloader : public QDialog { Q_OBJECT public: - explicit JavaDownload(QWidget* parent = 0); - ~JavaDownload(); + explicit Downloader(QWidget* parent = 0); + ~Downloader(); void accept(); @@ -41,5 +43,6 @@ class JavaDownload : public QDialog { void setSelectedVersion(BaseVersion::Ptr version); private: - Ui::JavaDownload* ui; + Ui::JavaDownloader* ui; }; +} // namespace Java diff --git a/launcher/ui/java/JavaDownload.ui b/launcher/ui/java/JavaDownloader.ui similarity index 86% rename from launcher/ui/java/JavaDownload.ui rename to launcher/ui/java/JavaDownloader.ui index bbc638e9b..0eeabb26d 100644 --- a/launcher/ui/java/JavaDownload.ui +++ b/launcher/ui/java/JavaDownloader.ui @@ -1,7 +1,7 @@ - JavaDownload - + JavaDownloader + 0 @@ -23,7 +23,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -67,7 +67,7 @@ buttonBox accepted() - JavaDownload + JavaDownloader accept() @@ -83,7 +83,7 @@ buttonBox rejected() - JavaDownload + JavaDownloader reject() diff --git a/launcher/ui/java/ListModel.cpp b/launcher/ui/java/VersionList.cpp similarity index 76% rename from launcher/ui/java/ListModel.cpp rename to launcher/ui/java/VersionList.cpp index 11cd01130..31353f2f7 100644 --- a/launcher/ui/java/ListModel.cpp +++ b/launcher/ui/java/VersionList.cpp @@ -16,46 +16,46 @@ * along with this program. If not, see . */ -#include "ListModel.h" +#include "VersionList.h" #include #include "BaseVersionList.h" #include "SysInfo.h" -#include "java/JavaRuntime.h" +#include "java/JavaMetadata.h" namespace Java { -InstallList::InstallList(Meta::Version::Ptr version, QObject* parent) : BaseVersionList(parent), m_version(version) +VersionList::VersionList(Meta::Version::Ptr version, QObject* parent) : BaseVersionList(parent), m_version(version) { if (version->isLoaded()) sortVersions(); } -Task::Ptr InstallList::getLoadTask() +Task::Ptr VersionList::getLoadTask() { m_version->load(Net::Mode::Online); auto task = m_version->getCurrentTask(); - connect(task.get(), &Task::finished, this, &InstallList::sortVersions); + connect(task.get(), &Task::finished, this, &VersionList::sortVersions); return task; } -const BaseVersion::Ptr InstallList::at(int i) const +const BaseVersion::Ptr VersionList::at(int i) const { return m_vlist.at(i); } -bool InstallList::isLoaded() +bool VersionList::isLoaded() { return m_version->isLoaded(); } -int InstallList::count() const +int VersionList::count() const { return m_vlist.count(); } -QVariant InstallList::data(const QModelIndex& index, int role) const +QVariant VersionList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); @@ -75,28 +75,28 @@ QVariant InstallList::data(const QModelIndex& index, int role) const return version->version.toString(); case RecommendedRole: return version->recommended; - case AliasRole: + case JavaNameRole: return version->name(); - case ArchitectureRole: + case CPUArchitectureRole: return version->vendor; default: return QVariant(); } } -BaseVersionList::RoleList InstallList::providesRoles() const +BaseVersionList::RoleList VersionList::providesRoles() const { - return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, AliasRole, ArchitectureRole }; + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, JavaNameRole, CPUArchitectureRole }; } bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) { - auto rleft = std::dynamic_pointer_cast(right); - auto rright = std::dynamic_pointer_cast(left); + auto rleft = std::dynamic_pointer_cast(right); + auto rright = std::dynamic_pointer_cast(left); return (*rleft) > (*rright); } -void InstallList::sortVersions() +void VersionList::sortVersions() { QString versionStr = SysInfo::getSupportedJavaArchitecture(); beginResetModel(); diff --git a/launcher/ui/java/ListModel.h b/launcher/ui/java/VersionList.h similarity index 71% rename from launcher/ui/java/ListModel.h rename to launcher/ui/java/VersionList.h index a157e0e8d..d334ed564 100644 --- a/launcher/ui/java/ListModel.h +++ b/launcher/ui/java/VersionList.h @@ -18,23 +18,17 @@ #pragma once -#include "java/JavaRuntime.h" -#include "meta/VersionList.h" +#include "BaseVersionList.h" +#include "java/JavaMetadata.h" +#include "meta/Version.h" namespace Java { -class JavaBaseVersionList : public Meta::VersionList { - Q_OBJECT - public: - explicit JavaBaseVersionList(const QString& uid, QObject* parent = nullptr) : VersionList(uid, parent) {} - BaseVersionList::RoleList providesRoles() const { return { VersionRole, RecommendedRole, VersionPointerRole }; } -}; - -class InstallList : public BaseVersionList { +class VersionList : public BaseVersionList { Q_OBJECT public: - explicit InstallList(Meta::Version::Ptr m_version, QObject* parent = 0); + explicit VersionList(Meta::Version::Ptr m_version, QObject* parent = 0); Task::Ptr getLoadTask() override; bool isLoaded() override; @@ -50,7 +44,7 @@ class InstallList : public BaseVersionList { protected: Meta::Version::Ptr m_version; - QList m_vlist; + QList m_vlist; }; } // namespace Java diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 09c951a3c..9b21aad72 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -36,7 +36,7 @@ #include "JavaPage.h" #include "JavaCommon.h" -#include "ui/java/JavaDownload.h" +#include "ui/java/JavaDownloader.h" #include "ui_JavaPage.h" #include @@ -169,7 +169,7 @@ void JavaPage::on_javaTestBtn_clicked() void JavaPage::on_javaDownloadBtn_clicked() { - auto jdialog = new JavaDownload(this); + auto jdialog = new Java::Downloader(this); jdialog->exec(); } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index df380bfb3..b962406d1 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -36,7 +36,7 @@ */ #include "InstanceSettingsPage.h" -#include "ui/java/JavaDownload.h" +#include "ui/java/JavaDownloader.h" #include "ui_InstanceSettingsPage.h" #include @@ -387,7 +387,7 @@ void InstanceSettingsPage::loadSettings() void InstanceSettingsPage::on_javaDownloadBtn_clicked() { - auto jdialog = new JavaDownload(this); + auto jdialog = new Java::Downloader(this); jdialog->exec(); } diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index d6fbf00a3..3f4fe08ea 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -19,7 +19,7 @@ #include "java/JavaUtils.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/java/JavaDownload.h" +#include "ui/java/JavaDownloader.h" #include "ui/widgets/VersionSelectWidget.h" #include "Application.h" @@ -274,7 +274,7 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() } void JavaSettingsWidget::on_javaDownloadBtn_clicked() { - auto jdialog = new JavaDownload(this); + auto jdialog = new Java::Downloader(this); jdialog->exec(); } void JavaSettingsWidget::on_javaStatusBtn_clicked() From 0384e652fb42ea18526a168a2370f8431cdcbc0f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 2 Feb 2024 22:42:42 +0200 Subject: [PATCH 223/755] Finished up the download dialog Signed-off-by: Trial97 --- launcher/BaseVersionList.cpp | 1 + launcher/BaseVersionList.h | 1 + launcher/VersionProxyModel.cpp | 9 +++++++++ launcher/VersionProxyModel.h | 2 +- launcher/ui/java/VersionList.cpp | 11 +++++++++-- 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index afee8388a..1456a17bd 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -112,5 +112,6 @@ QHash BaseVersionList::roleNames() const roles.insert(PathRole, "path"); roles.insert(JavaNameRole, "javaName"); roles.insert(CPUArchitectureRole, "architecture"); + roles.insert(JavaVendorRole, "javaVendor"); return roles; } diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index bc37e9e53..7517c71b4 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -50,6 +50,7 @@ class BaseVersionList : public QAbstractListModel { PathRole, JavaNameRole, CPUArchitectureRole, + JavaVendorRole, SortRole }; using RoleList = QList; diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 070e952a4..ed4c0767d 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -116,6 +116,8 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("Type"); case CPUArchitecture: return tr("Architecture"); + case JavaVendor: + return tr("Vendor"); case Path: return tr("Path"); case JavaName: @@ -135,6 +137,8 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("The version's type"); case CPUArchitecture: return tr("CPU Architecture"); + case JavaVendor: + return tr("Java vendor"); case Path: return tr("Filesystem path to this version"); case JavaName: @@ -171,6 +175,8 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const return sourceModel()->data(parentIndex, BaseVersionList::TypeRole); case CPUArchitecture: return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole); + case JavaVendor: + return sourceModel()->data(parentIndex, BaseVersionList::JavaVendorRole); case Path: return sourceModel()->data(parentIndex, BaseVersionList::PathRole); case JavaName: @@ -317,6 +323,9 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) if (roles.contains(BaseVersionList::CPUArchitectureRole)) { m_columns.push_back(CPUArchitecture); } + if (roles.contains(BaseVersionList::JavaVendorRole)) { + m_columns.push_back(JavaVendor); + } if (roles.contains(BaseVersionList::PathRole)) { m_columns.push_back(Path); } diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index cb55b7f14..5a1017bee 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -9,7 +9,7 @@ class VersionFilterModel; class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: - enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName }; + enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaVendor }; using FilterMap = QHash>; public: diff --git a/launcher/ui/java/VersionList.cpp b/launcher/ui/java/VersionList.cpp index 31353f2f7..7c5668d20 100644 --- a/launcher/ui/java/VersionList.cpp +++ b/launcher/ui/java/VersionList.cpp @@ -23,6 +23,7 @@ #include "BaseVersionList.h" #include "SysInfo.h" #include "java/JavaMetadata.h" +#include "meta/VersionList.h" namespace Java { @@ -77,8 +78,12 @@ QVariant VersionList::data(const QModelIndex& index, int role) const return version->recommended; case JavaNameRole: return version->name(); - case CPUArchitectureRole: + case JavaVendorRole: return version->vendor; + case TypeRole: + return version->packageType; + case Meta::VersionList::TimeRole: + return version->releaseTime; default: return QVariant(); } @@ -86,7 +91,8 @@ QVariant VersionList::data(const QModelIndex& index, int role) const BaseVersionList::RoleList VersionList::providesRoles() const { - return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, JavaNameRole, CPUArchitectureRole }; + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, + JavaNameRole, JavaVendorRole, TypeRole, Meta::VersionList::TimeRole }; } bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) @@ -106,6 +112,7 @@ void VersionList::sortVersions() std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); } else { m_vlist = {}; + qWarning() << "Your operating system is not yet supported: " << SysInfo::currentSystem() << " " << SysInfo::useQTForArch(); } endResetModel(); } From 82b15268bc8566be4b320c24b41113f8f25c2204 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 3 Feb 2024 00:11:05 +0200 Subject: [PATCH 224/755] preparing java autodownload Signed-off-by: Trial97 --- launcher/java/JavaVersion.h | 10 +++++----- launcher/minecraft/LaunchProfile.cpp | 9 +++++++++ launcher/minecraft/LaunchProfile.h | 4 ++++ launcher/minecraft/VersionFile.cpp | 1 + launcher/ui/java/JavaDownloader.cpp | 6 +++--- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/launcher/java/JavaVersion.h b/launcher/java/JavaVersion.h index 92c743bfb..dfb4770da 100644 --- a/launcher/java/JavaVersion.h +++ b/launcher/java/JavaVersion.h @@ -30,11 +30,11 @@ class JavaVersion { QString toString() const; - int major() { return m_major; } - int minor() { return m_minor; } - int security() { return m_security; } - QString build() { return m_prerelease; } - QString name() { return m_name; } + int major() const { return m_major; } + int minor() const { return m_minor; } + int security() const { return m_security; } + QString build() const { return m_prerelease; } + QString name() const { return m_name; } private: QString m_string; diff --git a/launcher/minecraft/LaunchProfile.cpp b/launcher/minecraft/LaunchProfile.cpp index cf819b411..77072472c 100644 --- a/launcher/minecraft/LaunchProfile.cpp +++ b/launcher/minecraft/LaunchProfile.cpp @@ -164,6 +164,10 @@ void LaunchProfile::applyCompatibleJavaMajors(QList& javaMajor) { m_compatibleJavaMajors.append(javaMajor); } +void LaunchProfile::applyCompatibleJavaName(QString javaName) +{ + m_compatibleJavaName = javaName; +} void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext) { @@ -334,6 +338,11 @@ const QList& LaunchProfile::getCompatibleJavaMajors() const return m_compatibleJavaMajors; } +const QString LaunchProfile::getCompatibleJavaName() const +{ + return m_compatibleJavaName; +} + void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext, QStringList& jars, QStringList& nativeJars, diff --git a/launcher/minecraft/LaunchProfile.h b/launcher/minecraft/LaunchProfile.h index 12b312383..f1be6fee0 100644 --- a/launcher/minecraft/LaunchProfile.h +++ b/launcher/minecraft/LaunchProfile.h @@ -59,6 +59,7 @@ class LaunchProfile : public ProblemProvider { void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext); void applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext); void applyCompatibleJavaMajors(QList& javaMajor); + void applyCompatibleJavaName(QString javaName); void applyMainJar(LibraryPtr jar); void applyProblemSeverity(ProblemSeverity severity); /// clear the profile @@ -80,6 +81,7 @@ class LaunchProfile : public ProblemProvider { const QList& getMavenFiles() const; const QList& getAgents() const; const QList& getCompatibleJavaMajors() const; + const QString getCompatibleJavaName() const; const LibraryPtr getMainJar() const; void getLibraryFiles(const RuntimeContext& runtimeContext, QStringList& jars, @@ -150,5 +152,7 @@ class LaunchProfile : public ProblemProvider { /// compatible java major versions QList m_compatibleJavaMajors; + QString m_compatibleJavaName; + ProblemSeverity m_problemSeverity = ProblemSeverity::None; }; diff --git a/launcher/minecraft/VersionFile.cpp b/launcher/minecraft/VersionFile.cpp index 6632bb8bf..8ee61128f 100644 --- a/launcher/minecraft/VersionFile.cpp +++ b/launcher/minecraft/VersionFile.cpp @@ -73,6 +73,7 @@ void VersionFile::applyTo(LaunchProfile* profile, const RuntimeContext& runtimeC profile->applyMods(mods); profile->applyTraits(traits); profile->applyCompatibleJavaMajors(compatibleJavaMajors); + profile->applyCompatibleJavaName(compatibleJavaName); for (auto library : libraries) { profile->applyLibrary(library, runtimeContext); diff --git a/launcher/ui/java/JavaDownloader.cpp b/launcher/ui/java/JavaDownloader.cpp index 9121988da..3552f0ef5 100644 --- a/launcher/ui/java/JavaDownloader.cpp +++ b/launcher/ui/java/JavaDownloader.cpp @@ -45,9 +45,9 @@ namespace Java { Downloader::Downloader(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownloader) { ui->setupUi(this); - auto versionList = new Meta::VersionList("net.minecraft.java", this); + auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); versionList->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); - ui->majorVersionSelect->initialize(versionList); + ui->majorVersionSelect->initialize(versionList.get()); ui->majorVersionSelect->selectCurrent(); ui->majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta")); ui->majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); @@ -85,7 +85,7 @@ void Downloader::accept() return; } Task::Ptr task; - auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->m_name); + auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->vendor, meta->m_name); switch (meta->downloadType) { case Java::DownloadType::Manifest: task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); From 15670cc27bbed41def06df9de749a8c645bfcc83 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 4 Feb 2024 00:19:10 +0000 Subject: [PATCH 225/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/07f6395285469419cf9d078f59b5b49993198c00' (2024-01-11) → 'github:hercules-ci/flake-parts/b253292d9c0a5ead9bc98c4e9a26c6312e27d69f' (2024-02-01) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/4fddc9be4eaf195d631333908f2a454b03628ee5' (2024-01-25) → 'github:nixos/nixpkgs/79a13f1437e149dc7be2d1290c74d378dad60814' (2024-02-03) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/f56597d53fd174f796b5a7d3ee0b494f9e2285cc' (2024-01-20) → 'github:cachix/pre-commit-hooks.nix/7c54e08a689b53c8a1e5d70169f2ec9e2a68ffaf' (2024-01-28) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index bbbab1e45..c825c54bb 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1704982712, - "narHash": "sha256-2Ptt+9h8dczgle2Oo6z5ni5rt/uLMG47UFTR1ry/wgg=", + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "07f6395285469419cf9d078f59b5b49993198c00", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", "type": "github" }, "original": { @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1706173671, - "narHash": "sha256-lciR7kQUK2FCAYuszyd7zyRRmTaXVeoZsCyK6QFpGdk=", + "lastModified": 1706925685, + "narHash": "sha256-hVInjWMmgH4yZgA4ZtbgJM1qEAel72SYhP5nOWX4UIM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4fddc9be4eaf195d631333908f2a454b03628ee5", + "rev": "79a13f1437e149dc7be2d1290c74d378dad60814", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1705757126, - "narHash": "sha256-Eksr+n4Q8EYZKAN0Scef5JK4H6FcHc+TKNHb95CWm+c=", + "lastModified": 1706424699, + "narHash": "sha256-Q3RBuOpZNH2eFA1e+IHgZLAOqDD9SKhJ/sszrL8bQD4=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "f56597d53fd174f796b5a7d3ee0b494f9e2285cc", + "rev": "7c54e08a689b53c8a1e5d70169f2ec9e2a68ffaf", "type": "github" }, "original": { From 7cac8ad2b42f19dd3f38fe5c5ef5f93116ca5aa9 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 6 Feb 2024 20:42:18 +0000 Subject: [PATCH 226/755] Use QTextFragment for inserting log lines Signed-off-by: TheKodeToad --- launcher/ui/widgets/LogView.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/launcher/ui/widgets/LogView.cpp b/launcher/ui/widgets/LogView.cpp index 4096889d3..6578b1f12 100644 --- a/launcher/ui/widgets/LogView.cpp +++ b/launcher/ui/widgets/LogView.cpp @@ -36,6 +36,7 @@ #include "LogView.h" #include #include +#include LogView::LogView(QWidget* parent) : QPlainTextEdit(parent) { @@ -117,6 +118,9 @@ void LogView::rowsAboutToBeInserted(const QModelIndex& parent, int first, int la void LogView::rowsInserted(const QModelIndex& parent, int first, int last) { + QTextDocument document; + QTextCursor cursor(&document); + for (int i = first; i <= last; i++) { auto idx = m_model->index(i, 0, parent); auto text = m_model->data(idx, Qt::DisplayRole).toString(); @@ -133,11 +137,16 @@ void LogView::rowsInserted(const QModelIndex& parent, int first, int last) if (bg.isValid()) { format.setBackground(bg.value()); } - auto workCursor = textCursor(); - workCursor.movePosition(QTextCursor::End); - workCursor.insertText(text, format); - workCursor.insertBlock(); + cursor.movePosition(QTextCursor::End); + cursor.insertText(text, format); + cursor.insertBlock(); } + + QTextDocumentFragment fragment(&document); + QTextCursor workCursor = textCursor(); + workCursor.movePosition(QTextCursor::End); + workCursor.insertFragment(fragment); + if (m_scroll && !m_scrolling) { m_scrolling = true; QMetaObject::invokeMethod(this, "scrollToBottom", Qt::QueuedConnection); From ba990e075bc9bea8b03857a643005f6fe9301a34 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 8 Feb 2024 19:45:46 +0200 Subject: [PATCH 227/755] Added JavaPath function Signed-off-by: Trial97 --- launcher/Application.cpp | 7 ++++--- launcher/Application.h | 3 +++ launcher/java/JavaUtils.cpp | 28 +++++++++++++++++++++++++++- launcher/java/JavaUtils.h | 1 + launcher/ui/java/JavaDownloader.cpp | 2 +- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 679de711e..20da46187 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -48,7 +48,6 @@ #include "net/PasteUpload.h" #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" -#include "settings/INIFile.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" @@ -106,8 +105,6 @@ #include "icons/IconList.h" #include "net/HttpMetaCache.h" -#include "java/JavaUtils.h" - #include "updater/ExternalUpdater.h" #include "tools/JProfiler.h" @@ -1833,3 +1830,7 @@ QUrl Application::normalizeImportUrl(QString const& url) return QUrl::fromUserInput(url); } } +const QString Application::javaPath() +{ + return FS::PathCombine(m_dataPath, "java"); +} diff --git a/launcher/Application.h b/launcher/Application.h index 85bf2dff4..ba65edd82 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -162,6 +162,9 @@ class Application : public QApplication { /// the data path the application is using const QString& dataRoot() { return m_dataPath; } + /// the java installed path the application is using + const QString javaPath(); + bool isPortable() { return m_portable; } const Capabilities capabilities() { return m_capabilities; } diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 67f1fd3a7..ff5ba5efe 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -337,6 +337,7 @@ QList JavaUtils::FindJavaPaths() } candidates.append(getMinecraftJavaBundle()); + candidates.append(getPrismJavaBundle()); candidates = addJavasFromEnv(candidates); candidates.removeDuplicates(); return candidates; @@ -363,6 +364,7 @@ QList JavaUtils::FindJavaPaths() javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); } javas.append(getMinecraftJavaBundle()); + javas.append(getPrismJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -393,7 +395,6 @@ QList JavaUtils::FindJavaPaths() scanJavaDir(snap + dirPath); } }; - scanJavaDir(FS::PathCombine(APPLICATION->dataRoot(), "java")); // oracle RPMs scanJavaDirs("/usr/java"); // general locations used by distro packaging @@ -416,6 +417,7 @@ QList JavaUtils::FindJavaPaths() scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); javas.append(getMinecraftJavaBundle()); + javas.append(getPrismJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -429,6 +431,8 @@ QList JavaUtils::FindJavaPaths() javas.append(this->GetDefaultJava()->path); javas.append(getMinecraftJavaBundle()); + javas.append(getPrismJavaBundle()); + javas.removeDuplicates(); return addJavasFromEnv(javas); } #endif @@ -484,3 +488,25 @@ QStringList getMinecraftJavaBundle() } return javas; } + +QStringList getPrismJavaBundle() +{ + QList javas; + QDir dir(APPLICATION->javaPath()); + if (!dir.exists()) + return javas; + + QString executable = "java"; +#if defined(Q_OS_WIN32) + executable += "w.exe"; +#endif + + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (auto& entry : entries) { + QString prefix; + prefix = entry.canonicalFilePath(); + javas.append(FS::PathCombine(prefix, "jre", "bin", executable)); + javas.append(FS::PathCombine(prefix, "bin", executable)); + } + return javas; +} diff --git a/launcher/java/JavaUtils.h b/launcher/java/JavaUtils.h index 0beb8c67a..aa5315a19 100644 --- a/launcher/java/JavaUtils.h +++ b/launcher/java/JavaUtils.h @@ -26,6 +26,7 @@ QString stripVariableEntries(QString name, QString target, QString remove); QProcessEnvironment CleanEnviroment(); QStringList getMinecraftJavaBundle(); +QStringList getPrismJavaBundle(); class JavaUtils : public QObject { Q_OBJECT diff --git a/launcher/ui/java/JavaDownloader.cpp b/launcher/ui/java/JavaDownloader.cpp index 3552f0ef5..4495210c3 100644 --- a/launcher/ui/java/JavaDownloader.cpp +++ b/launcher/ui/java/JavaDownloader.cpp @@ -85,7 +85,7 @@ void Downloader::accept() return; } Task::Ptr task; - auto final_path = FS::PathCombine(APPLICATION->dataRoot(), "java", meta->vendor, meta->m_name); + auto final_path = FS::PathCombine(APPLICATION->javaPath(), meta->m_name); switch (meta->downloadType) { case Java::DownloadType::Manifest: task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); From 3d29da916ded28e320eda2654ba42f529e682019 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 8 Feb 2024 20:15:19 +0200 Subject: [PATCH 228/755] Made Java path configurable Signed-off-by: Trial97 --- launcher/Application.cpp | 3 +- launcher/ui/pages/global/LauncherPage.cpp | 12 +++ launcher/ui/pages/global/LauncherPage.h | 1 + launcher/ui/pages/global/LauncherPage.ui | 107 +++++++++++++--------- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 20da46187..cc4fc6b1c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -586,6 +586,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); + m_settings->registerSetting("JavaDir", "java"); // Editors m_settings->registerSetting("JsonEditor", QString()); @@ -1832,5 +1833,5 @@ QUrl Application::normalizeImportUrl(QString const& url) } const QString Application::javaPath() { - return FS::PathCombine(m_dataPath, "java"); + return m_settings->get("JavaDir").toString(); } diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 78c44380a..99a80d98c 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -173,6 +173,16 @@ void LauncherPage::on_downloadsDirBrowseBtn_clicked() } } +void LauncherPage::on_javaDirBrowseBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Java Folder"), ui->javaDirTextBox->text()); + + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { + QString cooked_dir = FS::NormalizePath(raw_dir); + ui->javaDirTextBox->setText(cooked_dir); + } +} + void LauncherPage::on_metadataDisableBtn_clicked() { ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); @@ -208,6 +218,7 @@ void LauncherPage::applySettings() s->set("CentralModsDir", ui->modsDirTextBox->text()); s->set("IconsDir", ui->iconsDirTextBox->text()); s->set("DownloadsDir", ui->downloadsDirTextBox->text()); + s->set("JavaDir", ui->javaDirTextBox->text()); s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked()); auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId(); @@ -269,6 +280,7 @@ void LauncherPage::loadSettings() ui->modsDirTextBox->setText(s->get("CentralModsDir").toString()); ui->iconsDirTextBox->setText(s->get("IconsDir").toString()); ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString()); + ui->javaDirTextBox->setText(s->get("JavaDir").toString()); ui->downloadsDirWatchRecursiveCheckBox->setChecked(s->get("DownloadsDirWatchRecursive").toBool()); QString sortMode = s->get("InstSortMode").toString(); diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index e733224d2..32945626f 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -74,6 +74,7 @@ class LauncherPage : public QWidget, public BasePage { void on_modsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked(); void on_downloadsDirBrowseBtn_clicked(); + void on_javaDirBrowseBtn_clicked(); void on_metadataDisableBtn_clicked(); /*! diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 18b52e1b8..81acb0a17 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -7,7 +7,7 @@ 0 0 511 - 629 + 654 @@ -67,13 +67,20 @@ Folders - - + + - &Downloads: + Browse - - downloadsDirTextBox + + + + + + When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge). + + + Check downloads folder recursively @@ -87,25 +94,52 @@ - - - - - - - - - - + Browse + + + + + + + Browse + + + + + + + + + + + + + &Icons: + + + iconsDirTextBox + + + + + + + &Downloads: + + + downloadsDirTextBox + + + @@ -123,40 +157,23 @@ - - + + + + Java: + + + + + + + + Browse - - - - Browse - - - - - - - &Icons: - - - iconsDirTextBox - - - - - - - When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge). - - - Check downloads folder recursively - - -
    From b3fc07d44478aa7fc841c88fe9160c462b05577a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 8 Feb 2024 21:47:59 +0200 Subject: [PATCH 229/755] Added extra java search paths Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/java/JavaUtils.cpp | 27 ++++++++--- launcher/ui/pages/global/JavaPage.cpp | 29 ++++++++++- launcher/ui/pages/global/JavaPage.h | 5 +- launcher/ui/pages/global/JavaPage.ui | 69 +++++++++++++++++++++++++-- 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index cc4fc6b1c..d9abccfdc 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -629,6 +629,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("JvmArgs", ""); m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); + m_settings->registerSetting("JavaExtraSearchPaths", QStringList()); // Legacy settings m_settings->registerSetting("OnlineFixes", false); diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index ff5ba5efe..cd9593980 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -492,21 +492,34 @@ QStringList getMinecraftJavaBundle() QStringList getPrismJavaBundle() { QList javas; - QDir dir(APPLICATION->javaPath()); - if (!dir.exists()) - return javas; QString executable = "java"; #if defined(Q_OS_WIN32) executable += "w.exe"; #endif - auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (auto& entry : entries) { - QString prefix; - prefix = entry.canonicalFilePath(); + auto scanDir = [&](QString prefix) { javas.append(FS::PathCombine(prefix, "jre", "bin", executable)); javas.append(FS::PathCombine(prefix, "bin", executable)); + javas.append(FS::PathCombine(prefix, executable)); + }; + auto scanJavaDir = [&](const QString& dirPath) { + QDir dir(dirPath); + if (!dir.exists()) + return; + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (auto& entry : entries) { + scanDir(entry.canonicalFilePath()); + } + }; + + scanJavaDir(APPLICATION->javaPath()); + + auto extra_paths = APPLICATION->settings()->get("JavaExtraSearchPaths").toStringList(); + for (auto& entry : extra_paths) { + scanDir(entry); + scanJavaDir(entry); } + return javas; } diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 9b21aad72..6d278983d 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "ui/dialogs/VersionSelectDialog.h" @@ -57,7 +58,6 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) { ui->setupUi(this); - ui->tabWidget->tabBar()->hide(); loadSettings(); updateThresholds(); @@ -95,6 +95,7 @@ void JavaPage::applySettings() s->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " ")); s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked()); s->set("IgnoreJavaWizard", ui->skipJavaWizardCheckbox->isChecked()); + s->set("JavaExtraSearchPaths", m_extra_paths->stringList()); JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget()); } void JavaPage::loadSettings() @@ -117,6 +118,8 @@ void JavaPage::loadSettings() ui->jvmArgsTextBox->setPlainText(s->get("JvmArgs").toString()); ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool()); ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); + m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList()); + ui->extraJavaPathsList->setModel(m_extra_paths); } void JavaPage::on_javaDetectBtn_clicked() @@ -217,3 +220,27 @@ void JavaPage::updateThresholds() ui->labelMaxMemIcon->setPixmap(pix); } } + +void JavaPage::on_addExtraPathButton_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Add Extra Java Folder")); + + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { + QString cooked_dir = FS::NormalizePath(raw_dir); + auto currentList = m_extra_paths->stringList(); + if (!currentList.contains(cooked_dir)) { + currentList << cooked_dir; + m_extra_paths->setStringList(currentList); + } + } + APPLICATION->settings()->set("JavaExtraSearchPaths", m_extra_paths->stringList()); +} + +void JavaPage::on_removeExtraPathButton_clicked() +{ + auto indexes = ui->extraJavaPathsList->selectionModel()->selectedIndexes(); + if (indexes.size()) { + m_extra_paths->removeRow(indexes.first().row()); + } + APPLICATION->settings()->set("JavaExtraSearchPaths", m_extra_paths->stringList()); +} diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index 2bbfdf6bf..d1315db64 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "JavaCommon.h" #include "ui/pages/BasePage.h" @@ -73,10 +73,13 @@ class JavaPage : public QWidget, public BasePage { void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); void on_javaDownloadBtn_clicked(); + void on_addExtraPathButton_clicked(); + void on_removeExtraPathButton_clicked(); void on_maxMemSpinBox_valueChanged(int i); void checkerFinished(); private: Ui::JavaPage* ui; unique_qobject_ptr checker; + QStringListModel* m_extra_paths; }; diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 382c183c9..d57e2e548 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -32,11 +32,11 @@ - 0 + 1 - + - Tab 1 + General @@ -312,6 +312,69 @@ + + + Management + + + + + + Java extra paths + + + + + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + From 379f1a70e99a9a629c3cb174a1ad596fcb79a3cb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 8 Feb 2024 22:37:57 +0200 Subject: [PATCH 230/755] Leave headers alone Signed-off-by: Trial97 --- launcher/Application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index d9abccfdc..498427856 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -48,6 +48,7 @@ #include "net/PasteUpload.h" #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" +#include "settings/INIFile.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" @@ -105,6 +106,8 @@ #include "icons/IconList.h" #include "net/HttpMetaCache.h" +#include "java/JavaUtils.h" + #include "updater/ExternalUpdater.h" #include "tools/JProfiler.h" From 0a3303bcbdabbc2e5e1338dabfb3b8cf518d29e4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 9 Feb 2024 21:07:12 +0200 Subject: [PATCH 231/755] Added button to add extra java path to the Java Wizzard Signed-off-by: Trial97 --- launcher/ui/dialogs/VersionSelectDialog.h | 4 ---- launcher/ui/widgets/JavaSettingsWidget.cpp | 27 ++++++++++++++++++++-- launcher/ui/widgets/JavaSettingsWidget.h | 5 +++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/launcher/ui/dialogs/VersionSelectDialog.h b/launcher/ui/dialogs/VersionSelectDialog.h index 0ccd45e74..17efc1b93 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.h +++ b/launcher/ui/dialogs/VersionSelectDialog.h @@ -26,10 +26,6 @@ class QDialogButtonBox; class VersionSelectWidget; class QPushButton; -namespace Ui { -class VersionSelectDialog; -} - class VersionProxyModel; class VersionSelectDialog : public QDialog { diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 3f4fe08ea..bb57e5da7 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -42,6 +42,7 @@ JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent) connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited); connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked); connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaDownloadBtn_clicked); + connect(m_addJavaPathBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_addJavaPathBtn_clicked); } void JavaSettingsWidget::setupUi() @@ -124,9 +125,16 @@ void JavaSettingsWidget::setupUi() m_verticalLayout->addWidget(m_memoryGroupBox); - m_javaDownloadBtn = new QPushButton(tr("Download Java"), this); + m_horizontalBtnLayout = new QHBoxLayout(); + m_horizontalBtnLayout->setObjectName(QStringLiteral("horizontalBtnLayout")); - m_verticalLayout->addWidget(m_javaDownloadBtn); + m_javaDownloadBtn = new QPushButton(tr("Download Java"), this); + m_horizontalBtnLayout->addWidget(m_javaDownloadBtn); + + m_addJavaPathBtn = new QPushButton(tr("Add extra Java path"), this); + m_horizontalBtnLayout->addWidget(m_addJavaPathBtn); + + m_verticalLayout->addLayout(m_horizontalBtnLayout); retranslate(); } @@ -437,3 +445,18 @@ void JavaSettingsWidget::updateThresholds() m_labelMaxMemIcon->setPixmap(pix); } } + +void JavaSettingsWidget::on_addJavaPathBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Add Extra Java Folder")); + + auto currentList = APPLICATION->settings()->get("JavaExtraSearchPaths").toStringList(); + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { + QString cooked_dir = FS::NormalizePath(raw_dir); + if (!currentList.contains(cooked_dir)) { + currentList << cooked_dir; + } + } + APPLICATION->settings()->set("JavaExtraSearchPaths", currentList); + refresh(); +} diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index b7db955a4..385a9256a 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -25,7 +25,7 @@ class JavaSettingsWidget : public QWidget { public: explicit JavaSettingsWidget(QWidget* parent); - virtual ~JavaSettingsWidget(){}; + virtual ~JavaSettingsWidget() = default; enum class JavaStatus { NotSet, Pending, Good, DoesNotExist, DoesNotStart, ReturnedInvalidData } javaStatus = JavaStatus::NotSet; @@ -51,6 +51,7 @@ class JavaSettingsWidget : public QWidget { void on_javaBrowseBtn_clicked(); void on_javaStatusBtn_clicked(); void on_javaDownloadBtn_clicked(); + void on_addJavaPathBtn_clicked(); void checkFinished(const JavaChecker::Result& result); protected: /* methods */ @@ -78,7 +79,9 @@ class JavaSettingsWidget : public QWidget { QLabel* m_labelPermGen = nullptr; QSpinBox* m_permGenSpinBox = nullptr; + QHBoxLayout* m_horizontalBtnLayout = nullptr; QPushButton* m_javaDownloadBtn = nullptr; + QPushButton* m_addJavaPathBtn = nullptr; QIcon goodIcon; QIcon yellowIcon; QIcon badIcon; From 27d662e64241a75eee963048a669398a5083f7ed Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 9 Feb 2024 22:47:39 +0200 Subject: [PATCH 232/755] Added management for downloaded javas from prism Signed-off-by: Trial97 --- launcher/java/JavaInstallList.cpp | 10 +++-- launcher/java/JavaInstallList.h | 6 ++- launcher/java/JavaUtils.cpp | 31 ++++++++++++++ launcher/java/JavaUtils.h | 1 + launcher/ui/pages/global/JavaPage.cpp | 40 ++++++++++++++++++ launcher/ui/pages/global/JavaPage.h | 2 + launcher/ui/pages/global/JavaPage.ui | 60 ++++++++++++++++++++++++++- 7 files changed, 143 insertions(+), 7 deletions(-) diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index d99201f24..4172ba2cf 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -46,7 +46,9 @@ #include "java/JavaUtils.h" #include "tasks/ConcurrentTask.h" -JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {} +JavaInstallList::JavaInstallList(QObject* parent, bool onlyManagedVersions) + : BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions) +{} Task::Ptr JavaInstallList::getLoadTask() { @@ -66,7 +68,7 @@ void JavaInstallList::load() { if (m_status != Status::InProgress) { m_status = Status::InProgress; - m_load_task.reset(new JavaListLoadTask(this)); + m_load_task.reset(new JavaListLoadTask(this, m_only_managed_versions)); m_load_task->start(); } } @@ -148,7 +150,7 @@ void JavaInstallList::sortVersions() endResetModel(); } -JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task() +JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions) : Task(), m_only_managed_versions(onlyManagedVersions) { m_list = vlist; m_current_recommended = NULL; @@ -159,7 +161,7 @@ void JavaListLoadTask::executeTask() setStatus(tr("Detecting Java installations...")); JavaUtils ju; - QList candidate_paths = ju.FindJavaPaths(); + QList candidate_paths = m_only_managed_versions ? getPrismJavaBundle() : ju.FindJavaPaths(); ConcurrentTask::Ptr job(new ConcurrentTask(this, "Java detection", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); m_job.reset(job); diff --git a/launcher/java/JavaInstallList.h b/launcher/java/JavaInstallList.h index 08f0b310d..c68c2a3be 100644 --- a/launcher/java/JavaInstallList.h +++ b/launcher/java/JavaInstallList.h @@ -33,7 +33,7 @@ class JavaInstallList : public BaseVersionList { enum class Status { NotDone, InProgress, Done }; public: - explicit JavaInstallList(QObject* parent = 0); + explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false); Task::Ptr getLoadTask() override; bool isLoaded() override; @@ -55,13 +55,14 @@ class JavaInstallList : public BaseVersionList { Status m_status = Status::NotDone; shared_qobject_ptr m_load_task; QList m_vlist; + bool m_only_managed_versions; }; class JavaListLoadTask : public Task { Q_OBJECT public: - explicit JavaListLoadTask(JavaInstallList* vlist); + explicit JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions = false); virtual ~JavaListLoadTask() = default; protected: @@ -74,4 +75,5 @@ class JavaListLoadTask : public Task { JavaInstallList* m_list; JavaInstall* m_current_recommended; QList m_results; + bool m_only_managed_versions; }; diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index cd9593980..1d254e405 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -338,6 +338,7 @@ QList JavaUtils::FindJavaPaths() candidates.append(getMinecraftJavaBundle()); candidates.append(getPrismJavaBundle()); + candidates.append(getPrismExtraJavaPaths()); candidates = addJavasFromEnv(candidates); candidates.removeDuplicates(); return candidates; @@ -365,6 +366,7 @@ QList JavaUtils::FindJavaPaths() } javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); + javas.append(getPrismExtraJavaPaths()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -418,6 +420,7 @@ QList JavaUtils::FindJavaPaths() javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); + javas.append(getPrismExtraJavaPaths()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -432,6 +435,7 @@ QList JavaUtils::FindJavaPaths() javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); + javas.append(getPrismExtraJavaPaths()); javas.removeDuplicates(); return addJavasFromEnv(javas); } @@ -515,6 +519,33 @@ QStringList getPrismJavaBundle() scanJavaDir(APPLICATION->javaPath()); + return javas; +} + +QStringList getPrismExtraJavaPaths() +{ + QList javas; + + QString executable = "java"; +#if defined(Q_OS_WIN32) + executable += "w.exe"; +#endif + + auto scanDir = [&](QString prefix) { + javas.append(FS::PathCombine(prefix, "jre", "bin", executable)); + javas.append(FS::PathCombine(prefix, "bin", executable)); + javas.append(FS::PathCombine(prefix, executable)); + }; + auto scanJavaDir = [&](const QString& dirPath) { + QDir dir(dirPath); + if (!dir.exists()) + return; + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (auto& entry : entries) { + scanDir(entry.canonicalFilePath()); + } + }; + auto extra_paths = APPLICATION->settings()->get("JavaExtraSearchPaths").toStringList(); for (auto& entry : extra_paths) { scanDir(entry); diff --git a/launcher/java/JavaUtils.h b/launcher/java/JavaUtils.h index aa5315a19..66e439834 100644 --- a/launcher/java/JavaUtils.h +++ b/launcher/java/JavaUtils.h @@ -27,6 +27,7 @@ QString stripVariableEntries(QString name, QString target, QString remove); QProcessEnvironment CleanEnviroment(); QStringList getMinecraftJavaBundle(); QStringList getPrismJavaBundle(); +QStringList getPrismExtraJavaPaths(); class JavaUtils : public QObject { Q_OBJECT diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 6d278983d..7d7530e35 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -36,6 +36,8 @@ #include "JavaPage.h" #include "JavaCommon.h" +#include "java/JavaInstall.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui/java/JavaDownloader.h" #include "ui_JavaPage.h" @@ -59,6 +61,11 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) { ui->setupUi(this); + ui->managedJavaList->initialize(new JavaInstallList(this, true)); + ui->managedJavaList->selectCurrent(); + ui->managedJavaList->setEmptyString(tr("No java versions are currently available in the meta")); + ui->managedJavaList->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + loadSettings(); updateThresholds(); } @@ -244,3 +251,36 @@ void JavaPage::on_removeExtraPathButton_clicked() } APPLICATION->settings()->set("JavaExtraSearchPaths", m_extra_paths->stringList()); } + +void JavaPage::on_downloadJavaButton_clicked() +{ + on_javaDownloadBtn_clicked(); +} + +void JavaPage::on_removeJavaButton_clicked() +{ + auto version = ui->managedJavaList->selectedVersion(); + auto dcast = std::dynamic_pointer_cast(version); + if (!dcast) { + return; + } + QDir dir(APPLICATION->javaPath()); + + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (auto& entry : entries) { + if (dcast->path.startsWith(entry.canonicalFilePath())) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"), + tr("You are about to remove \"%1\" java version.\n" + "Are you sure?") + .arg(entry.fileName()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) { + FS::deletePath(entry.canonicalFilePath()); + ui->managedJavaList->loadList(); + } + break; + } + } +} diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index d1315db64..03d14b824 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -75,6 +75,8 @@ class JavaPage : public QWidget, public BasePage { void on_javaDownloadBtn_clicked(); void on_addExtraPathButton_clicked(); void on_removeExtraPathButton_clicked(); + void on_downloadJavaButton_clicked(); + void on_removeJavaButton_clicked(); void on_maxMemSpinBox_valueChanged(int i); void checkerFinished(); diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index d57e2e548..a518b0b14 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -32,7 +32,7 @@ - 1 + 0 @@ -317,6 +317,56 @@ Management + + + + Downloaded Java Versions + + + + + + + 0 + 0 + + + + + + + + + + Download + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + @@ -379,6 +429,14 @@ + + + VersionSelectWidget + QWidget +
    ui/widgets/VersionSelectWidget.h
    + 1 +
    +
    minMemSpinBox maxMemSpinBox From c0fb053ccc966045df42e8b218a994cfcfdef986 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 9 Feb 2024 23:14:49 +0200 Subject: [PATCH 233/755] Added warning for x86 java Signed-off-by: Trial97 --- launcher/java/JavaInstall.h | 1 + launcher/java/JavaInstallList.cpp | 1 + launcher/ui/pages/global/JavaPage.cpp | 8 ++++++++ launcher/ui/pages/instance/InstanceSettingsPage.cpp | 10 ++++++++++ launcher/ui/widgets/JavaSettingsWidget.cpp | 4 ++++ 5 files changed, 24 insertions(+) diff --git a/launcher/java/JavaInstall.h b/launcher/java/JavaInstall.h index 8c2743a00..6890264f3 100644 --- a/launcher/java/JavaInstall.h +++ b/launcher/java/JavaInstall.h @@ -40,6 +40,7 @@ struct JavaInstall : public BaseVersion { QString arch; QString path; bool recommended = false; + bool is_64bit = false; }; using JavaInstallPtr = std::shared_ptr; diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index 4172ba2cf..569fda306 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -193,6 +193,7 @@ void JavaListLoadTask::javaCheckerFinished() javaVersion->id = result.javaVersion; javaVersion->arch = result.realPlatform; javaVersion->path = result.path; + javaVersion->is_64bit = result.is_64bit; candidates.append(javaVersion); qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path; diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 7d7530e35..9b2aa7637 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -145,6 +145,14 @@ void JavaPage::on_javaDetectBtn_clicked() if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) { java = std::dynamic_pointer_cast(vselect.selectedVersion()); ui->javaPathTextBox->setText(java->path); + if (!java->is_64bit && APPLICATION->settings()->get("MaxMemAlloc").toInt() > 2048) { + CustomMessageBox::selectable(this, tr("Confirm Selection"), + tr("You selected an x86 java version.\n" + "This means that will not support more than 2Gb(2048Mb) of ram.\n" + "Please make sure that the maximum memory value is lower."), + QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) + ->exec(); + } } } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index b962406d1..926e48952 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -36,6 +36,7 @@ */ #include "InstanceSettingsPage.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui/java/JavaDownloader.h" #include "ui_InstanceSettingsPage.h" @@ -412,6 +413,15 @@ void InstanceSettingsPage::on_javaDetectBtn_clicked() ui->labelPermGen->setVisible(visible); ui->labelPermgenNote->setVisible(visible); m_settings->set("PermGenVisible", visible); + + if (!java->is_64bit && m_settings->get("MaxMemAlloc").toInt() > 2048) { + CustomMessageBox::selectable(this, tr("Confirm Selection"), + tr("You selected an x86 java version.\n" + "This means that will not support more than 2Gb(2048Mb) of ram.\n" + "Please make sure that the maximum memory value is lower."), + QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) + ->exec(); + } } } diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index bb57e5da7..507c632d5 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -433,6 +433,10 @@ void JavaSettingsWidget::updateThresholds() } else if (observedMaxMemory < observedMinMemory) { iconName = "status-yellow"; m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); + } else if (observedMaxMemory > 2048 && m_result.is_64bit) { + iconName = "status-bad"; + m_labelMaxMemIcon->setToolTip( + tr("Your maximum memory allocation exceeds selected java posible memory(due to x86 applicatiion limitations).")); } else { iconName = "status-good"; m_labelMaxMemIcon->setToolTip(""); From 031a9f4738df8977d8273968c2b3def6cc425fa2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 10 Feb 2024 11:03:51 +0200 Subject: [PATCH 234/755] Replaced QFile::remove with FS::deletePath Signed-off-by: Trial97 --- launcher/Application.cpp | 9 ++------ launcher/BaseInstaller.cpp | 3 ++- launcher/InstanceCreationTask.cpp | 3 ++- launcher/MMCZip.cpp | 22 +++++++++---------- launcher/icons/IconList.cpp | 2 +- launcher/meta/BaseEntity.cpp | 4 ++-- launcher/minecraft/Component.cpp | 2 +- launcher/minecraft/PackProfile.cpp | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 2 +- .../atlauncher/ATLPackInstallTask.cpp | 4 ++-- .../modplatform/helpers/OverrideUtils.cpp | 2 +- .../updater/prismupdater/PrismUpdater.cpp | 11 +++------- 12 files changed, 29 insertions(+), 37 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 42343ff8f..25ee8fa69 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -389,20 +389,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log"; static const QString logBase = FS::PathCombine("logs", baseLogFile); - auto moveFile = [](const QString& oldName, const QString& newName) { - QFile::remove(newName); - QFile::copy(oldName, newName); - QFile::remove(oldName); - }; if (FS::ensureFolderPathExists("logs")) { // if this did not fail for (auto i = 0; i <= 4; i++) if (auto oldName = baseLogFile.arg(i); QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there - moveFile(oldName, logBase.arg(i)); + FS::move(oldName, logBase.arg(i)); } for (auto i = 4; i > 0; i--) - moveFile(logBase.arg(i - 1), logBase.arg(i)); + FS::move(logBase.arg(i - 1), logBase.arg(i)); logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { diff --git a/launcher/BaseInstaller.cpp b/launcher/BaseInstaller.cpp index 1ff86ed40..96a3b5ebe 100644 --- a/launcher/BaseInstaller.cpp +++ b/launcher/BaseInstaller.cpp @@ -16,6 +16,7 @@ #include #include "BaseInstaller.h" +#include "FileSystem.h" #include "minecraft/MinecraftInstance.h" BaseInstaller::BaseInstaller() {} @@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to) bool BaseInstaller::remove(MinecraftInstance* from) { - return QFile::remove(filename(from->instanceRoot())); + return FS::deletePath(filename(from->instanceRoot())); } QString BaseInstaller::filename(const QString& root) const diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp index 73dc17891..9688fa112 100644 --- a/launcher/InstanceCreationTask.cpp +++ b/launcher/InstanceCreationTask.cpp @@ -2,6 +2,7 @@ #include #include +#include "FileSystem.h" InstanceCreationTask::InstanceCreationTask() = default; @@ -47,7 +48,7 @@ void InstanceCreationTask::executeTask() if (!QFile::exists(path)) continue; qDebug() << "Removing" << path; - if (!QFile::remove(path)) { + if (!FS::deletePath(path)) { qCritical() << "Couldn't remove the old conflicting files."; emitFailed(tr("Failed to remove old conflicting files.")); return; diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index b81106a59..b6bfac25d 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -122,7 +122,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, QuaZip zip(fileCompressed); QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(fileCompressed); + FS::deletePath(fileCompressed); return false; } @@ -130,7 +130,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, zip.close(); if (zip.getZipError() != 0) { - QFile::remove(fileCompressed); + FS::deletePath(fileCompressed); return false; } @@ -143,7 +143,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListtype() == ResourceType::ZIPFILE) { if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { zipOut.close(); - QFile::remove(targetJarPath); + FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } @@ -170,7 +170,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo(); if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); - QFile::remove(targetJarPath); + FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } @@ -193,7 +193,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo().fileName() << "to the jar."; return false; } @@ -201,7 +201,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo().fileName() << "to the jar."; return false; } @@ -209,7 +209,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList ZipResult void ExportToZipTask::finish() { if (m_build_zip_future.isCanceled()) { - QFile::remove(m_output_path); + FS::deletePath(m_output_path); emitAborted(); } else if (auto result = m_build_zip_future.result(); result.has_value()) { - QFile::remove(m_output_path); + FS::deletePath(m_output_path); emitFailed(result.value()); } else { emitSucceeded(); diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index 5576b9745..e4157ea2d 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -322,7 +322,7 @@ const MMCIcon* IconList::icon(const QString& key) const bool IconList::deleteIcon(const QString& key) { - return iconFileExists(key) && QFile::remove(icon(key)->getFilePath()); + return iconFileExists(key) && FS::deletePath(icon(key)->getFilePath()); } bool IconList::trashIcon(const QString& key) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 5f9804e48..8a99e3303 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,6 +15,7 @@ #include "BaseEntity.h" +#include "FileSystem.h" #include "Json.h" #include "net/ApiDownload.h" #include "net/HttpMetaCache.h" @@ -83,8 +84,7 @@ bool Meta::BaseEntity::loadLocalFile() } catch (const Exception& e) { qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); // just make sure it's gone and we never consider it again. - QFile::remove(fname); - return false; + return !FS::deletePath(fname); } } diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index 79ea7a06d..ad2e4023c 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -336,7 +336,7 @@ bool Component::revert() bool result = true; // just kill the file and reload if (QFile::exists(filename)) { - result = QFile::remove(filename); + result = FS::deletePath(filename); } if (result) { // file gone... diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 180f8aa30..4b17cdf07 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -839,7 +839,7 @@ bool PackProfile::installCustomJar_internal(QString filepath) QFileInfo jarInfo(finalPath); if (jarInfo.exists()) { - if (!QFile::remove(finalPath)) { + if (!FS::deletePath(finalPath)) { return false; } } diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 9157f35f0..5bea720d0 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -111,7 +111,7 @@ bool ResourceFolderModel::installResource(QString original_path) case ResourceType::ZIPFILE: case ResourceType::LITEMOD: { if (QFile::exists(new_path) || QFile::exists(new_path + QString(".disabled"))) { - if (!QFile::remove(new_path)) { + if (!FS::deletePath(new_path)) { qCritical() << "Cleaning up new location (" << new_path << ") was unsuccessful!"; return false; } diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 8ae8145de..57660aa6d 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -282,7 +282,7 @@ void PackInstallTask::deleteExistingFiles() // Delete the files for (const auto& item : filesToDelete) { - QFile::remove(item); + FS::deletePath(item); } } @@ -987,7 +987,7 @@ bool PackInstallTask::extractMods(const QMap& toExtract, // the copy from the Configs.zip QFileInfo fileInfo(to); if (fileInfo.exists()) { - if (!QFile::remove(to)) { + if (!FS::deletePath(to)) { qWarning() << "Failed to delete" << to; return false; } diff --git a/launcher/modplatform/helpers/OverrideUtils.cpp b/launcher/modplatform/helpers/OverrideUtils.cpp index 65b5f7603..60983a5cf 100644 --- a/launcher/modplatform/helpers/OverrideUtils.cpp +++ b/launcher/modplatform/helpers/OverrideUtils.cpp @@ -10,7 +10,7 @@ void createOverrides(const QString& name, const QString& parent_folder, const QS { QString file_path(FS::PathCombine(parent_folder, name + ".txt")); if (QFile::exists(file_path)) - QFile::remove(file_path); + FS::deletePath(file_path); FS::ensureFilePathExists(file_path); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 5fe22bdd0..8948b3e82 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -352,15 +352,10 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar FS::ensureFolderPathExists(FS::PathCombine(m_dataPath, "logs")); static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); - auto moveFile = [](const QString& oldName, const QString& newName) { - QFile::remove(newName); - QFile::copy(oldName, newName); - QFile::remove(oldName); - }; if (FS::ensureFolderPathExists("logs")) { // enough history to track both launches of the updater during a portable install - moveFile(logBase.arg(1), logBase.arg(2)); - moveFile(logBase.arg(0), logBase.arg(1)); + FS::move(logBase.arg(1), logBase.arg(2)); + FS::move(logBase.arg(0), logBase.arg(1)); } logFile = std::unique_ptr(new QFile(logBase.arg(0))); @@ -924,7 +919,7 @@ bool PrismUpdaterApp::callAppImageUpdate() void PrismUpdaterApp::clearUpdateLog() { - QFile::remove(m_updateLogPath); + FS::deletePath(m_updateLogPath); } void PrismUpdaterApp::logUpdate(const QString& msg) From 830ce246e17e381807d9394819c175dc0b730ff8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 10 Feb 2024 13:48:02 +0200 Subject: [PATCH 235/755] Fixed curseforge neoforge import for 1.20.1 versions Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameInstanceCreationTask.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 7c3dfe6d4..93745bacf 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -354,6 +354,8 @@ bool FlameCreationTask::createInstance() auto id = loader.id; if (id.startsWith("neoforge-")) { id.remove("neoforge-"); + if (id.startsWith("1.20.1-")) + id = id.mid(7); // this is a mess for curseforge loaderType = "neoforge"; loaderUid = "net.neoforged"; } else if (id.startsWith("forge-")) { From 29bf6cc68c132c036f8c9ee90a337fb2db7d3cf2 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sat, 10 Feb 2024 23:15:26 +0200 Subject: [PATCH 236/755] Update launcher/modplatform/flame/FlameInstanceCreationTask.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/modplatform/flame/FlameInstanceCreationTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 93745bacf..ef552c3c2 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -355,7 +355,7 @@ bool FlameCreationTask::createInstance() if (id.startsWith("neoforge-")) { id.remove("neoforge-"); if (id.startsWith("1.20.1-")) - id = id.mid(7); // this is a mess for curseforge + id.remove("1.20.1-"); // this is a mess for curseforge loaderType = "neoforge"; loaderUid = "net.neoforged"; } else if (id.startsWith("forge-")) { From 0f254c842543d08ed5520c359ad8e13f0a751b39 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Feb 2024 00:19:31 +0000 Subject: [PATCH 237/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/79a13f1437e149dc7be2d1290c74d378dad60814' (2024-02-03) → 'github:nixos/nixpkgs/442d407992384ed9c0e6d352de75b69079904e4e' (2024-02-09) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/7c54e08a689b53c8a1e5d70169f2ec9e2a68ffaf' (2024-01-28) → 'github:cachix/pre-commit-hooks.nix/0db2e67ee49910adfa13010e7f012149660af7f0' (2024-02-07) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index c825c54bb..11bf876f9 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1706925685, - "narHash": "sha256-hVInjWMmgH4yZgA4ZtbgJM1qEAel72SYhP5nOWX4UIM=", + "lastModified": 1707451808, + "narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=", "owner": "nixos", "repo": "nixpkgs", - "rev": "79a13f1437e149dc7be2d1290c74d378dad60814", + "rev": "442d407992384ed9c0e6d352de75b69079904e4e", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1706424699, - "narHash": "sha256-Q3RBuOpZNH2eFA1e+IHgZLAOqDD9SKhJ/sszrL8bQD4=", + "lastModified": 1707297608, + "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "7c54e08a689b53c8a1e5d70169f2ec9e2a68ffaf", + "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", "type": "github" }, "original": { From ded77e61832661557a6985d9aa587f3b5d1e1f87 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 11 Feb 2024 15:32:17 +0000 Subject: [PATCH 238/755] Fix NetJob use-after-free Signed-off-by: TheKodeToad --- launcher/modplatform/helpers/NetworkResourceAPI.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 16ec90ec2..225583764 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -43,7 +43,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& callbacks.on_succeed(doc); }); - QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) { + QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) { int network_error_code = -1; if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -102,7 +102,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi callbacks.on_succeed(doc, args.pack); }); - QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) { + QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) { int network_error_code = -1; if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -153,7 +153,7 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, callbacks.on_succeed(doc, args.dependency); }); - QObject::connect(netJob.get(), &NetJob::failed, [&netJob, callbacks](QString reason) { + QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) { int network_error_code = -1; if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); From ff989b4305d5ae4578d2154afe27ce6bf72cadaf Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 11 Feb 2024 15:38:39 +0000 Subject: [PATCH 239/755] Fix ResourceModel error display Signed-off-by: TheKodeToad --- launcher/ui/pages/modplatform/ResourceModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 8a70eb4de..b38108d28 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -209,7 +209,7 @@ void ResourceModel::loadEntry(QModelIndex& entry) }; if (!callbacks.on_fail) callbacks.on_fail = [](QString reason, int) { - QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project versions:%1").arg(reason)); + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project versions: %1").arg(reason)); }; if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) @@ -232,7 +232,7 @@ void ResourceModel::loadEntry(QModelIndex& entry) callbacks.on_fail = [this](QString reason) { if (!s_running_models.constFind(this).value()) return; - QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info:%1").arg(reason)); + QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info: %1").arg(reason)); }; if (!callbacks.on_abort) callbacks.on_abort = [this] { From fdaaee64d193472144dd95c15c8d03f6fc4ed83a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 11 Feb 2024 21:49:28 +0200 Subject: [PATCH 240/755] Fixed curseforge neoforge export for 1.20.1 Signed-off-by: Trial97 --- launcher/modplatform/flame/FlamePackExportTask.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 3827170b3..e28c82b4c 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -398,8 +398,12 @@ QByteArray FlamePackExportTask::generateIndex() id = "fabric-" + fabric->getVersion(); else if (forge != nullptr) id = "forge-" + forge->getVersion(); - else if (neoforge != nullptr) - id = "neoforge-" + neoforge->getVersion(); + else if (neoforge != nullptr) { + id = "neoforge-"; + if (minecraft->m_version == "1.20.1") + id += "1.20.1-"; + id += neoforge->getVersion(); + } version["modLoaders"] = QJsonArray(); if (!id.isEmpty()) { QJsonObject loader; From 7f4c74ffbe413fcf191842606c12056fbc6493c7 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sun, 11 Feb 2024 22:15:39 +0000 Subject: [PATCH 241/755] Fix immediate free in Exception.h Signed-off-by: TheKodeToad --- launcher/Exception.h | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/launcher/Exception.h b/launcher/Exception.h index ef1e4e0d8..0e869fb14 100644 --- a/launcher/Exception.h +++ b/launcher/Exception.h @@ -1,4 +1,37 @@ -// Licensed under the Apache-2.0 license. See README.md for details. +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #pragma once @@ -8,12 +41,12 @@ class Exception : public std::exception { public: - Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; } - Exception(const Exception& other) : std::exception(), m_message(other.cause()) {} + Exception(const QString& message) : std::exception(), m_message(message.toUtf8()) { qCritical() << "Exception:" << message; } + Exception(const Exception& other) : std::exception(), m_message(other.m_message) {} virtual ~Exception() noexcept {} - const char* what() const noexcept { return m_message.toLatin1().constData(); } - QString cause() const { return m_message; } + const char* what() const noexcept { return m_message.constData(); } + QString cause() const { return QString::fromUtf8(m_message); } private: - QString m_message; + QByteArray m_message; }; From 8cf2a04f317c59920e8af90d7e16fea872b2c122 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 12 Feb 2024 15:24:56 +0000 Subject: [PATCH 242/755] Fix formatting issues Signed-off-by: TheKodeToad --- launcher/Exception.h | 2 +- launcher/ui/pages/modplatform/ResourceModel.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/launcher/Exception.h b/launcher/Exception.h index 0e869fb14..55b40fdc8 100644 --- a/launcher/Exception.h +++ b/launcher/Exception.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-3.0-only +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 TheKodeToad diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index b38108d28..f3c7ff60b 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -209,7 +209,8 @@ void ResourceModel::loadEntry(QModelIndex& entry) }; if (!callbacks.on_fail) callbacks.on_fail = [](QString reason, int) { - QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project versions: %1").arg(reason)); + QMessageBox::critical(nullptr, tr("Error"), + tr("A network error occurred. Could not load project versions: %1").arg(reason)); }; if (auto job = m_api->getProjectVersions(std::move(args), std::move(callbacks)); job) From 01180c20e19fcdd9a902d9cd51514549eeceb531 Mon Sep 17 00:00:00 2001 From: Echo J Date: Tue, 13 Feb 2024 23:54:49 +0200 Subject: [PATCH 243/755] Use absolute path to load MangoHUD library This should finally fix the Arch Linux Java symbol issue with MangoHUD enabled: https://bugs.archlinux.org/task/77183 Signed-off-by: Echo J --- launcher/minecraft/MinecraftInstance.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 5a8c0d28a..edeebd34d 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -594,9 +594,6 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() QStringList preloadList; if (auto value = env.value("LD_PRELOAD"); !value.isEmpty()) preloadList = value.split(QLatin1String(":")); - QStringList libPaths; - if (auto value = env.value("LD_LIBRARY_PATH"); !value.isEmpty()) - libPaths = value.split(QLatin1String(":")); auto mangoHudLibString = MangoHud::getLibraryString(); if (!mangoHudLibString.isEmpty()) { @@ -604,18 +601,16 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() QString libPath = mangoHudLib.absolutePath(); auto appendLib = [libPath, &preloadList](QString fileName) { if (QFileInfo(FS::PathCombine(libPath, fileName)).exists()) - preloadList << fileName; + preloadList << FS::PathCombine(libPath, fileName); }; // dlsym variant is only needed for OpenGL and not included in the vulkan layer appendLib("libMangoHud_dlsym.so"); appendLib("libMangoHud_opengl.so"); appendLib(mangoHudLib.fileName()); - libPaths << libPath; } env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":"))); - env.insert("LD_LIBRARY_PATH", libPaths.join(QLatin1String(":"))); env.insert("MANGOHUD", "1"); } From 5d36067c749c55b4ca3ff37fa7382dcd54e60ca2 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:21:53 +0100 Subject: [PATCH 244/755] chore: update libraries updated qt to 6.6.2 updated some submodules updated sparkle framework to the latest version Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- CMakeLists.txt | 2 +- flatpak/shared-modules | 2 +- libraries/cmark | 2 +- libraries/filesystem | 2 +- libraries/libnbtplusplus | 2 +- libraries/quazip | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9b3597c3..a26e6c094 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,7 +76,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.6.1' + qt_version: '6.6.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -88,7 +88,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.6.1' + qt_version: '6.6.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -98,7 +98,7 @@ jobs: qt_ver: 6 qt_host: mac qt_arch: '' - qt_version: '6.6.1' + qt_version: '6.6.2' qt_modules: 'qt5compat qtimageformats' qt_tools: '' diff --git a/CMakeLists.txt b/CMakeLists.txt index e42186cb5..5fe85c80c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,7 +381,7 @@ if(UNIX AND APPLE) set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") - set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive") + set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz" CACHE STRING "URL to Sparkle release archive") set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle") diff --git a/flatpak/shared-modules b/flatpak/shared-modules index 55a8e460c..f2b0c16a2 160000 --- a/flatpak/shared-modules +++ b/flatpak/shared-modules @@ -1 +1 @@ -Subproject commit 55a8e460c6343229597a13e973ba4855c27a1c4c +Subproject commit f2b0c16a2a217a1822ce5a6538ba8f755ed1dd32 diff --git a/libraries/cmark b/libraries/cmark index 5ba25ff40..8fbf02968 160000 --- a/libraries/cmark +++ b/libraries/cmark @@ -1 +1 @@ -Subproject commit 5ba25ff40eba44c811f79ab6a792baf945b8307c +Subproject commit 8fbf029685482827828b5858444157052f1b0a5f diff --git a/libraries/filesystem b/libraries/filesystem index 8a2edd6d9..2fc4b4637 160000 --- a/libraries/filesystem +++ b/libraries/filesystem @@ -1 +1 @@ -Subproject commit 8a2edd6d92ed820521d42c94d179462bf06b5ed3 +Subproject commit 2fc4b463759e043476fc0036da094e5877e3dd50 diff --git a/libraries/libnbtplusplus b/libraries/libnbtplusplus index a5e8fd52b..23b955121 160000 --- a/libraries/libnbtplusplus +++ b/libraries/libnbtplusplus @@ -1 +1 @@ -Subproject commit a5e8fd52b8bf4ab5d5bcc042b2a247867589985f +Subproject commit 23b955121b8217c1c348a9ed2483167a6f3ff4ad diff --git a/libraries/quazip b/libraries/quazip index 6117161af..9d3aa3ee9 160000 --- a/libraries/quazip +++ b/libraries/quazip @@ -1 +1 @@ -Subproject commit 6117161af08e366c37499895b00ef62f93adc345 +Subproject commit 9d3aa3ee948c1cde5a9f873ecbc3bb229c1182ee From 94b48e970241a122da43957ea457b29fa7ce499d Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:28:46 +0100 Subject: [PATCH 245/755] fix: remove cmark_static cmark 0.31 removed CMARK_STATIC/CMARK_SHARED and replaced it with the native cmake BUILD_SHARED_LIBS Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fe85c80c..e4e224f1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -504,11 +504,10 @@ else() endif() if(NOT cmark_FOUND) message(STATUS "Using bundled cmark") - set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE) - set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE) - set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE) + set(BUILD_TESTING 0) + set(BUILD_SHARED_LIBS 0) add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser - add_library(cmark::cmark ALIAS cmark_static) + add_library(cmark::cmark ALIAS cmark) else() message(STATUS "Using system cmark") endif() From 25b69b4c0d7d0b6863c7f158c382eb85cf040c14 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:29:49 +0100 Subject: [PATCH 246/755] fix: fix sparkle hash :/ oops Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4e224f1c..6cceb2599 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -382,7 +382,7 @@ if(UNIX AND APPLE) set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz" CACHE STRING "URL to Sparkle release archive") - set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive") + set(MACOSX_SPARKLE_SHA256 "572dd67ae398a466f19f343a449e1890bac1ef74885b4739f68f979a8a89884b" CACHE STRING "SHA256 checksum for Sparkle release archive") set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle") # directories to look for dependencies From 2941307581114c435a2950df59e8ddc2c3c7c0c4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 16 Feb 2024 23:42:39 +0200 Subject: [PATCH 247/755] Added basic java auto-detect and auto-download Signed-off-by: Trial97 --- launcher/Application.cpp | 2 + launcher/CMakeLists.txt | 2 + launcher/minecraft/MinecraftInstance.cpp | 12 +- launcher/minecraft/launch/AutoInstallJava.cpp | 192 ++++++++++++++++++ launcher/minecraft/launch/AutoInstallJava.h | 67 ++++++ .../minecraft/launch/VerifyJavaInstall.cpp | 45 +--- launcher/ui/pages/global/JavaPage.cpp | 4 + launcher/ui/pages/global/JavaPage.ui | 66 +++--- 8 files changed, 325 insertions(+), 65 deletions(-) create mode 100644 launcher/minecraft/launch/AutoInstallJava.cpp create mode 100644 launcher/minecraft/launch/AutoInstallJava.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 498427856..6b7333972 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -633,6 +633,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); m_settings->registerSetting("JavaExtraSearchPaths", QStringList()); + m_settings->registerSetting("AutomaticJavaSwitch", false); + m_settings->registerSetting("AutomaticJavaDownload", false); // Legacy settings m_settings->registerSetting("OnlineFixes", false); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index c7945d1b8..fcc3c2f3a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -281,6 +281,8 @@ set(MINECRAFT_SOURCES minecraft/launch/ScanModFolders.h minecraft/launch/VerifyJavaInstall.cpp minecraft/launch/VerifyJavaInstall.h + minecraft/launch/AutoInstallJava.cpp + minecraft/launch/AutoInstallJava.h minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4229f73eb..00ce405aa 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -38,6 +38,8 @@ #include "MinecraftInstance.h" #include "Application.h" #include "BuildConfig.h" +#include "QObjectPtr.h" +#include "minecraft/launch/AutoInstallJava.h" #include "minecraft/launch/CreateGameFolders.h" #include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/PrintInstanceInfo.h" @@ -1041,11 +1043,6 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(makeShared(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher)); } - // check java - { - process->appendStep(makeShared(pptr)); - } - // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) { process->appendStep(makeShared(pptr)); @@ -1105,6 +1102,11 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt { process->appendStep(makeShared(pptr)); } + // check java + { + process->appendStep(makeShared(pptr)); + process->appendStep(makeShared(pptr)); + } // verify that minimum Java requirements are met { diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp new file mode 100644 index 000000000..0d3740c93 --- /dev/null +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AutoInstallJava.h" +#include +#include +#include + +#include "Application.h" +#include "FileSystem.h" +#include "MessageLevel.h" +#include "SysInfo.h" +#include "java/JavaInstall.h" +#include "java/JavaInstallList.h" +#include "java/JavaVersion.h" +#include "java/download/ArchiveDownloadTask.h" +#include "java/download/ManifestDownloadTask.h" +#include "meta/Index.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "net/Mode.h" + +AutoInstallJava::AutoInstallJava(LaunchTask* parent) + : LaunchStep(parent) + , m_instance(std::dynamic_pointer_cast(m_parent->instance())) + , m_supported_arch(SysInfo::getSupportedJavaArchitecture()){}; + +void AutoInstallJava::executeTask() +{ + auto settings = m_instance->settings(); + if (!APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() || + (settings->get("OverrideJava").toBool() && settings->get("OverrideJavaLocation").toBool())) { + emitSucceeded(); + return; + } + auto packProfile = m_instance->getPackProfile(); + if (!APPLICATION->settings()->get("AutomaticJavaDownload").toBool()) { + auto javas = APPLICATION->javalist().get(); + m_current_task = javas->getLoadTask(); + connect(m_current_task.get(), &Task::finished, this, [this, javas, packProfile] { + for (auto i = 0; i < javas->count(); i++) { + auto java = std::dynamic_pointer_cast(javas->at(i)); + if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) { + setJavaPath(java->path); + return; + } + } + emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emitSucceeded(); + }); + return; + } + auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); + QDir javaDir(APPLICATION->javaPath()); + auto wantedJavaPath = javaDir.absoluteFilePath(wantedJavaName); + if (QFileInfo::exists(wantedJavaPath)) { + setJavaPathFromPartial(); + return; + } + auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); + m_current_task = versionList->getLoadTask(); + connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::tryNextMajorJava); + connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::emitFailed); +} + +void AutoInstallJava::setJavaPath(QString path) +{ + auto settings = m_instance->settings(); + settings->set("OverrideJava", true); + settings->set("OverrideJavaLocation", true); + settings->set("JavaPath", path); + emit logLine(tr("Compatible java found at: %1.").arg(path), MessageLevel::Info); + emitSucceeded(); +} + +void AutoInstallJava::setJavaPathFromPartial() +{ + QString executable = "java"; +#if defined(Q_OS_WIN32) + executable += "w.exe"; +#endif + auto packProfile = m_instance->getPackProfile(); + auto javaName = packProfile->getProfile()->getCompatibleJavaName(); + QDir javaDir(APPLICATION->javaPath()); + // just checking if the executable is there should suffice + // but if needed this can be achieved through refreshing the javalist + // and retrieving the path that contains the java name + auto relativeBinary = FS::PathCombine(javaName, "bin", executable); + auto finalPath = javaDir.absoluteFilePath(relativeBinary); + if (QFileInfo::exists(finalPath)) { + setJavaPath(finalPath); + } else { + emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emitSucceeded(); + } + return; +} + +void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName) +{ + auto runtimes = version->data()->runtimes; + if (runtimes.contains(m_supported_arch)) { + for (auto java : runtimes.value(m_supported_arch)) { + if (java->name() == javaName) { + Task::Ptr task; + QDir javaDir(APPLICATION->javaPath()); + auto final_path = javaDir.absoluteFilePath(java->m_name); + switch (java->downloadType) { + case Java::DownloadType::Manifest: + task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + break; + case Java::DownloadType::Archive: + task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + break; + } + QEventLoop loop; + auto deletePath = [final_path] { FS::deletePath(final_path); }; + connect(task.get(), &Task::failed, this, [this, deletePath](QString reason) { + deletePath(); + emitFailed(reason); + }); + connect(this, &Task::aborted, this, [task, deletePath] { + task->abort(); + deletePath(); + }); + connect(task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); + task->start(); + return; + } + } + } + tryNextMajorJava(); +} + +void AutoInstallJava::tryNextMajorJava() +{ + if (!isRunning()) + return; + auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); + auto packProfile = m_instance->getPackProfile(); + auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); + auto majorJavaVersions = packProfile->getProfile()->getCompatibleJavaMajors(); + if (m_majorJavaVersionIndex >= majorJavaVersions.length()) { + emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emitSucceeded(); + return; + } + auto majorJavaVersion = majorJavaVersions[m_majorJavaVersionIndex]; + m_majorJavaVersionIndex++; + + auto javaMajor = versionList->getVersion(QString("java%1").arg(majorJavaVersion)); + javaMajor->load(Net::Mode::Online); + auto task = javaMajor->getCurrentTask(); + if (javaMajor->isLoaded() || !task) { + downloadJava(javaMajor, wantedJavaName); + } else { + connect(task.get(), &Task::succeeded, this, [this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); }); + connect(task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); + } +} diff --git a/launcher/minecraft/launch/AutoInstallJava.h b/launcher/minecraft/launch/AutoInstallJava.h new file mode 100644 index 000000000..4dcd3796a --- /dev/null +++ b/launcher/minecraft/launch/AutoInstallJava.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include "java/JavaMetadata.h" +#include "meta/Version.h" +#include "minecraft/MinecraftInstance.h" +#include "tasks/Task.h" + +class AutoInstallJava : public LaunchStep { + Q_OBJECT + + public: + explicit AutoInstallJava(LaunchTask* parent); + ~AutoInstallJava() override = default; + + void executeTask() override; + bool canAbort() const override { return m_current_task ? m_current_task->canAbort() : false; } + + protected: + void setJavaPath(QString path); + void setJavaPathFromPartial(); + void downloadJava(Meta::Version::Ptr version, QString javaName); + void tryNextMajorJava(); + + private: + MinecraftInstancePtr m_instance; + Task::Ptr m_current_task; + + qsizetype m_majorJavaVersionIndex = 0; + const QString m_supported_arch; +}; diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index 218811290..279545fed 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -50,7 +50,6 @@ void VerifyJavaInstall::executeTask() auto settings = instance->settings(); auto storedVersion = settings->get("JavaVersion").toString(); auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool(); - auto automaticJavaSwitch = settings->get("AutomaticJavaSwitch").toBool(); auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors(); @@ -67,38 +66,16 @@ void VerifyJavaInstall::executeTask() return; } - auto logFail = [this, &javaVersion, compatibleMajors] { - emit logLine(tr("This instance is not compatible with Java version %1.\n" - "Please switch to one of the following Java versions for this instance:") - .arg(javaVersion.major()), - MessageLevel::Error); - for (auto major : compatibleMajors) { - emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); - } - emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " - "you're doing."), - MessageLevel::Error); - - emitFailed(QString("Incompatible Java major version")); - }; - - if (automaticJavaSwitch || true) { - settings->set("OverrideJava", true); - auto javas = APPLICATION->javalist().get(); - auto task = javas->getLoadTask(); - connect(task.get(), &Task::finished, this, [this, javas, compatibleMajors, settings, &logFail] { - for (auto i = 0; i < javas->count(); i++) { - auto java = std::dynamic_pointer_cast(javas->at(i)); - if (java && compatibleMajors.contains(java->id.major())) { - settings->set("OverrideJavaLocation", true); - settings->set("JavaPath", java->path); - emitSucceeded(); - return; - } - } - logFail(); - }); - } else { - logFail(); + emit logLine(tr("This instance is not compatible with Java version %1.\n" + "Please switch to one of the following Java versions for this instance:") + .arg(javaVersion.major()), + MessageLevel::Error); + for (auto major : compatibleMajors) { + emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); } + emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " + "you're doing."), + MessageLevel::Error); + + emitFailed(QString("Incompatible Java major version")); } diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 9b2aa7637..496cb69a7 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -102,6 +102,8 @@ void JavaPage::applySettings() s->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " ")); s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked()); s->set("IgnoreJavaWizard", ui->skipJavaWizardCheckbox->isChecked()); + s->set("AutomaticJavaSwitch", ui->autodetectJavaCheckBox->isChecked()); + s->set("AutomaticJavaDownload", ui->autodownloadCheckBox->isChecked()); s->set("JavaExtraSearchPaths", m_extra_paths->stringList()); JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget()); } @@ -125,6 +127,8 @@ void JavaPage::loadSettings() ui->jvmArgsTextBox->setPlainText(s->get("JvmArgs").toString()); ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool()); ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); + ui->autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); + ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaDownload").toBool()); m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList()); ui->extraJavaPathsList->setModel(m_extra_paths); } diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index a518b0b14..d135adc9a 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -160,25 +160,6 @@ Java Runtime - - - - true - - - - 0 - 0 - - - - - 16777215 - 100 - - - - @@ -232,7 +213,7 @@
    - + @@ -248,6 +229,42 @@ + + + + If enabled, the launcher will not prompt you to choose a Java version if one isn't found. + + + Skip Java &Wizard + + + + + + + true + + + + 0 + 0 + + + + + 16777215 + 100 + + + + + + + + Autodetect Java version + + + @@ -284,13 +301,10 @@ - - - - If enabled, the launcher will not prompt you to choose a Java version if one isn't found. - + + - Skip Java &Wizard + Autodownload Mojang Java From ea2adf909dfe10c0adaf53ef2fabbd94ee7ae8cd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 17 Feb 2024 11:14:32 +0200 Subject: [PATCH 248/755] Added progeess dialog to autodownload java Signed-off-by: Trial97 --- .../java/download/ArchiveDownloadTask.cpp | 5 +- .../java/download/ManifestDownloadTask.cpp | 10 +++- launcher/minecraft/launch/AutoInstallJava.cpp | 59 ++++++++++++++----- launcher/minecraft/launch/AutoInstallJava.h | 1 + 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index 847f3f364..9f8dc0543 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -50,9 +50,12 @@ void ArchiveDownloadTask::executeTask() auto fullPath = entry->getFullPath(); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download.get(), &NetJob::progress, this, &ArchiveDownloadTask::progress); connect(download.get(), &NetJob::failed, this, &ArchiveDownloadTask::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); + connect(download.get(), &Task::progress, this, &ArchiveDownloadTask::setProgress); + connect(download.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress); + connect(download.get(), &Task::status, this, &ArchiveDownloadTask::setStatus); + connect(download.get(), &Task::details, this, &ArchiveDownloadTask::setDetails); connect(download.get(), &NetJob::succeeded, [this, fullPath] { // This should do all of the extracting and creating folders extractJava(fullPath); diff --git a/launcher/java/download/ManifestDownloadTask.cpp b/launcher/java/download/ManifestDownloadTask.cpp index 1a3071530..0c2eee0c8 100644 --- a/launcher/java/download/ManifestDownloadTask.cpp +++ b/launcher/java/download/ManifestDownloadTask.cpp @@ -52,9 +52,12 @@ void ManifestDownloadTask::executeTask() download->addNetAction(action); connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); - connect(download.get(), &NetJob::progress, this, &ManifestDownloadTask::progress); connect(download.get(), &NetJob::failed, this, &ManifestDownloadTask::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); + connect(download.get(), &Task::progress, this, &ManifestDownloadTask::setProgress); + connect(download.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress); + connect(download.get(), &Task::status, this, &ManifestDownloadTask::setStatus); + connect(download.get(), &Task::details, this, &ManifestDownloadTask::setDetails); connect(download.get(), &NetJob::succeeded, [files, this] { QJsonParseError parse_error{}; @@ -117,8 +120,11 @@ void ManifestDownloadTask::downloadJava(const QJsonDocument& doc) disconnect(this, &Task::aborted, elementDownload, &NetJob::abort); elementDownload->deleteLater(); }); - connect(elementDownload, &NetJob::progress, this, &ManifestDownloadTask::progress); connect(elementDownload, &NetJob::failed, this, &ManifestDownloadTask::emitFailed); + connect(elementDownload, &Task::progress, this, &ManifestDownloadTask::setProgress); + connect(elementDownload, &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress); + connect(elementDownload, &Task::status, this, &ManifestDownloadTask::setStatus); + connect(elementDownload, &Task::details, this, &ManifestDownloadTask::setDetails); connect(this, &Task::aborted, elementDownload, &NetJob::abort); connect(elementDownload, &NetJob::succeeded, [this] { emitSucceeded(); }); diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp index 0d3740c93..d93276432 100644 --- a/launcher/minecraft/launch/AutoInstallJava.cpp +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -34,8 +34,8 @@ */ #include "AutoInstallJava.h" -#include -#include +#include +#include #include #include "Application.h" @@ -61,7 +61,8 @@ void AutoInstallJava::executeTask() { auto settings = m_instance->settings(); if (!APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() || - (settings->get("OverrideJava").toBool() && settings->get("OverrideJavaLocation").toBool())) { + (settings->get("OverrideJava").toBool() && settings->get("OverrideJavaLocation").toBool() && + QFileInfo::exists(settings->get("JavaPath").toString()))) { emitSucceeded(); return; } @@ -80,6 +81,11 @@ void AutoInstallJava::executeTask() emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); }); + connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); + connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); + connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); + connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); + emit progressReportingRequest(); return; } auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); @@ -93,6 +99,11 @@ void AutoInstallJava::executeTask() m_current_task = versionList->getLoadTask(); connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::tryNextMajorJava); connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::emitFailed); + connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); + connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); + connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); + connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); + emit progressReportingRequest(); } void AutoInstallJava::setJavaPath(QString path) @@ -134,29 +145,34 @@ void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName) if (runtimes.contains(m_supported_arch)) { for (auto java : runtimes.value(m_supported_arch)) { if (java->name() == javaName) { - Task::Ptr task; QDir javaDir(APPLICATION->javaPath()); auto final_path = javaDir.absoluteFilePath(java->m_name); switch (java->downloadType) { case Java::DownloadType::Manifest: - task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + m_current_task = + makeShared(java->url, final_path, java->checksumType, java->checksumHash); break; case Java::DownloadType::Archive: - task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + m_current_task = + makeShared(java->url, final_path, java->checksumType, java->checksumHash); break; } - QEventLoop loop; auto deletePath = [final_path] { FS::deletePath(final_path); }; - connect(task.get(), &Task::failed, this, [this, deletePath](QString reason) { + connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) { deletePath(); emitFailed(reason); }); - connect(this, &Task::aborted, this, [task, deletePath] { - task->abort(); + connect(this, &Task::aborted, this, [this, deletePath] { + m_current_task->abort(); deletePath(); }); - connect(task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); - task->start(); + connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); + connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); + connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); + connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); + connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); + connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); + m_current_task->start(); return; } } @@ -182,11 +198,22 @@ void AutoInstallJava::tryNextMajorJava() auto javaMajor = versionList->getVersion(QString("java%1").arg(majorJavaVersion)); javaMajor->load(Net::Mode::Online); - auto task = javaMajor->getCurrentTask(); - if (javaMajor->isLoaded() || !task) { + m_current_task = javaMajor->getCurrentTask(); + if (javaMajor->isLoaded() || !m_current_task) { downloadJava(javaMajor, wantedJavaName); } else { - connect(task.get(), &Task::succeeded, this, [this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); }); - connect(task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); + connect(m_current_task.get(), &Task::succeeded, this, + [this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); }); + connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); + connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); + connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); + connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); + connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); } } +bool AutoInstallJava::abort() +{ + if (m_current_task && m_current_task->canAbort()) + return m_current_task->abort(); + return true; +} diff --git a/launcher/minecraft/launch/AutoInstallJava.h b/launcher/minecraft/launch/AutoInstallJava.h index 4dcd3796a..45d0f870e 100644 --- a/launcher/minecraft/launch/AutoInstallJava.h +++ b/launcher/minecraft/launch/AutoInstallJava.h @@ -51,6 +51,7 @@ class AutoInstallJava : public LaunchStep { void executeTask() override; bool canAbort() const override { return m_current_task ? m_current_task->canAbort() : false; } + bool abort() override; protected: void setJavaPath(QString path); From 5232b3cd89ce70722865bc1ad6d96d8f52db1789 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 17 Feb 2024 12:17:00 +0200 Subject: [PATCH 249/755] Added some logs and fixed natives extraction Signed-off-by: Trial97 --- launcher/minecraft/MinecraftInstance.cpp | 11 ++++++----- launcher/minecraft/launch/AutoInstallJava.cpp | 5 ++++- launcher/minecraft/launch/VerifyJavaInstall.cpp | 11 +++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 00ce405aa..cfc65428c 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1078,6 +1078,12 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(makeShared(pptr, Net::Mode::Offline)); } + // check java + { + process->appendStep(makeShared(pptr)); + process->appendStep(makeShared(pptr)); + } + // if there are any jar mods { process->appendStep(makeShared(pptr)); @@ -1102,11 +1108,6 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt { process->appendStep(makeShared(pptr)); } - // check java - { - process->appendStep(makeShared(pptr)); - process->appendStep(makeShared(pptr)); - } // verify that minimum Java requirements are met { diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp index d93276432..7905689da 100644 --- a/launcher/minecraft/launch/AutoInstallJava.cpp +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -68,12 +68,15 @@ void AutoInstallJava::executeTask() } auto packProfile = m_instance->getPackProfile(); if (!APPLICATION->settings()->get("AutomaticJavaDownload").toBool()) { - auto javas = APPLICATION->javalist().get(); + auto javas = APPLICATION->javalist(); m_current_task = javas->getLoadTask(); connect(m_current_task.get(), &Task::finished, this, [this, javas, packProfile] { for (auto i = 0; i < javas->count(); i++) { auto java = std::dynamic_pointer_cast(javas->at(i)); if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) { + if (!java->is_64bit) { + emit logLine(tr("The automatic Java mechanism detected a x32 java."), MessageLevel::Info); + } setJavaPath(java->path); return; } diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index 279545fed..536384f12 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -37,6 +37,7 @@ #include #include "Application.h" +#include "MessageLevel.h" #include "java/JavaInstall.h" #include "java/JavaInstallList.h" #include "java/JavaVersion.h" @@ -50,6 +51,16 @@ void VerifyJavaInstall::executeTask() auto settings = instance->settings(); auto storedVersion = settings->get("JavaVersion").toString(); auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool(); + auto javaArchitecture = settings->get("JavaArchitecture").toString(); + auto maxMemAlloc = settings->get("MaxMemAlloc").toInt(); + + emit logLine(tr("Java architecture is x%1.").arg(javaArchitecture), MessageLevel::Info); + if (javaArchitecture == "32" && maxMemAlloc > 2048) { + emit logLine(tr("Max memory allocation exceeds the supported value.\n" + "The selected java is 32 bit and doesn't support more than 2GB of ram.\n" + "The instance may not start due to this."), + MessageLevel::Error); + } auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors(); From 54bfbe562ee0b16ac4799951595850d15ccc2bbf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 18 Feb 2024 00:18:59 +0000 Subject: [PATCH 250/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/442d407992384ed9c0e6d352de75b69079904e4e' (2024-02-09) → 'github:nixos/nixpkgs/6e2f00c83911461438301db0dba5281197fe4b3a' (2024-02-17) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/0db2e67ee49910adfa13010e7f012149660af7f0' (2024-02-07) → 'github:cachix/pre-commit-hooks.nix/5df5a70ad7575f6601d91f0efec95dd9bc619431' (2024-02-15) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 11bf876f9..b5d8e7494 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1707451808, - "narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=", + "lastModified": 1708151420, + "narHash": "sha256-MGT/4aGCWQPQiu6COqJdCj9kSpLPiShgbwpbC38YXC8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "442d407992384ed9c0e6d352de75b69079904e4e", + "rev": "6e2f00c83911461438301db0dba5281197fe4b3a", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1707297608, - "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", + "lastModified": 1708018599, + "narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", + "rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431", "type": "github" }, "original": { From a7dad96a701100ab583b4c9ca00a7c9b979fcf84 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 09:32:31 +0200 Subject: [PATCH 251/755] Added refresh button on managed java list Signed-off-by: Trial97 --- launcher/ui/pages/global/JavaPage.cpp | 4 ++++ launcher/ui/pages/global/JavaPage.h | 1 + launcher/ui/pages/global/JavaPage.ui | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 496cb69a7..e967204c8 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -296,3 +296,7 @@ void JavaPage::on_removeJavaButton_clicked() } } } +void JavaPage::on_refreshJavaButton_clicked() +{ + ui->managedJavaList->loadList(); +} diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index 03d14b824..1a521e2db 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -77,6 +77,7 @@ class JavaPage : public QWidget, public BasePage { void on_removeExtraPathButton_clicked(); void on_downloadJavaButton_clicked(); void on_removeJavaButton_clicked(); + void on_refreshJavaButton_clicked(); void on_maxMemSpinBox_valueChanged(int i); void checkerFinished(); diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index d135adc9a..80be81fb4 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -376,6 +376,13 @@ + + + + Refresh + + + From 4c76f7afe0a1ea015d7c0eac7a1a6b1c8a1a760a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 09:46:44 +0200 Subject: [PATCH 252/755] Made auto java checkbox dependent Signed-off-by: Trial97 --- launcher/ui/pages/global/JavaPage.cpp | 8 +++++++- launcher/ui/pages/global/JavaPage.ui | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index e967204c8..0e97211d9 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -41,6 +41,7 @@ #include "ui/java/JavaDownloader.h" #include "ui_JavaPage.h" +#include #include #include #include @@ -65,6 +66,11 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) ui->managedJavaList->selectCurrent(); ui->managedJavaList->setEmptyString(tr("No java versions are currently available in the meta")); ui->managedJavaList->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { + ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked()); + if (!ui->autodetectJavaCheckBox->isChecked()) + ui->autodownloadCheckBox->setChecked(false); + }); loadSettings(); updateThresholds(); @@ -128,7 +134,7 @@ void JavaPage::loadSettings() ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool()); ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); ui->autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); - ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaDownload").toBool()); + ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool()); m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList()); ui->extraJavaPathsList->setModel(m_extra_paths); } diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 80be81fb4..43843017c 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -303,6 +303,9 @@ + + false + Autodownload Mojang Java From 2f489d1aec120274b3e98187bf8326c8990bca12 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 11:34:54 +0200 Subject: [PATCH 253/755] Added auto java options to the java wizzard page Signed-off-by: Trial97 --- launcher/ui/setupwizard/JavaWizardPage.cpp | 2 ++ launcher/ui/widgets/JavaSettingsWidget.cpp | 38 ++++++++++++++++++++++ launcher/ui/widgets/JavaSettingsWidget.h | 8 +++++ 3 files changed, 48 insertions(+) diff --git a/launcher/ui/setupwizard/JavaWizardPage.cpp b/launcher/ui/setupwizard/JavaWizardPage.cpp index abe4860da..d537580fc 100644 --- a/launcher/ui/setupwizard/JavaWizardPage.cpp +++ b/launcher/ui/setupwizard/JavaWizardPage.cpp @@ -57,6 +57,8 @@ bool JavaWizardPage::validatePage() { auto settings = APPLICATION->settings(); auto result = m_java_widget->validate(); + settings->set("AutomaticJavaSwitch", m_java_widget->autodetectJava()); + settings->set("AutomaticJavaDownload", m_java_widget->autodownloadJava()); switch (result) { default: case JavaSettingsWidget::ValidationStatus::Bad: { diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 507c632d5..c15f7bf42 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -136,6 +136,26 @@ void JavaSettingsWidget::setupUi() m_verticalLayout->addLayout(m_horizontalBtnLayout); + m_autoJavaGroupBox = new QGroupBox(this); + m_autoJavaGroupBox->setObjectName(QStringLiteral("autoJavaGroupBox")); + m_veriticalJavaLayout = new QVBoxLayout(m_autoJavaGroupBox); + m_veriticalJavaLayout->setObjectName(QStringLiteral("veriticalJavaLayout")); + + m_autodetectJavaCheckBox = new QCheckBox(m_autoJavaGroupBox); + m_autodetectJavaCheckBox->setObjectName("autodetectJavaCheckBox"); + m_veriticalJavaLayout->addWidget(m_autodetectJavaCheckBox); + + m_autodownloadCheckBox = new QCheckBox(m_autoJavaGroupBox); + m_autodownloadCheckBox->setObjectName("autodownloadCheckBox"); + m_autodownloadCheckBox->setEnabled(false); + m_veriticalJavaLayout->addWidget(m_autodownloadCheckBox); + connect(m_autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { + m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked()); + if (!m_autodetectJavaCheckBox->isChecked()) + m_autodownloadCheckBox->setChecked(false); + }); + m_verticalLayout->addWidget(m_autoJavaGroupBox); + retranslate(); } @@ -153,6 +173,9 @@ void JavaSettingsWidget::initialize() m_maxMemSpinBox->setValue(observedMaxMemory); m_permGenSpinBox->setValue(observedPermGenMemory); updateThresholds(); + + m_autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); + m_autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool()); } void JavaSettingsWidget::refresh() @@ -280,11 +303,13 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() m_javaPathTextBox->setText(cooked_path); checkJavaPath(cooked_path); } + void JavaSettingsWidget::on_javaDownloadBtn_clicked() { auto jdialog = new Java::Downloader(this); jdialog->exec(); } + void JavaSettingsWidget::on_javaStatusBtn_clicked() { QString text; @@ -418,6 +443,9 @@ void JavaSettingsWidget::retranslate() m_minMemSpinBox->setToolTip(tr("The amount of memory Minecraft is started with.")); m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes.")); m_javaBrowseBtn->setText(tr("Browse")); + m_autodownloadCheckBox->setText(tr("Autodownload Mojang Java")); + m_autodetectJavaCheckBox->setText(tr("Autodetect Java version")); + m_autoJavaGroupBox->setTitle(tr("Autodetect Java")); } void JavaSettingsWidget::updateThresholds() @@ -464,3 +492,13 @@ void JavaSettingsWidget::on_addJavaPathBtn_clicked() APPLICATION->settings()->set("JavaExtraSearchPaths", currentList); refresh(); } + +bool JavaSettingsWidget::autodownloadJava() const +{ + return m_autodetectJavaCheckBox->isChecked(); +} + +bool JavaSettingsWidget::autodetectJava() const +{ + return m_autodownloadCheckBox->isChecked(); +} diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 385a9256a..205b83d8c 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -4,6 +4,7 @@ #include #include #include +#include #include class QLineEdit; @@ -41,6 +42,8 @@ class JavaSettingsWidget : public QWidget { int minHeapSize() const; int maxHeapSize() const; QString javaPath() const; + bool autodetectJava() const; + bool autodownloadJava() const; void updateThresholds(); @@ -86,6 +89,11 @@ class JavaSettingsWidget : public QWidget { QIcon yellowIcon; QIcon badIcon; + QGroupBox* m_autoJavaGroupBox = nullptr; + QVBoxLayout* m_veriticalJavaLayout = nullptr; + QCheckBox* m_autodetectJavaCheckBox = nullptr; + QCheckBox* m_autodownloadCheckBox = nullptr; + unsigned int observedMinMemory = 0; unsigned int observedMaxMemory = 0; unsigned int observedPermGenMemory = 0; From 4aafa98852ec1f89cc765def26ab9e4174b8af3b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 12:58:34 +0200 Subject: [PATCH 254/755] Improved the message boxes for java wizzard Signed-off-by: Trial97 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 71 ++++++++++++++++++---- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index c15f7bf42..66278abb0 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include +#include "DesktopServices.h" #include "FileSystem.h" #include "JavaCommon.h" #include "java/JavaChecker.h" @@ -174,8 +176,19 @@ void JavaSettingsWidget::initialize() m_permGenSpinBox->setValue(observedPermGenMemory); updateThresholds(); - m_autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); - m_autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool()); + auto button = CustomMessageBox::selectable(this, tr("Auto Java Download"), + tr("%1 has now the ability to auto downloand the correct java for each minecraft version.\n" + "Do you want to enable java auto-download?\n") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) + ->exec(); + if (button == QMessageBox::Yes) { + m_autodetectJavaCheckBox->setChecked(true); + m_autodownloadCheckBox->setChecked(true); + } else { + m_autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); + m_autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool()); + } } void JavaSettingsWidget::refresh() @@ -192,20 +205,52 @@ JavaSettingsWidget::ValidationStatus JavaSettingsWidget::validate() switch (javaStatus) { default: case JavaStatus::NotSet: + /* fallthrough */ case JavaStatus::DoesNotExist: + /* fallthrough */ case JavaStatus::DoesNotStart: + /* fallthrough */ case JavaStatus::ReturnedInvalidData: { - int button = CustomMessageBox::selectable(this, tr("No Java version selected"), - tr("You didn't select a Java version or selected something that doesn't work.\n" - "%1 will not be able to start Minecraft.\n" - "Do you wish to proceed without any Java?" - "\n\n" - "You can change the Java version in the settings later.\n") - .arg(BuildConfig.LAUNCHER_DISPLAYNAME), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton) - ->exec(); - if (button == QMessageBox::No) { - return ValidationStatus::Bad; + if (!m_autodownloadCheckBox->isChecked()) { // the java will not be autodownloaded + int button = QMessageBox::No; + if (m_result.mojangPlatform == "32" && maxHeapSize() > 2048) { + button = CustomMessageBox::selectable( + this, tr("Java x32 detected"), + tr("You selected a 32 bit java, but allocated more than 2048MiB as maximum memory.\n" + "%1 will not be able to start Minecraft.\n" + "Do you wish to proceed?" + "\n\n" + "You can change the Java version in the settings later.\n") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No | QMessageBox::Help, QMessageBox::NoButton) + ->exec(); + + } else { + button = CustomMessageBox::selectable(this, tr("No Java version selected"), + tr("You didn't select a Java version or selected something that doesn't work.\n" + "%1 will not be able to start Minecraft.\n" + "Do you wish to proceed without any Java?" + "\n\n" + "You can change the Java version in the settings later.\n") + .arg(BuildConfig.LAUNCHER_DISPLAYNAME), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No | QMessageBox::Help, + QMessageBox::NoButton) + ->exec(); + } + switch (button) { + case QMessageBox::Yes: + return ValidationStatus::JavaBad; + case QMessageBox::Help: + DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("java-wizzard"))); + /* fallthrough */ + case QMessageBox::No: + /* fallthrough */ + default: + return ValidationStatus::Bad; + } + if (button == QMessageBox::No) { + return ValidationStatus::Bad; + } } return ValidationStatus::JavaBad; } break; From b4f3a969b3fd2f96675d25e3c8726c76395b3d7e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 14:30:38 +0200 Subject: [PATCH 255/755] Updated strings that are displayed to user Signed-off-by: Trial97 --- launcher/MMCZip.cpp | 2 +- launcher/MMCZip.h | 2 +- launcher/java/JavaInstall.cpp | 2 +- launcher/java/JavaInstall.h | 2 +- launcher/java/JavaMetadata.cpp | 2 +- launcher/java/JavaMetadata.h | 2 +- launcher/java/download/ArchiveDownloadTask.cpp | 2 +- launcher/java/download/ArchiveDownloadTask.h | 2 +- launcher/java/download/ManifestDownloadTask.cpp | 2 +- launcher/java/download/ManifestDownloadTask.h | 2 +- launcher/minecraft/launch/AutoInstallJava.cpp | 10 +++++----- launcher/minecraft/launch/AutoInstallJava.h | 2 +- launcher/minecraft/launch/VerifyJavaInstall.cpp | 2 +- launcher/ui/java/JavaDownloader.cpp | 2 +- launcher/ui/pages/global/JavaPage.cpp | 2 +- launcher/ui/pages/global/JavaPage.ui | 2 +- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 2 +- launcher/ui/pages/instance/InstanceSettingsPage.ui | 3 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 6 +++--- 19 files changed, 24 insertions(+), 27 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 9e26c5eda..8273acbb6 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index db6a7421a..e2987badd 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -2,7 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/JavaInstall.cpp b/launcher/java/JavaInstall.cpp index cfa471402..8e97e0e14 100644 --- a/launcher/java/JavaInstall.cpp +++ b/launcher/java/JavaInstall.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/JavaInstall.h b/launcher/java/JavaInstall.h index 6890264f3..7d8d392fa 100644 --- a/launcher/java/JavaInstall.h +++ b/launcher/java/JavaInstall.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/JavaMetadata.cpp b/launcher/java/JavaMetadata.cpp index b261122dc..b73c54fe6 100644 --- a/launcher/java/JavaMetadata.cpp +++ b/launcher/java/JavaMetadata.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/JavaMetadata.h b/launcher/java/JavaMetadata.h index dd3ae865c..640a412f1 100644 --- a/launcher/java/JavaMetadata.h +++ b/launcher/java/JavaMetadata.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index 9f8dc0543..e3db77d2c 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/download/ArchiveDownloadTask.h b/launcher/java/download/ArchiveDownloadTask.h index 88c0223d5..c656567fe 100644 --- a/launcher/java/download/ArchiveDownloadTask.h +++ b/launcher/java/download/ArchiveDownloadTask.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/download/ManifestDownloadTask.cpp b/launcher/java/download/ManifestDownloadTask.cpp index 0c2eee0c8..ba5ff2cc0 100644 --- a/launcher/java/download/ManifestDownloadTask.cpp +++ b/launcher/java/download/ManifestDownloadTask.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/java/download/ManifestDownloadTask.h b/launcher/java/download/ManifestDownloadTask.h index dcfee4d4d..f0eaf95a6 100644 --- a/launcher/java/download/ManifestDownloadTask.h +++ b/launcher/java/download/ManifestDownloadTask.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (c) 2023 Trial97 + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp index 7905689da..ab68910a7 100644 --- a/launcher/minecraft/launch/AutoInstallJava.cpp +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -75,13 +75,13 @@ void AutoInstallJava::executeTask() auto java = std::dynamic_pointer_cast(javas->at(i)); if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) { if (!java->is_64bit) { - emit logLine(tr("The automatic Java mechanism detected a x32 java."), MessageLevel::Info); + emit logLine(tr("The automatic Java mechanism detected an x32 java."), MessageLevel::Info); } setJavaPath(java->path); return; } } - emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emit logLine(tr("No compatible java version was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); }); connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); @@ -136,7 +136,7 @@ void AutoInstallJava::setJavaPathFromPartial() if (QFileInfo::exists(finalPath)) { setJavaPath(finalPath); } else { - emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emit logLine(tr("No compatible java version was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); } return; @@ -192,7 +192,7 @@ void AutoInstallJava::tryNextMajorJava() auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); auto majorJavaVersions = packProfile->getProfile()->getCompatibleJavaMajors(); if (m_majorJavaVersionIndex >= majorJavaVersions.length()) { - emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emit logLine(tr("No compatible java version was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); return; } diff --git a/launcher/minecraft/launch/AutoInstallJava.h b/launcher/minecraft/launch/AutoInstallJava.h index 45d0f870e..7e4efc50c 100644 --- a/launcher/minecraft/launch/AutoInstallJava.h +++ b/launcher/minecraft/launch/AutoInstallJava.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index 536384f12..f26b850eb 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -57,7 +57,7 @@ void VerifyJavaInstall::executeTask() emit logLine(tr("Java architecture is x%1.").arg(javaArchitecture), MessageLevel::Info); if (javaArchitecture == "32" && maxMemAlloc > 2048) { emit logLine(tr("Max memory allocation exceeds the supported value.\n" - "The selected java is 32 bit and doesn't support more than 2GB of ram.\n" + "The selected java is 32-bit and doesn't support more than 2048MiB of RAM.\n" "The instance may not start due to this."), MessageLevel::Error); } diff --git a/launcher/ui/java/JavaDownloader.cpp b/launcher/ui/java/JavaDownloader.cpp index 4495210c3..7417a4ea8 100644 --- a/launcher/ui/java/JavaDownloader.cpp +++ b/launcher/ui/java/JavaDownloader.cpp @@ -49,7 +49,7 @@ Downloader::Downloader(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownlo versionList->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); ui->majorVersionSelect->initialize(versionList.get()); ui->majorVersionSelect->selectCurrent(); - ui->majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta")); + ui->majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta.")); ui->majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); ui->javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 0e97211d9..524165e62 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -158,7 +158,7 @@ void JavaPage::on_javaDetectBtn_clicked() if (!java->is_64bit && APPLICATION->settings()->get("MaxMemAlloc").toInt() > 2048) { CustomMessageBox::selectable(this, tr("Confirm Selection"), tr("You selected an x86 java version.\n" - "This means that will not support more than 2Gb(2048Mb) of ram.\n" + "This means that will not support more than 2048MiB of RAM.\n" "Please make sure that the maximum memory value is lower."), QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) ->exec(); diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 43843017c..3f763f8d2 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -307,7 +307,7 @@ false - Autodownload Mojang Java + Auto-download Mojang Java diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 926e48952..8a63aab22 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -417,7 +417,7 @@ void InstanceSettingsPage::on_javaDetectBtn_clicked() if (!java->is_64bit && m_settings->get("MaxMemAlloc").toInt() > 2048) { CustomMessageBox::selectable(this, tr("Confirm Selection"), tr("You selected an x86 java version.\n" - "This means that will not support more than 2Gb(2048Mb) of ram.\n" + "This means that will not support more than 2048MiB of RAM.\n" "Please make sure that the maximum memory value is lower."), QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) ->exec(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 7762ca8a7..94464064e 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -617,9 +617,6 @@ Enable online fixes (experimental) - - Disable Quilt Loader Beacon - diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 66278abb0..91fd6ff03 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -216,7 +216,7 @@ JavaSettingsWidget::ValidationStatus JavaSettingsWidget::validate() if (m_result.mojangPlatform == "32" && maxHeapSize() > 2048) { button = CustomMessageBox::selectable( this, tr("Java x32 detected"), - tr("You selected a 32 bit java, but allocated more than 2048MiB as maximum memory.\n" + tr("You selected an 32-bit java, but allocated more than 2048MiB as maximum memory.\n" "%1 will not be able to start Minecraft.\n" "Do you wish to proceed?" "\n\n" @@ -488,7 +488,7 @@ void JavaSettingsWidget::retranslate() m_minMemSpinBox->setToolTip(tr("The amount of memory Minecraft is started with.")); m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes.")); m_javaBrowseBtn->setText(tr("Browse")); - m_autodownloadCheckBox->setText(tr("Autodownload Mojang Java")); + m_autodownloadCheckBox->setText(tr("Auto-download Mojang Java")); m_autodetectJavaCheckBox->setText(tr("Autodetect Java version")); m_autoJavaGroupBox->setTitle(tr("Autodetect Java")); } @@ -509,7 +509,7 @@ void JavaSettingsWidget::updateThresholds() } else if (observedMaxMemory > 2048 && m_result.is_64bit) { iconName = "status-bad"; m_labelMaxMemIcon->setToolTip( - tr("Your maximum memory allocation exceeds selected java posible memory(due to x86 applicatiion limitations).")); + tr("Your maximum memory allocation exceeds selected java possible memory(due to x86 application limitations).")); } else { iconName = "status-good"; m_labelMaxMemIcon->setToolTip(""); From 44327980db851edbe1fe909c9e934b1f4c559c6a Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:28:32 +0100 Subject: [PATCH 256/755] feat: add "use zink" button Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- launcher/Application.cpp | 3 ++- launcher/minecraft/MinecraftInstance.cpp | 10 +++++++++- launcher/ui/pages/global/MinecraftPage.cpp | 2 ++ launcher/ui/pages/global/MinecraftPage.ui | 10 ++++++++++ launcher/ui/pages/instance/InstanceSettingsPage.cpp | 4 ++++ launcher/ui/pages/instance/InstanceSettingsPage.ui | 10 ++++++++++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 42343ff8f..db4b0d9cd 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -639,10 +639,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("UseNativeGLFW", false); m_settings->registerSetting("CustomGLFWPath", ""); - // Peformance related options + // Performance related options m_settings->registerSetting("EnableFeralGamemode", false); m_settings->registerSetting("EnableMangoHud", false); m_settings->registerSetting("UseDiscreteGpu", false); + m_settings->registerSetting("UseZink", false); // Game time m_settings->registerSetting("ShowGameTime", true); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index edeebd34d..e0b677b69 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -173,11 +173,12 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride); - // Peformance related options + // Performance related options auto performanceOverride = m_settings->registerSetting("OverridePerformance", false); m_settings->registerOverride(global_settings->getSetting("EnableFeralGamemode"), performanceOverride); m_settings->registerOverride(global_settings->getSetting("EnableMangoHud"), performanceOverride); m_settings->registerOverride(global_settings->getSetting("UseDiscreteGpu"), performanceOverride); + m_settings->registerOverride(global_settings->getSetting("UseZink"), performanceOverride); // Miscellaneous auto miscellaneousOverride = m_settings->registerSetting("OverrideMiscellaneous", false); @@ -622,6 +623,13 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment() env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only"); env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia"); } + + if (settings()->get("UseZink").toBool()) { + // taken from https://wiki.archlinux.org/title/OpenGL#OpenGL_over_Vulkan_(Zink) + env.insert("__GLX_VENDOR_LIBRARY_NAME", "mesa"); + env.insert("MESA_LOADER_DRIVER_OVERRIDE", "zink"); + env.insert("GALLIUM_DRIVER", "zink"); + } #endif return env; } diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index a9530effc..3431dcb9c 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -109,6 +109,7 @@ void MinecraftPage::applySettings() s->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked()); s->set("EnableMangoHud", ui->enableMangoHud->isChecked()); s->set("UseDiscreteGpu", ui->useDiscreteGpuCheck->isChecked()); + s->set("UseZink", ui->useZink->isChecked()); // Game time s->set("ShowGameTime", ui->showGameTime->isChecked()); @@ -151,6 +152,7 @@ void MinecraftPage::loadSettings() ui->enableFeralGamemodeCheck->setChecked(s->get("EnableFeralGamemode").toBool()); ui->enableMangoHud->setChecked(s->get("EnableMangoHud").toBool()); ui->useDiscreteGpuCheck->setChecked(s->get("UseDiscreteGpu").toBool()); + ui->useZink->setChecked(s->get("UseZink").toBool()); #if !defined(Q_OS_LINUX) ui->perfomanceGroupBox->setVisible(false); diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 3008099e3..7d2741250 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -309,6 +309,16 @@ + + + + <html><head/><body><p>Use Zink, a Mesa OpenGL driver that implements OpenGL on top of Vulkan. Performance may vary depending on the situation. Note: If no suitable Vulkan driver is found, software rendering will be used.</p></body></html> + + + Use Zink + + + diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index d4fd0ec5b..76add9402 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -232,10 +232,13 @@ void InstanceSettingsPage::applySettings() m_settings->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked()); m_settings->set("EnableMangoHud", ui->enableMangoHud->isChecked()); m_settings->set("UseDiscreteGpu", ui->useDiscreteGpuCheck->isChecked()); + m_settings->set("UseZink", ui->useZink->isChecked()); + } else { m_settings->reset("EnableFeralGamemode"); m_settings->reset("EnableMangoHud"); m_settings->reset("UseDiscreteGpu"); + m_settings->reset("UseZink"); } // Game time @@ -354,6 +357,7 @@ void InstanceSettingsPage::loadSettings() ui->enableFeralGamemodeCheck->setChecked(m_settings->get("EnableFeralGamemode").toBool()); ui->enableMangoHud->setChecked(m_settings->get("EnableMangoHud").toBool()); ui->useDiscreteGpuCheck->setChecked(m_settings->get("UseDiscreteGpu").toBool()); + ui->useZink->setChecked(m_settings->get("UseZink").toBool()); #if !defined(Q_OS_LINUX) ui->settingsTabs->setTabVisible(ui->settingsTabs->indexOf(ui->performancePage), false); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 632569e0c..9490860ae 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -567,6 +567,16 @@ + + + + Use Zink, a Mesa OpenGL driver that implements OpenGL on top of Vulkan. Performance may vary depending on the situation. Note: If no suitable Vulkan driver is found, software rendering will be used. + + + Use Zink + + + From 038d3e4596586718ca6982c68a17c36723561a42 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 16:49:10 +0200 Subject: [PATCH 257/755] Fixed asan complains Signed-off-by: Trial97 --- launcher/java/download/ArchiveDownloadTask.cpp | 5 ++++- launcher/java/download/ManifestDownloadTask.cpp | 5 ++++- launcher/ui/pages/global/JavaPage.cpp | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index e3db77d2c..34f60260e 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -49,7 +49,10 @@ void ArchiveDownloadTask::executeTask() download->addNetAction(action); auto fullPath = entry->getFullPath(); - connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); + connect(download.get(), &NetJob::finished, [download, this] { + disconnect(this, &Task::aborted, download.get(), &NetJob::abort); + download->deleteLater(); + }); connect(download.get(), &NetJob::failed, this, &ArchiveDownloadTask::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); connect(download.get(), &Task::progress, this, &ArchiveDownloadTask::setProgress); diff --git a/launcher/java/download/ManifestDownloadTask.cpp b/launcher/java/download/ManifestDownloadTask.cpp index ba5ff2cc0..5551c75d5 100644 --- a/launcher/java/download/ManifestDownloadTask.cpp +++ b/launcher/java/download/ManifestDownloadTask.cpp @@ -51,7 +51,10 @@ void ManifestDownloadTask::executeTask() } download->addNetAction(action); - connect(download.get(), &NetJob::finished, [download, this] { disconnect(this, &Task::aborted, download.get(), &NetJob::abort); }); + connect(download.get(), &NetJob::finished, [download, this] { + disconnect(this, &Task::aborted, download.get(), &NetJob::abort); + download->deleteLater(); + }); connect(download.get(), &NetJob::failed, this, &ManifestDownloadTask::emitFailed); connect(this, &Task::aborted, download.get(), &NetJob::abort); connect(download.get(), &Task::progress, this, &ManifestDownloadTask::setProgress); diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 524165e62..deb085f93 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -135,7 +135,7 @@ void JavaPage::loadSettings() ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); ui->autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool()); - m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList()); + m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList(), this); ui->extraJavaPathsList->setModel(m_extra_paths); } @@ -199,6 +199,7 @@ void JavaPage::on_javaDownloadBtn_clicked() { auto jdialog = new Java::Downloader(this); jdialog->exec(); + ui->managedJavaList->loadList(); } void JavaPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) From 889f604a415fd01150868090a1d47b7c408104d5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 21:38:31 +0200 Subject: [PATCH 258/755] Removed JavaExtraSearchPaths Signed-off-by: Trial97 --- launcher/Application.cpp | 1 - launcher/java/JavaUtils.cpp | 37 ------------------- launcher/java/JavaUtils.h | 1 - launcher/ui/pages/global/JavaPage.cpp | 27 -------------- launcher/ui/pages/global/JavaPage.h | 3 -- launcher/ui/pages/global/JavaPage.ui | 43 ---------------------- launcher/ui/widgets/JavaSettingsWidget.cpp | 19 ---------- launcher/ui/widgets/JavaSettingsWidget.h | 2 - 8 files changed, 133 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6b7333972..31950a813 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -632,7 +632,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("JvmArgs", ""); m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); - m_settings->registerSetting("JavaExtraSearchPaths", QStringList()); m_settings->registerSetting("AutomaticJavaSwitch", false); m_settings->registerSetting("AutomaticJavaDownload", false); diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 1d254e405..57e381e1d 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -338,7 +338,6 @@ QList JavaUtils::FindJavaPaths() candidates.append(getMinecraftJavaBundle()); candidates.append(getPrismJavaBundle()); - candidates.append(getPrismExtraJavaPaths()); candidates = addJavasFromEnv(candidates); candidates.removeDuplicates(); return candidates; @@ -366,7 +365,6 @@ QList JavaUtils::FindJavaPaths() } javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); - javas.append(getPrismExtraJavaPaths()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -420,7 +418,6 @@ QList JavaUtils::FindJavaPaths() javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); - javas.append(getPrismExtraJavaPaths()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; @@ -435,7 +432,6 @@ QList JavaUtils::FindJavaPaths() javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); - javas.append(getPrismExtraJavaPaths()); javas.removeDuplicates(); return addJavasFromEnv(javas); } @@ -521,36 +517,3 @@ QStringList getPrismJavaBundle() return javas; } - -QStringList getPrismExtraJavaPaths() -{ - QList javas; - - QString executable = "java"; -#if defined(Q_OS_WIN32) - executable += "w.exe"; -#endif - - auto scanDir = [&](QString prefix) { - javas.append(FS::PathCombine(prefix, "jre", "bin", executable)); - javas.append(FS::PathCombine(prefix, "bin", executable)); - javas.append(FS::PathCombine(prefix, executable)); - }; - auto scanJavaDir = [&](const QString& dirPath) { - QDir dir(dirPath); - if (!dir.exists()) - return; - auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (auto& entry : entries) { - scanDir(entry.canonicalFilePath()); - } - }; - - auto extra_paths = APPLICATION->settings()->get("JavaExtraSearchPaths").toStringList(); - for (auto& entry : extra_paths) { - scanDir(entry); - scanJavaDir(entry); - } - - return javas; -} diff --git a/launcher/java/JavaUtils.h b/launcher/java/JavaUtils.h index 66e439834..aa5315a19 100644 --- a/launcher/java/JavaUtils.h +++ b/launcher/java/JavaUtils.h @@ -27,7 +27,6 @@ QString stripVariableEntries(QString name, QString target, QString remove); QProcessEnvironment CleanEnviroment(); QStringList getMinecraftJavaBundle(); QStringList getPrismJavaBundle(); -QStringList getPrismExtraJavaPaths(); class JavaUtils : public QObject { Q_OBJECT diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index deb085f93..cb2a8b90b 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -110,7 +110,6 @@ void JavaPage::applySettings() s->set("IgnoreJavaWizard", ui->skipJavaWizardCheckbox->isChecked()); s->set("AutomaticJavaSwitch", ui->autodetectJavaCheckBox->isChecked()); s->set("AutomaticJavaDownload", ui->autodownloadCheckBox->isChecked()); - s->set("JavaExtraSearchPaths", m_extra_paths->stringList()); JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget()); } void JavaPage::loadSettings() @@ -135,8 +134,6 @@ void JavaPage::loadSettings() ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); ui->autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool()); - m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList(), this); - ui->extraJavaPathsList->setModel(m_extra_paths); } void JavaPage::on_javaDetectBtn_clicked() @@ -247,30 +244,6 @@ void JavaPage::updateThresholds() } } -void JavaPage::on_addExtraPathButton_clicked() -{ - QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Add Extra Java Folder")); - - if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { - QString cooked_dir = FS::NormalizePath(raw_dir); - auto currentList = m_extra_paths->stringList(); - if (!currentList.contains(cooked_dir)) { - currentList << cooked_dir; - m_extra_paths->setStringList(currentList); - } - } - APPLICATION->settings()->set("JavaExtraSearchPaths", m_extra_paths->stringList()); -} - -void JavaPage::on_removeExtraPathButton_clicked() -{ - auto indexes = ui->extraJavaPathsList->selectionModel()->selectedIndexes(); - if (indexes.size()) { - m_extra_paths->removeRow(indexes.first().row()); - } - APPLICATION->settings()->set("JavaExtraSearchPaths", m_extra_paths->stringList()); -} - void JavaPage::on_downloadJavaButton_clicked() { on_javaDownloadBtn_clicked(); diff --git a/launcher/ui/pages/global/JavaPage.h b/launcher/ui/pages/global/JavaPage.h index 1a521e2db..48e557740 100644 --- a/launcher/ui/pages/global/JavaPage.h +++ b/launcher/ui/pages/global/JavaPage.h @@ -73,8 +73,6 @@ class JavaPage : public QWidget, public BasePage { void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); void on_javaDownloadBtn_clicked(); - void on_addExtraPathButton_clicked(); - void on_removeExtraPathButton_clicked(); void on_downloadJavaButton_clicked(); void on_removeJavaButton_clicked(); void on_refreshJavaButton_clicked(); @@ -84,5 +82,4 @@ class JavaPage : public QWidget, public BasePage { private: Ui::JavaPage* ui; unique_qobject_ptr checker; - QStringListModel* m_extra_paths; }; diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index 3f763f8d2..dc1668ee3 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -391,49 +391,6 @@ - - - - Java extra paths - - - - - - - - - - - Add - - - - - - - Remove - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 91fd6ff03..4b5819288 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -44,7 +44,6 @@ JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent) connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited); connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked); connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaDownloadBtn_clicked); - connect(m_addJavaPathBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_addJavaPathBtn_clicked); } void JavaSettingsWidget::setupUi() @@ -133,9 +132,6 @@ void JavaSettingsWidget::setupUi() m_javaDownloadBtn = new QPushButton(tr("Download Java"), this); m_horizontalBtnLayout->addWidget(m_javaDownloadBtn); - m_addJavaPathBtn = new QPushButton(tr("Add extra Java path"), this); - m_horizontalBtnLayout->addWidget(m_addJavaPathBtn); - m_verticalLayout->addLayout(m_horizontalBtnLayout); m_autoJavaGroupBox = new QGroupBox(this); @@ -523,21 +519,6 @@ void JavaSettingsWidget::updateThresholds() } } -void JavaSettingsWidget::on_addJavaPathBtn_clicked() -{ - QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Add Extra Java Folder")); - - auto currentList = APPLICATION->settings()->get("JavaExtraSearchPaths").toStringList(); - if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { - QString cooked_dir = FS::NormalizePath(raw_dir); - if (!currentList.contains(cooked_dir)) { - currentList << cooked_dir; - } - } - APPLICATION->settings()->set("JavaExtraSearchPaths", currentList); - refresh(); -} - bool JavaSettingsWidget::autodownloadJava() const { return m_autodetectJavaCheckBox->isChecked(); diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 205b83d8c..c8b6953df 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -54,7 +54,6 @@ class JavaSettingsWidget : public QWidget { void on_javaBrowseBtn_clicked(); void on_javaStatusBtn_clicked(); void on_javaDownloadBtn_clicked(); - void on_addJavaPathBtn_clicked(); void checkFinished(const JavaChecker::Result& result); protected: /* methods */ @@ -84,7 +83,6 @@ class JavaSettingsWidget : public QWidget { QHBoxLayout* m_horizontalBtnLayout = nullptr; QPushButton* m_javaDownloadBtn = nullptr; - QPushButton* m_addJavaPathBtn = nullptr; QIcon goodIcon; QIcon yellowIcon; QIcon badIcon; From 1c96ae58077cae7a8d5b8f851d5ede385811f6ea Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 18 Feb 2024 22:32:54 +0200 Subject: [PATCH 259/755] Fixed java refresh button Signed-off-by: Trial97 --- launcher/ui/java/JavaDownloader.cpp | 3 ++- launcher/ui/java/JavaDownloader.ui | 10 +++++----- launcher/ui/java/VersionList.cpp | 4 +++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/launcher/ui/java/JavaDownloader.cpp b/launcher/ui/java/JavaDownloader.cpp index 7417a4ea8..24918dd76 100644 --- a/launcher/ui/java/JavaDownloader.cpp +++ b/launcher/ui/java/JavaDownloader.cpp @@ -55,7 +55,7 @@ Downloader::Downloader(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownlo ui->javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); ui->javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); - ui->buttonBox->button(QDialogButtonBox::Retry)->setText(tr("Refresh")); + ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Refresh")); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Download")); connect(ui->majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &Downloader::setSelectedVersion); @@ -98,6 +98,7 @@ void Downloader::accept() connect(task.get(), &Task::failed, this, deletePath); connect(task.get(), &Task::aborted, this, deletePath); ProgressDialog pg(this); + pg.setSkipButton(true, tr("Abort")); pg.execWithTask(task.get()); QDialog::accept(); } diff --git a/launcher/ui/java/JavaDownloader.ui b/launcher/ui/java/JavaDownloader.ui index 0eeabb26d..3f6a8adc6 100644 --- a/launcher/ui/java/JavaDownloader.ui +++ b/launcher/ui/java/JavaDownloader.ui @@ -48,7 +48,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset @@ -71,8 +71,8 @@ accept() - 248 - 254 + 257 + 583 157 @@ -87,8 +87,8 @@ reject() - 316 - 260 + 325 + 583 286 diff --git a/launcher/ui/java/VersionList.cpp b/launcher/ui/java/VersionList.cpp index 7c5668d20..4bf04224a 100644 --- a/launcher/ui/java/VersionList.cpp +++ b/launcher/ui/java/VersionList.cpp @@ -104,10 +104,12 @@ bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) void VersionList::sortVersions() { + if (!m_version || !m_version->data()) + return; QString versionStr = SysInfo::getSupportedJavaArchitecture(); beginResetModel(); auto runtimes = m_version->data()->runtimes; - if (!versionStr.isEmpty() && runtimes.contains(versionStr)) { + if (!versionStr.isEmpty() && !runtimes.isEmpty() && runtimes.contains(versionStr)) { m_vlist = runtimes.value(versionStr); std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); } else { From 9aa81cf00508c45ad6fe6b1d53c0933f1494571c Mon Sep 17 00:00:00 2001 From: crpz1 <8588315+crpz1@users.noreply.github.com> Date: Tue, 20 Feb 2024 00:21:37 +1100 Subject: [PATCH 260/755] Use empty list label code for welcome screen Signed-off-by: crpz1 <8588315+crpz1@users.noreply.github.com> --- launcher/ui/instanceview/InstanceView.cpp | 56 +++++++++++++---------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 6f3812a62..0723b5add 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -482,32 +482,42 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) if (model()->rowCount() == 0) { painter.save(); - const QString line1 = tr("Welcome!"); - const QString line2 = tr("Click \"Add Instance\" to get started."); - auto rect = this->viewport()->rect(); - auto font = option.font; - font.setPointSize(37); - painter.setFont(font); - auto fm = painter.fontMetrics(); + QString emptyString = tr("Welcome!") + "\n" + tr("Click \"Add Instance\" to get started."); - if (rect.height() <= (fm.height() * 5) || rect.width() <= fm.horizontalAdvance(line2)) { - auto s = rect.height() / (5. * fm.height()); - auto sx = rect.width() * 1. / fm.horizontalAdvance(line2); - if (s >= sx) - s = sx; - auto ps = font.pointSize() * s; - if (ps <= 0) - ps = 1; - font.setPointSize(ps); - painter.setFont(font); - fm = painter.fontMetrics(); + // calculate the rect for the overlay + painter.setRenderHint(QPainter::Antialiasing, true); + QFont font("sans", 20); + font.setBold(true); + + QRect bounds = viewport()->geometry(); + bounds.moveTop(0); + auto innerBounds = bounds; + innerBounds.adjust(10, 10, -10, -10); + + QColor background = QApplication::palette().color(QPalette::WindowText); + QColor foreground = QApplication::palette().color(QPalette::Base); + foreground.setAlpha(190); + painter.setFont(font); + auto fontMetrics = painter.fontMetrics(); + auto textRect = fontMetrics.boundingRect(innerBounds, Qt::AlignHCenter | Qt::TextWordWrap, emptyString); + textRect.moveCenter(bounds.center()); + + auto wrapRect = textRect; + wrapRect.adjust(-10, -10, 10, 10); + + // check if we are allowed to draw in our area + if (!event->rect().intersects(wrapRect)) { + return; } - // text - rect.setTop(rect.top() + fm.height() * 1.5); - painter.drawText(rect, Qt::AlignHCenter, line1); - rect.setTop(rect.top() + fm.height()); - painter.drawText(rect, Qt::AlignHCenter, line2); + painter.setBrush(QBrush(background)); + painter.setPen(foreground); + painter.drawRoundedRect(wrapRect, 5.0, 5.0); + + painter.setPen(foreground); + painter.setFont(font); + painter.drawText(textRect, Qt::AlignHCenter | Qt::TextWordWrap, emptyString); + painter.restore(); return; } From 4421746c0e0c2eda0e9aab14371aaf40024f76e9 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:00:21 +0100 Subject: [PATCH 261/755] fix: fix sparkle signing Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a26e6c094..0352988f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -407,7 +407,7 @@ jobs: if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then brew install openssl@3 echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem - signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n) + signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.zip -inkey ed25519-priv.pem | openssl base64 | tr -d \\n) rm ed25519-priv.pem cat >> $GITHUB_STEP_SUMMARY << EOF ### Artifact Information :information_source: From f747ae949cdc70d0ef832a2751d0adb5405ba5cf Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 22 Feb 2024 06:13:27 -0500 Subject: [PATCH 262/755] ci: fix permissions for backport workflow Signed-off-by: seth --- .github/workflows/backport.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 9b51b201e..5df88fe86 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -16,6 +16,7 @@ jobs: permissions: contents: write # for korthout/backport-action to create branch pull-requests: write # for korthout/backport-action to create PR to backport + actions: write # for korthout/backport-action to create PR with workflow changes name: Backport Pull Request if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) runs-on: ubuntu-latest From 726b0ffb3b0488bd289d85feacea31066c464a45 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 22 Feb 2024 19:40:24 +0200 Subject: [PATCH 263/755] Reduced temporary instance folder name Signed-off-by: Trial97 --- launcher/InstanceList.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index c884a4f12..c663d22d0 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -933,8 +934,12 @@ Task* InstanceList::wrapInstanceTask(InstanceTask* task) QString InstanceList::getStagedInstancePath() { - QString key = QUuid::createUuid().toString(QUuid::WithoutBraces); QString tempDir = ".LAUNCHER_TEMP/"; + auto tempPath = FS::PathCombine(m_instDir, tempDir); + if (QFileInfo::exists(tempPath)) { + FS::deletePath(tempPath); // clean the path to prevent any collisions + } + QString key = QUuid::createUuid().toString(QUuid::WithoutBraces).left(6); // reduce the size from 36 to 6 QString relPath = FS::PathCombine(tempDir, key); QDir rootPath(m_instDir); auto path = FS::PathCombine(m_instDir, relPath); @@ -942,7 +947,6 @@ QString InstanceList::getStagedInstancePath() return QString(); } #ifdef Q_OS_WIN32 - auto tempPath = FS::PathCombine(m_instDir, tempDir); SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); #endif return path; From aac6fea724e029b9ef4e4ed3e98b04c40149add0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 22 Feb 2024 20:34:34 +0200 Subject: [PATCH 264/755] Updated application manifest Signed-off-by: Trial97 --- program_info/prismlauncher.manifest.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/program_info/prismlauncher.manifest.in b/program_info/prismlauncher.manifest.in index fb28afc17..71378134c 100644 --- a/program_info/prismlauncher.manifest.in +++ b/program_info/prismlauncher.manifest.in @@ -1,5 +1,10 @@ + + + true + + From 6a9f5540d385cd2edf7e43f3ad308041571a9f83 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Feb 2024 13:38:19 +0000 Subject: [PATCH 265/755] Re-randomise key until it does not collide Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index c663d22d0..9c1f8f71b 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -934,22 +934,21 @@ Task* InstanceList::wrapInstanceTask(InstanceTask* task) QString InstanceList::getStagedInstancePath() { - QString tempDir = ".LAUNCHER_TEMP/"; - auto tempPath = FS::PathCombine(m_instDir, tempDir); - if (QFileInfo::exists(tempPath)) { - FS::deletePath(tempPath); // clean the path to prevent any collisions - } - QString key = QUuid::createUuid().toString(QUuid::WithoutBraces).left(6); // reduce the size from 36 to 6 - QString relPath = FS::PathCombine(tempDir, key); - QDir rootPath(m_instDir); - auto path = FS::PathCombine(m_instDir, relPath); - if (!rootPath.mkpath(relPath)) { - return QString(); - } + const QString tempRoot = FS::PathCombine(m_instDir, ".LAUNCHER_TEMP"); + + QString result; + + do { + const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6); + result = FS::PathCombine(tempRoot, key); + } while (QFileInfo::exists(result)); + + if (!QDir::current().mkpath(result)) + return {}; #ifdef Q_OS_WIN32 SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); #endif - return path; + return result; } bool InstanceList::commitStagedInstance(const QString& path, From 9574ebe480a6a837533fc4339c1f3605300555ca Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Feb 2024 14:12:30 +0000 Subject: [PATCH 266/755] Delete staging on abort Previously instances would get "stuck" in the folder Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 9c1f8f71b..5297b1444 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -907,7 +907,11 @@ class InstanceStaging : public Task { emitFailed(reason); } - void childAborted() { emitAborted(); } + void childAborted() + { + m_parent->destroyStagingPath(m_stagingPath); + emitAborted(); + } private: InstanceList* m_parent; From df60f5cc9650a111a65ada2ea589bf7ecf9ac506 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Feb 2024 18:38:45 +0000 Subject: [PATCH 267/755] Add error when staging folder could not be created Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 5297b1444..f50d21a6b 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -848,13 +848,10 @@ class InstanceStaging : public Task { const unsigned maxBackoff = 16; public: - InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName) - : m_parent(parent) - , backoff(minBackoff, maxBackoff) - , m_stagingPath(std::move(stagingPath)) - , m_instance_name(std::move(instanceName)) - , m_groupName(std::move(groupName)) + InstanceStaging(InstanceList* parent, InstanceTask* child) : m_parent(parent), backoff(minBackoff, maxBackoff) { + m_stagingPath = parent->getStagedInstancePath(); + m_child.reset(child); connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); connect(child, &Task::failed, this, &InstanceStaging::childFailed); @@ -867,7 +864,7 @@ class InstanceStaging : public Task { connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded); } - virtual ~InstanceStaging(){}; + virtual ~InstanceStaging() {} // FIXME/TODO: add ability to abort during instance commit retries bool abort() override @@ -882,14 +879,22 @@ class InstanceStaging : public Task { bool canAbort() const override { return (m_child && m_child->canAbort()); } protected: - virtual void executeTask() override { m_child->start(); } + virtual void executeTask() override + { + if (m_stagingPath.isNull()) { + emitFailed(tr("Could not create staging folder")); + return; + } + + m_child->start(); + } QStringList warnings() const override { return m_child->warnings(); } private slots: void childSucceeded() { unsigned sleepTime = backoff(); - if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) { + if (m_parent->commitStagedInstance(m_stagingPath, *m_child.get(), m_child->group(), *m_child.get())) { emitSucceeded(); return; } @@ -898,7 +903,7 @@ class InstanceStaging : public Task { emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something.")); return; } - qDebug() << "Failed to commit instance" << m_instance_name.name() << "Initiating backoff:" << sleepTime; + qDebug() << "Failed to commit instance" << m_child->name() << "Initiating backoff:" << sleepTime; m_backoffTimer.start(sleepTime * 500); } void childFailed(const QString& reason) @@ -923,17 +928,12 @@ class InstanceStaging : public Task { ExponentialSeries backoff; QString m_stagingPath; unique_qobject_ptr m_child; - InstanceName m_instance_name; - QString m_groupName; QTimer m_backoffTimer; }; Task* InstanceList::wrapInstanceTask(InstanceTask* task) { - auto stagingPath = getStagedInstancePath(); - task->setStagingPath(stagingPath); - task->setParentSettings(m_globalSettings); - return new InstanceStaging(this, task, stagingPath, *task, task->group()); + return new InstanceStaging(this, task); } QString InstanceList::getStagedInstancePath() From bd0cd828269ee259dd10439c84baeaec2f1220df Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Feb 2024 18:41:01 +0000 Subject: [PATCH 268/755] Add upper-bound for randomisation Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index f50d21a6b..f4f225fd9 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -938,11 +938,15 @@ Task* InstanceList::wrapInstanceTask(InstanceTask* task) QString InstanceList::getStagedInstancePath() { - const QString tempRoot = FS::PathCombine(m_instDir, ".LAUNCHER_TEMP"); + const QString tempRoot = FS::PathCombine(m_instDir, ".tmp"); QString result; + int tries = 0; do { + if (++tries > 256) + return {}; + const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6); result = FS::PathCombine(tempRoot, key); } while (QFileInfo::exists(result)); From fccf857d8e5040fd64d7bc302463c1c9184f110f Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Feb 2024 19:16:39 +0000 Subject: [PATCH 269/755] Fix a mistake Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index f4f225fd9..acde60568 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -848,11 +848,15 @@ class InstanceStaging : public Task { const unsigned maxBackoff = 16; public: - InstanceStaging(InstanceList* parent, InstanceTask* child) : m_parent(parent), backoff(minBackoff, maxBackoff) + InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings) : m_parent(parent), backoff(minBackoff, maxBackoff) { m_stagingPath = parent->getStagedInstancePath(); m_child.reset(child); + + m_child->setStagingPath(m_stagingPath); + m_child->setParentSettings(std::move(settings)); + connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); connect(child, &Task::failed, this, &InstanceStaging::childFailed); connect(child, &Task::aborted, this, &InstanceStaging::childAborted); @@ -933,7 +937,7 @@ class InstanceStaging : public Task { Task* InstanceList::wrapInstanceTask(InstanceTask* task) { - return new InstanceStaging(this, task); + return new InstanceStaging(this, task, m_globalSettings); } QString InstanceList::getStagedInstancePath() From c0eb80947db57c4b0de376213d53aac4f694e1b0 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 24 Feb 2024 21:06:03 +0000 Subject: [PATCH 270/755] Fix build on Windows and formatting Signed-off-by: TheKodeToad --- launcher/InstanceList.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index acde60568..5e4abf020 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -848,7 +848,8 @@ class InstanceStaging : public Task { const unsigned maxBackoff = 16; public: - InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings) : m_parent(parent), backoff(minBackoff, maxBackoff) + InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObjectPtr settings) + : m_parent(parent), backoff(minBackoff, maxBackoff) { m_stagingPath = parent->getStagedInstancePath(); @@ -958,7 +959,7 @@ QString InstanceList::getStagedInstancePath() if (!QDir::current().mkpath(result)) return {}; #ifdef Q_OS_WIN32 - SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); + SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); #endif return result; } From 1b89c63ad477c1cfd7cb7f18ea32b045ae7ed59d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 25 Feb 2024 00:18:54 +0000 Subject: [PATCH 271/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/6e2f00c83911461438301db0dba5281197fe4b3a' (2024-02-17) → 'github:nixos/nixpkgs/f63ce824cd2f036216eb5f637dfef31e1a03ee89' (2024-02-24) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index b5d8e7494..ead111da1 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1708151420, - "narHash": "sha256-MGT/4aGCWQPQiu6COqJdCj9kSpLPiShgbwpbC38YXC8=", + "lastModified": 1708751719, + "narHash": "sha256-0uWOKSpXJXmXswOvDM5Vk3blB74apFB6rNGWV5IjoN0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "6e2f00c83911461438301db0dba5281197fe4b3a", + "rev": "f63ce824cd2f036216eb5f637dfef31e1a03ee89", "type": "github" }, "original": { From 344398f2382b879689aef7d4d02785faf0d9bff8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 26 Feb 2024 21:20:14 +0200 Subject: [PATCH 272/755] Updated ftb app import instance detection Signed-off-by: Trial97 --- .../modplatform/import_ftb/ImportFTBPage.cpp | 10 ++- .../modplatform/import_ftb/ListModel.cpp | 90 ++++++++++++------- .../pages/modplatform/import_ftb/ListModel.h | 16 ++-- 3 files changed, 76 insertions(+), 40 deletions(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index ac06f4cdd..c72a5a9da 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -21,6 +21,7 @@ #include "ui_ImportFTBPage.h" #include +#include #include #include "FileSystem.h" #include "ListModel.h" @@ -58,7 +59,14 @@ ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidg connect(ui->searchEdit, &QLineEdit::textChanged, this, &ImportFTBPage::triggerSearch); connect(ui->browseButton, &QPushButton::clicked, this, [this] { - auto path = listModel->getPath(); + auto paths = listModel->getPosiblePaths(); + QString path; + for (auto p : paths) { + if (p != "" && QFileInfo::exists(p)) { + path = p; + break; + } + } QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), path, QFileDialog::ShowDirsOnly); if (!dir.isEmpty()) listModel->setPath(dir); diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index e058937a6..ad03d571b 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -24,7 +24,9 @@ #include #include #include "Application.h" +#include "Exception.h" #include "FileSystem.h" +#include "Json.h" #include "StringUtils.h" #include "modplatform/import_ftb/PackHelpers.h" #include "ui/widgets/ProjectItem.h" @@ -41,27 +43,54 @@ QString getStaticPath() #else partialPath = QDir::homePath(); #endif - return FS::PathCombine(partialPath, ".ftba"); + return FS::PathCombine(partialPath, ".ftba", "instances"); } -static const QString FTB_APP_PATH = FS::PathCombine(getStaticPath(), "instances"); +QString getDynamicPath() +{ + auto settingsPath = FS::PathCombine(QDir::homePath(), ".ftba", "bin", "settings.json"); + if (!QFileInfo::exists(settingsPath)) { + qWarning() << "The ftb app setings doesn't exist."; + return {}; + } + try { + auto doc = Json::requireDocument(FS::read(settingsPath)); + return Json::requireString(Json::requireObject(doc), "instanceLocation"); + } catch (const Exception& e) { + qCritical() << "Could not read ftb settings file: " << e.cause(); + } + return {}; +} + +ListModel::ListModel(QObject* parent) : QAbstractListModel(parent), m_static_path(getStaticPath()), m_dynamic_path(getDynamicPath()) {} void ListModel::update() { beginResetModel(); - modpacks.clear(); + m_modpacks.clear(); - QString instancesPath = getPath(); - if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { - QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, - QDirIterator::FollowSymlinks); - while (directoryIterator.hasNext()) { - auto modpack = parseDirectory(directoryIterator.next()); - if (!modpack.path.isEmpty()) - modpacks.append(modpack); + auto paths = getPosiblePaths(); + paths.removeDuplicates(); + for (auto instancesPath : paths) { + if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { + QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, + QDirIterator::FollowSymlinks); + while (directoryIterator.hasNext()) { + auto currentPath = directoryIterator.next(); + bool wasAdded = false; + for (auto pack : m_modpacks) { + if (pack.path == currentPath) { + wasAdded = true; + break; + } + } + if (!wasAdded) { + auto modpack = parseDirectory(currentPath); + if (!modpack.path.isEmpty()) + m_modpacks.append(modpack); + } + } } - } else { - qDebug() << "Couldn't find ftb instances folder: " << instancesPath; } endResetModel(); @@ -70,11 +99,11 @@ void ListModel::update() QVariant ListModel::data(const QModelIndex& index, int role) const { int pos = index.row(); - if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { + if (pos >= m_modpacks.size() || pos < 0 || !index.isValid()) { return QVariant(); } - auto pack = modpacks.at(pos); + auto pack = m_modpacks.at(pos); if (role == Qt::ToolTipRole) { } @@ -110,9 +139,9 @@ QVariant ListModel::data(const QModelIndex& index, int role) const FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent) { - currentSorting = Sorting::ByGameVersion; - sortings.insert(tr("Sort by Name"), Sorting::ByName); - sortings.insert(tr("Sort by Game Version"), Sorting::ByGameVersion); + m_currentSorting = Sorting::ByGameVersion; + m_sortings.insert(tr("Sort by Name"), Sorting::ByName); + m_sortings.insert(tr("Sort by Game Version"), Sorting::ByGameVersion); } bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const @@ -120,12 +149,12 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value(); Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value(); - if (currentSorting == Sorting::ByGameVersion) { + if (m_currentSorting == Sorting::ByGameVersion) { Version lv(leftPack.mcVersion); Version rv(rightPack.mcVersion); return lv < rv; - } else if (currentSorting == Sorting::ByName) { + } else if (m_currentSorting == Sorting::ByName) { return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; } @@ -136,39 +165,39 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const { - if (searchTerm.isEmpty()) { + if (m_searchTerm.isEmpty()) { return true; } QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); Modpack pack = sourceModel()->data(index, Qt::UserRole).value(); - return pack.name.contains(searchTerm, Qt::CaseInsensitive); + return pack.name.contains(m_searchTerm, Qt::CaseInsensitive); } void FilterModel::setSearchTerm(const QString term) { - searchTerm = term.trimmed(); + m_searchTerm = term.trimmed(); invalidate(); } const QMap FilterModel::getAvailableSortings() { - return sortings; + return m_sortings; } QString FilterModel::translateCurrentSorting() { - return sortings.key(currentSorting); + return m_sortings.key(m_currentSorting); } void FilterModel::setSorting(Sorting s) { - currentSorting = s; + m_currentSorting = s; invalidate(); } FilterModel::Sorting FilterModel::getCurrentSorting() { - return currentSorting; + return m_currentSorting; } void ListModel::setPath(QString path) { @@ -176,11 +205,8 @@ void ListModel::setPath(QString path) update(); } -QString ListModel::getPath() +QStringList ListModel::getPosiblePaths() { - auto path = APPLICATION->settings()->get("FTBAppInstancesPath").toString(); - if (path.isEmpty() || !QFileInfo(path).exists()) - path = FTB_APP_PATH; - return path; + return { APPLICATION->settings()->get("FTBAppInstancesPath").toString(), m_dynamic_path, m_static_path }; } } // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h index ed33a88f3..393836b26 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -42,28 +42,30 @@ class FilterModel : public QSortFilterProxyModel { bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; private: - QMap sortings; - Sorting currentSorting; - QString searchTerm; + QMap m_sortings; + Sorting m_currentSorting; + QString m_searchTerm; }; class ListModel : public QAbstractListModel { Q_OBJECT public: - ListModel(QObject* parent) : QAbstractListModel(parent) {} + ListModel(QObject* parent); virtual ~ListModel() = default; - int rowCount(const QModelIndex& parent) const { return modpacks.size(); } + int rowCount(const QModelIndex& parent) const { return m_modpacks.size(); } int columnCount(const QModelIndex& parent) const { return 1; } QVariant data(const QModelIndex& index, int role) const; void update(); - QString getPath(); + QStringList getPosiblePaths(); void setPath(QString path); private: - ModpackList modpacks; + ModpackList m_modpacks; + const QString m_static_path; + const QString m_dynamic_path; }; } // namespace FTBImportAPP \ No newline at end of file From da96172b0b8835df8591368e96d20464a3a4eb41 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Mon, 26 Feb 2024 22:00:40 +0200 Subject: [PATCH 273/755] Apply suggestions from code review Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/java/VersionList.cpp | 2 +- launcher/ui/pages/global/JavaPage.cpp | 2 +- launcher/ui/pages/instance/InstanceSettingsPage.cpp | 2 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/launcher/ui/java/VersionList.cpp b/launcher/ui/java/VersionList.cpp index 4bf04224a..dc454f264 100644 --- a/launcher/ui/java/VersionList.cpp +++ b/launcher/ui/java/VersionList.cpp @@ -114,7 +114,7 @@ void VersionList::sortVersions() std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); } else { m_vlist = {}; - qWarning() << "Your operating system is not yet supported: " << SysInfo::currentSystem() << " " << SysInfo::useQTForArch(); + qWarning() << "No Java versions found for your operating system." << SysInfo::currentSystem() << " " << SysInfo::useQTForArch(); } endResetModel(); } diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index cb2a8b90b..95d5f581e 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -154,7 +154,7 @@ void JavaPage::on_javaDetectBtn_clicked() ui->javaPathTextBox->setText(java->path); if (!java->is_64bit && APPLICATION->settings()->get("MaxMemAlloc").toInt() > 2048) { CustomMessageBox::selectable(this, tr("Confirm Selection"), - tr("You selected an x86 java version.\n" + tr("You selected a 32 bit java version.\n" "This means that will not support more than 2048MiB of RAM.\n" "Please make sure that the maximum memory value is lower."), QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 8a63aab22..50cffc9a7 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -416,7 +416,7 @@ void InstanceSettingsPage::on_javaDetectBtn_clicked() if (!java->is_64bit && m_settings->get("MaxMemAlloc").toInt() > 2048) { CustomMessageBox::selectable(this, tr("Confirm Selection"), - tr("You selected an x86 java version.\n" + tr("You selected a 32 bit java version.\n" "This means that will not support more than 2048MiB of RAM.\n" "Please make sure that the maximum memory value is lower."), QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 4b5819288..f8031687b 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -173,8 +173,8 @@ void JavaSettingsWidget::initialize() updateThresholds(); auto button = CustomMessageBox::selectable(this, tr("Auto Java Download"), - tr("%1 has now the ability to auto downloand the correct java for each minecraft version.\n" - "Do you want to enable java auto-download?\n") + tr("%1 can automatically download the correct Java version for each version of Minecraft..\n" + "Do you want to enable Java auto-download?\n") .arg(BuildConfig.LAUNCHER_DISPLAYNAME), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ->exec(); @@ -505,7 +505,7 @@ void JavaSettingsWidget::updateThresholds() } else if (observedMaxMemory > 2048 && m_result.is_64bit) { iconName = "status-bad"; m_labelMaxMemIcon->setToolTip( - tr("Your maximum memory allocation exceeds selected java possible memory(due to x86 application limitations).")); + tr("Because you're using 32 bit Java, you're exceeding the maximum possible allocation.")); } else { iconName = "status-good"; m_labelMaxMemIcon->setToolTip(""); From 24fd07861b797fb845217c04b9ad5267edcc4470 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 26 Feb 2024 22:25:14 +0200 Subject: [PATCH 274/755] format code Signed-off-by: Trial97 --- launcher/ui/widgets/JavaSettingsWidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index f8031687b..29357bdab 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -504,8 +504,7 @@ void JavaSettingsWidget::updateThresholds() m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); } else if (observedMaxMemory > 2048 && m_result.is_64bit) { iconName = "status-bad"; - m_labelMaxMemIcon->setToolTip( - tr("Because you're using 32 bit Java, you're exceeding the maximum possible allocation.")); + m_labelMaxMemIcon->setToolTip(tr("Because you're using 32 bit Java, you're exceeding the maximum possible allocation.")); } else { iconName = "status-good"; m_labelMaxMemIcon->setToolTip(""); From 27e76d0dcb69d0f34fa64fca89964b231c194387 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 27 Feb 2024 17:11:19 +0200 Subject: [PATCH 275/755] Removed static path Signed-off-by: Trial97 --- .../modplatform/import_ftb/ImportFTBPage.cpp | 11 +-- .../modplatform/import_ftb/ListModel.cpp | 69 ++++++++++--------- .../pages/modplatform/import_ftb/ListModel.h | 5 +- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index c72a5a9da..db59fe10a 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -59,15 +59,8 @@ ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidg connect(ui->searchEdit, &QLineEdit::textChanged, this, &ImportFTBPage::triggerSearch); connect(ui->browseButton, &QPushButton::clicked, this, [this] { - auto paths = listModel->getPosiblePaths(); - QString path; - for (auto p : paths) { - if (p != "" && QFileInfo::exists(p)) { - path = p; - break; - } - } - QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), path, QFileDialog::ShowDirsOnly); + QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), listModel->getUserPath(), + QFileDialog::ShowDirsOnly); if (!dir.isEmpty()) listModel->setPath(dir); }); diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index ad03d571b..52671ea8e 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -33,22 +33,18 @@ namespace FTBImportAPP { -QString getStaticPath() +QString getFTBRoot() { - QString partialPath; + QString partialPath = QDir::homePath(); #if defined(Q_OS_OSX) - partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); -#elif defined(Q_OS_WIN32) - partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); -#else - partialPath = QDir::homePath(); + partialPath = FS::PathCombine(partialPath, "Library/Application Support"); #endif - return FS::PathCombine(partialPath, ".ftba", "instances"); + return FS::PathCombine(partialPath, ".ftba"); } QString getDynamicPath() { - auto settingsPath = FS::PathCombine(QDir::homePath(), ".ftba", "bin", "settings.json"); + auto settingsPath = FS::PathCombine(getFTBRoot(), "bin", "settings.json"); if (!QFileInfo::exists(settingsPath)) { qWarning() << "The ftb app setings doesn't exist."; return {}; @@ -62,36 +58,40 @@ QString getDynamicPath() return {}; } -ListModel::ListModel(QObject* parent) : QAbstractListModel(parent), m_static_path(getStaticPath()), m_dynamic_path(getDynamicPath()) {} +ListModel::ListModel(QObject* parent) : QAbstractListModel(parent), m_instances_path(getDynamicPath()) {} void ListModel::update() { beginResetModel(); m_modpacks.clear(); - auto paths = getPosiblePaths(); - paths.removeDuplicates(); - for (auto instancesPath : paths) { - if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { - QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, - QDirIterator::FollowSymlinks); - while (directoryIterator.hasNext()) { - auto currentPath = directoryIterator.next(); - bool wasAdded = false; - for (auto pack : m_modpacks) { - if (pack.path == currentPath) { - wasAdded = true; - break; - } - } - if (!wasAdded) { - auto modpack = parseDirectory(currentPath); - if (!modpack.path.isEmpty()) - m_modpacks.append(modpack); - } + auto wasPathAdded = [this](QString path) { + for (auto pack : m_modpacks) { + if (pack.path == path) + return true; + } + return false; + }; + + auto scanPath = [this, wasPathAdded](QString path) { + if (path.isEmpty()) + return; + if (auto instancesInfo = QFileInfo(path); !instancesInfo.exists() || !instancesInfo.isDir()) + return; + QDirIterator directoryIterator(path, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, + QDirIterator::FollowSymlinks); + while (directoryIterator.hasNext()) { + auto currentPath = directoryIterator.next(); + if (!wasPathAdded(currentPath)) { + auto modpack = parseDirectory(currentPath); + if (!modpack.path.isEmpty()) + m_modpacks.append(modpack); } } - } + }; + + scanPath(APPLICATION->settings()->get("FTBAppInstancesPath").toString()); + scanPath(m_instances_path); endResetModel(); } @@ -205,8 +205,11 @@ void ListModel::setPath(QString path) update(); } -QStringList ListModel::getPosiblePaths() +QString ListModel::getUserPath() { - return { APPLICATION->settings()->get("FTBAppInstancesPath").toString(), m_dynamic_path, m_static_path }; + auto path = APPLICATION->settings()->get("FTBAppInstancesPath").toString(); + if (path.isEmpty()) + path = m_instances_path; + return path; } } // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h index 393836b26..a842ac8ff 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -60,12 +60,11 @@ class ListModel : public QAbstractListModel { void update(); - QStringList getPosiblePaths(); + QString getUserPath(); void setPath(QString path); private: ModpackList m_modpacks; - const QString m_static_path; - const QString m_dynamic_path; + const QString m_instances_path; }; } // namespace FTBImportAPP \ No newline at end of file From 41d52dc34717aa2cad0537abb255a5a475bd4bca Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 29 Feb 2024 00:06:28 +0200 Subject: [PATCH 276/755] Remove dependencies if review mods is rejected Signed-off-by: Trial97 --- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 1431ea92c..11833c438 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -133,6 +133,7 @@ void ResourceDownloadDialog::confirm() confirm_dialog->retranslateUi(resourcesString()); QHash getRequiredBy; + QStringList depNames; if (auto task = getModDependenciesTask(); task) { connect(task.get(), &Task::failed, this, [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); @@ -155,8 +156,10 @@ void ResourceDownloadDialog::confirm() QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); return; } else { - for (auto dep : task->getDependecies()) + for (auto dep : task->getDependecies()) { addResource(dep->pack, dep->version); + depNames << dep->pack->name; + } getRequiredBy = task->getRequiredBy(); } } @@ -180,6 +183,9 @@ void ResourceDownloadDialog::confirm() } this->accept(); + } else { + for (auto name : depNames) + removeResource(name); } } From 322a8899ed574d20056f4ac0b52c4dd28f05ea46 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 17:19:39 +0000 Subject: [PATCH 277/755] chore(deps): update determinatesystems/update-flake-lock action to v21 --- .github/workflows/update-flake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index 2d78997ed..a6005cfe4 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@6004951b182f8860210c8d6f0d808ec5b1a33d28 # v25 - - uses: DeterminateSystems/update-flake-lock@v20 + - uses: DeterminateSystems/update-flake-lock@v21 with: commit-msg: "chore(nix): update lockfile" pr-title: "chore(nix): update lockfile" From e1146455a3fe2bcdfd68a295d317606dc2c45507 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 23:16:59 +0000 Subject: [PATCH 278/755] chore(deps): update actions/cache action to v4.0.1 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0352988f7..9cb737df6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -166,7 +166,7 @@ jobs: - name: Retrieve ccache cache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' - uses: actions/cache@v4.0.0 + uses: actions/cache@v4.0.1 with: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} From 138df66355b54151302f0b143ec1c2a5a4eb11c8 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Mar 2024 00:03:20 +0000 Subject: [PATCH 279/755] Fix server side mods being marked as unsupported on client Signed-off-by: TheKodeToad --- .../modrinth/ModrinthPackExportTask.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index e9e8a3b75..c704708ad 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -287,16 +287,12 @@ QByteArray ModrinthPackExportTask::generateIndex() env["client"] = "required"; env["server"] = "required"; } - switch (iterator->side) { - case Metadata::ModSide::ClientSide: - env["server"] = "unsupported"; - break; - case Metadata::ModSide::ServerSide: - env["client"] = "unsupported"; - break; - case Metadata::ModSide::UniversalSide: - break; - } + + // a server side mod does not imply that the mod does not work on the client + // however, if a mrpack mod is marked as server-only it will not install on the client + if (iterator->side == Metadata::ModSide::ClientSide) + env["server"] = "unsupported"; + fileOut["env"] = env; fileOut["path"] = path; From 4101fbb63433d1ee90476f55d76a7a7f0ced6ccb Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Mar 2024 00:54:18 +0000 Subject: [PATCH 280/755] Allow multiline description Signed-off-by: TheKodeToad --- launcher/ui/dialogs/ExportPackDialog.cpp | 25 +++++++--- launcher/ui/dialogs/ExportPackDialog.ui | 60 +++++++++++++++--------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 5af24b1b7..73e44efb1 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -47,11 +47,18 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { setWindowTitle(tr("Export Modrinth Pack")); - ui->summary->setText(instance->settings()->get("ExportSummary").toString()); + + ui->authorLabel->hide(); + ui->author->hide(); + + ui->summary->setPlainText(instance->settings()->get("ExportSummary").toString()); } else { setWindowTitle(tr("Export CurseForge Pack")); - ui->summaryLabel->setText(tr("&Author")); - ui->summary->setText(instance->settings()->get("ExportAuthor").toString()); + + ui->summaryLabel->hide(); + ui->summary->hide(); + + ui->author->setText(instance->settings()->get("ExportAuthor").toString()); } // ensure a valid pack is generated @@ -108,9 +115,13 @@ void ExportPackDialog::done(int result) auto settings = instance->settings(); settings->set("ExportName", ui->name->text()); settings->set("ExportVersion", ui->version->text()); - settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text()); settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked()); + if (m_provider == ModPlatform::ResourceProvider::MODRINTH) + settings->set("ExportSummary", ui->summary->toPlainText()); + else + settings->set("ExportAuthor", ui->author->text()); + if (result == Accepted) { const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text(); const QString filename = FS::RemoveInvalidFilenameChars(name); @@ -134,10 +145,10 @@ void ExportPackDialog::done(int result) Task* task; if (m_provider == ModPlatform::ResourceProvider::MODRINTH) { - task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, - output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); + task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->toPlainText(), ui->optionalFiles->isChecked(), + instance, output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); } else { - task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output, + task = new FlamePackExportTask(name, ui->version->text(), ui->author->text(), ui->optionalFiles->isChecked(), instance, output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1)); } diff --git a/launcher/ui/dialogs/ExportPackDialog.ui b/launcher/ui/dialogs/ExportPackDialog.ui index 09dea72a8..a4a174212 100644 --- a/launcher/ui/dialogs/ExportPackDialog.ui +++ b/launcher/ui/dialogs/ExportPackDialog.ui @@ -7,7 +7,7 @@ 0 0 650 - 510 + 532 @@ -19,21 +19,8 @@ &Description - - - - - &Summary - - - summary - - - - - - - + + &Name @@ -43,7 +30,10 @@ - + + + + &Version @@ -53,16 +43,43 @@ - - - - + 1.0.0 + + + + &Summary + + + summary + + + + + + + true + + + + + + + &Author + + + author + + + + + + @@ -124,6 +141,7 @@ name version summary + author files optionalFiles From d6abcb73c599695d41bd82501688c96dea7878f2 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:32:45 +0100 Subject: [PATCH 281/755] chore: bump develop to 9.0 until it's too late lol Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cceb2599..2e46bb605 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,7 +178,7 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help") ######## Set version numbers ######## -set(Launcher_VERSION_MAJOR 8) +set(Launcher_VERSION_MAJOR 9) set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") From 9120848278f8709525b574e92a4e602c593e1f4c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 6 Mar 2024 19:33:14 +0200 Subject: [PATCH 282/755] Made java downloader as a build option Signed-off-by: Trial97 --- CMakeLists.txt | 3 +++ buildconfig/BuildConfig.cpp.in | 4 +++ buildconfig/BuildConfig.h | 1 + launcher/CMakeLists.txt | 4 +++ launcher/ui/pages/global/JavaPage.cpp | 25 +++++++++++------- .../pages/instance/InstanceSettingsPage.cpp | 2 ++ launcher/ui/widgets/JavaSettingsWidget.cpp | 26 +++++++++++-------- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cceb2599..9b7104478 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,9 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules") set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") +# Java downloader +option(ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ON) + # Native libraries if(UNIX AND APPLE) set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index b40cacb0f..cdf3cbffc 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -81,6 +81,10 @@ Config::Config() UPDATER_ENABLED = true; } +#if ENABLE_JAVA_DOWNLOADER + JAVA_DOWNLOADER_ENABLED = true; +#endif + GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_TAG = "@Launcher_GIT_TAG@"; GIT_REFSPEC = "@Launcher_GIT_REFSPEC@"; diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 77b6eef54..e3beb8dbe 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -67,6 +67,7 @@ class Config { QString VERSION_CHANNEL; bool UPDATER_ENABLED = false; + bool JAVA_DOWNLOADER_ENABLED = false; /// A short string identifying this build's platform or distribution. QString BUILD_PLATFORM; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index fcc3c2f3a..f620388dc 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1320,6 +1320,10 @@ if(DEFINED Launcher_APP_BINARY_DEFS) target_compile_definitions(Launcher_logic PRIVATE ${Launcher_APP_BINARY_DEFS}) endif() +if(ENABLE_JAVA_DOWNLOADER) + target_compile_definitions(Launcher_logic PUBLIC ENABLE_JAVA_DOWNLOADER) +endif() + install(TARGETS ${Launcher_Name} BUNDLE DESTINATION "." COMPONENT Runtime LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 95d5f581e..0276daed5 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -35,6 +35,7 @@ */ #include "JavaPage.h" +#include "BuildConfig.h" #include "JavaCommon.h" #include "java/JavaInstall.h" #include "ui/dialogs/CustomMessageBox.h" @@ -62,15 +63,21 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) { ui->setupUi(this); - ui->managedJavaList->initialize(new JavaInstallList(this, true)); - ui->managedJavaList->selectCurrent(); - ui->managedJavaList->setEmptyString(tr("No java versions are currently available in the meta")); - ui->managedJavaList->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); - connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { - ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked()); - if (!ui->autodetectJavaCheckBox->isChecked()) - ui->autodownloadCheckBox->setChecked(false); - }); + if (BuildConfig.JAVA_DOWNLOADER_ENABLED) { + ui->managedJavaList->initialize(new JavaInstallList(this, true)); + ui->managedJavaList->selectCurrent(); + ui->managedJavaList->setEmptyString(tr("No java versions are currently available in the meta")); + ui->managedJavaList->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { + ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked()); + if (!ui->autodetectJavaCheckBox->isChecked()) + ui->autodownloadCheckBox->setChecked(false); + }); + } else { + ui->autodownloadCheckBox->setHidden(true); + ui->javaDownloadBtn->setHidden(true); + ui->tabWidget->tabBar()->hide(); + } loadSettings(); updateThresholds(); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 50cffc9a7..f2a723b81 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -64,6 +64,8 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent) m_settings = inst->settings(); ui->setupUi(this); + ui->javaDownloadBtn->setHidden(!BuildConfig.JAVA_DOWNLOADER_ENABLED); + connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked); connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings); connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 29357bdab..866d4c182 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -129,8 +129,10 @@ void JavaSettingsWidget::setupUi() m_horizontalBtnLayout = new QHBoxLayout(); m_horizontalBtnLayout->setObjectName(QStringLiteral("horizontalBtnLayout")); - m_javaDownloadBtn = new QPushButton(tr("Download Java"), this); - m_horizontalBtnLayout->addWidget(m_javaDownloadBtn); + if (BuildConfig.JAVA_DOWNLOADER_ENABLED) { + m_javaDownloadBtn = new QPushButton(tr("Download Java"), this); + m_horizontalBtnLayout->addWidget(m_javaDownloadBtn); + } m_verticalLayout->addLayout(m_horizontalBtnLayout); @@ -143,15 +145,17 @@ void JavaSettingsWidget::setupUi() m_autodetectJavaCheckBox->setObjectName("autodetectJavaCheckBox"); m_veriticalJavaLayout->addWidget(m_autodetectJavaCheckBox); - m_autodownloadCheckBox = new QCheckBox(m_autoJavaGroupBox); - m_autodownloadCheckBox->setObjectName("autodownloadCheckBox"); - m_autodownloadCheckBox->setEnabled(false); - m_veriticalJavaLayout->addWidget(m_autodownloadCheckBox); - connect(m_autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { - m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked()); - if (!m_autodetectJavaCheckBox->isChecked()) - m_autodownloadCheckBox->setChecked(false); - }); + if (BuildConfig.JAVA_DOWNLOADER_ENABLED) { + m_autodownloadCheckBox = new QCheckBox(m_autoJavaGroupBox); + m_autodownloadCheckBox->setObjectName("autodownloadCheckBox"); + m_autodownloadCheckBox->setEnabled(false); + m_veriticalJavaLayout->addWidget(m_autodownloadCheckBox); + connect(m_autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { + m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked()); + if (!m_autodetectJavaCheckBox->isChecked()) + m_autodownloadCheckBox->setChecked(false); + }); + } m_verticalLayout->addWidget(m_autoJavaGroupBox); retranslate(); From 6591379a8ac36e22970043460eecdc38fab4eb2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Mar 2024 07:47:18 +0000 Subject: [PATCH 283/755] chore(deps): update cachix/install-nix-action action to v26 --- .github/workflows/update-flake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index a6005cfe4..855b105ea 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@6004951b182f8860210c8d6f0d808ec5b1a33d28 # v25 + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 - uses: DeterminateSystems/update-flake-lock@v21 with: From 03def913aded7e9c4e39bcd07baee55406c43a17 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Mar 2024 21:26:24 +0000 Subject: [PATCH 284/755] chore(deps): update softprops/action-gh-release action to v2 --- .github/workflows/trigger_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 2afbaeb61..fa22c96d5 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -84,7 +84,7 @@ jobs: - name: Create release id: create_release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: token: ${{ secrets.GITHUB_TOKEN }} tag_name: ${{ github.ref }} From 92113e233aefbd2b255775d4e858e45845a94a2c Mon Sep 17 00:00:00 2001 From: chouzz Date: Sat, 9 Mar 2024 19:34:16 +0800 Subject: [PATCH 285/755] Add totalTimePlayed for FTBApp import Signed-off-by: chouzz --- launcher/modplatform/import_ftb/PackHelpers.cpp | 1 + launcher/modplatform/import_ftb/PackHelpers.h | 1 + launcher/modplatform/import_ftb/PackInstallTask.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp index ecf973452..e523b9d20 100644 --- a/launcher/modplatform/import_ftb/PackHelpers.cpp +++ b/launcher/modplatform/import_ftb/PackHelpers.cpp @@ -43,6 +43,7 @@ Modpack parseDirectory(QString path) modpack.version = Json::requireString(root, "version", "version"); modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion"); modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs"); + modpack.totalPlayTime = Json::requireInteger(root, "totalPlayTime", "totalPlayTime"); } catch (const Exception& e) { qDebug() << "Couldn't load ftb instance json: " << e.cause(); return {}; diff --git a/launcher/modplatform/import_ftb/PackHelpers.h b/launcher/modplatform/import_ftb/PackHelpers.h index 221eb5bf6..449ed2546 100644 --- a/launcher/modplatform/import_ftb/PackHelpers.h +++ b/launcher/modplatform/import_ftb/PackHelpers.h @@ -36,6 +36,7 @@ struct Modpack { QString name; QString version; QString mcVersion; + int totalPlayTime; // not needed for instance creation QVariant jvmArgs; diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp index 946ec4eb7..8046300e1 100644 --- a/launcher/modplatform/import_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/import_ftb/PackInstallTask.cpp @@ -55,6 +55,7 @@ void PackInstallTask::copySettings() instanceSettings->suspendSave(); MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath); instance.settings()->set("InstanceType", "OneSix"); + instance.settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000); if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) { instance.settings()->set("OverrideJavaArgs", true); From 75628bf81ec48925417374d7ae2672a0e48ce6d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 10 Mar 2024 00:19:11 +0000 Subject: [PATCH 286/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/b253292d9c0a5ead9bc98c4e9a26c6312e27d69f' (2024-02-01) → 'github:hercules-ci/flake-parts/f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2' (2024-03-01) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/f63ce824cd2f036216eb5f637dfef31e1a03ee89' (2024-02-24) → 'github:nixos/nixpkgs/0e7f98a5f30166cbed344569426850b21e4091d4' (2024-03-09) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index ead111da1..e7d3ba95f 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "lastModified": 1709336216, + "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", "type": "github" }, "original": { @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1708751719, - "narHash": "sha256-0uWOKSpXJXmXswOvDM5Vk3blB74apFB6rNGWV5IjoN0=", + "lastModified": 1709968316, + "narHash": "sha256-4rZEtEDT6jcgRaqxsatBeds7x1PoEiEjb6QNGb4mNrk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f63ce824cd2f036216eb5f637dfef31e1a03ee89", + "rev": "0e7f98a5f30166cbed344569426850b21e4091d4", "type": "github" }, "original": { From 853e4a13abfc2da266d505663c3784278ea5afad Mon Sep 17 00:00:00 2001 From: AtomHare <29772841+AtomHare@users.noreply.github.com> Date: Sat, 16 Mar 2024 12:44:55 +0100 Subject: [PATCH 287/755] fix: Compressing typo Signed-off-by: AtomHare <29772841+AtomHare@users.noreply.github.com> --- launcher/MMCZip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index ce2573329..033aa0755 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -463,7 +463,7 @@ auto ExportToZipTask::exportZip() -> ZipResult auto absolute = file.absoluteFilePath(); auto relative = m_dir.relativeFilePath(absolute); - setStatus("Compresing: " + relative); + setStatus("Compressing: " + relative); setProgress(m_progress + 1, m_progressTotal); if (m_follow_symlinks) { if (file.isSymLink()) From 95c3daca596f31d61fa947c6f62df60da33c07f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 17 Mar 2024 00:19:13 +0000 Subject: [PATCH 288/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/0e7f98a5f30166cbed344569426850b21e4091d4' (2024-03-09) → 'github:nixos/nixpkgs/9af9c1c87ed3e3ed271934cb896e0cdd33dae212' (2024-03-15) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index e7d3ba95f..ab1026c08 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709968316, - "narHash": "sha256-4rZEtEDT6jcgRaqxsatBeds7x1PoEiEjb6QNGb4mNrk=", + "lastModified": 1710534455, + "narHash": "sha256-huQT4Xs0y4EeFKn2BTBVYgEwJSv8SDlm82uWgMnCMmI=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0e7f98a5f30166cbed344569426850b21e4091d4", + "rev": "9af9c1c87ed3e3ed271934cb896e0cdd33dae212", "type": "github" }, "original": { From 05487152fe4eae0ad3d12b18c398ea2a99b44e32 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 17 Mar 2024 20:53:26 +0200 Subject: [PATCH 289/755] Fixed curesforge export Signed-off-by: Trial97 --- launcher/modplatform/flame/FlamePackExportTask.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index e28c82b4c..3a2028fd1 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -393,16 +393,16 @@ QByteArray FlamePackExportTask::generateIndex() version["version"] = minecraft->m_version; QString id; if (quilt != nullptr) - id = "quilt-" + quilt->getVersion(); + id = "quilt-" + quilt->m_version; else if (fabric != nullptr) - id = "fabric-" + fabric->getVersion(); + id = "fabric-" + fabric->m_version; else if (forge != nullptr) - id = "forge-" + forge->getVersion(); + id = "forge-" + forge->m_version; else if (neoforge != nullptr) { id = "neoforge-"; if (minecraft->m_version == "1.20.1") id += "1.20.1-"; - id += neoforge->getVersion(); + id += neoforge->m_version; } version["modLoaders"] = QJsonArray(); if (!id.isEmpty()) { From ef4e5eb3cf726e3b59f202d6eceee6f53450743e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 18 Mar 2024 19:15:36 +0200 Subject: [PATCH 290/755] fixed java build option Signed-off-by: Trial97 --- buildconfig/BuildConfig.cpp.in | 5 ++--- launcher/CMakeLists.txt | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index cdf3cbffc..a2b5c2187 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -81,9 +81,8 @@ Config::Config() UPDATER_ENABLED = true; } -#if ENABLE_JAVA_DOWNLOADER - JAVA_DOWNLOADER_ENABLED = true; -#endif + #cmakedefine01 ENABLE_JAVA_DOWNLOADER + JAVA_DOWNLOADER_ENABLED = ENABLE_JAVA_DOWNLOADER; GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_TAG = "@Launcher_GIT_TAG@"; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index f620388dc..fcc3c2f3a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1320,10 +1320,6 @@ if(DEFINED Launcher_APP_BINARY_DEFS) target_compile_definitions(Launcher_logic PRIVATE ${Launcher_APP_BINARY_DEFS}) endif() -if(ENABLE_JAVA_DOWNLOADER) - target_compile_definitions(Launcher_logic PUBLIC ENABLE_JAVA_DOWNLOADER) -endif() - install(TARGETS ${Launcher_Name} BUNDLE DESTINATION "." COMPONENT Runtime LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime From 7ffebcad72696e6779b065340098667ccaecd440 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:41:32 +0000 Subject: [PATCH 291/755] chore(deps): update actions/cache action to v4.0.2 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9cb737df6..ab2bd64b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -166,7 +166,7 @@ jobs: - name: Retrieve ccache cache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' - uses: actions/cache@v4.0.1 + uses: actions/cache@v4.0.2 with: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} From d72bd70c055d23223ee78fa62e6ae4ccfcb490d7 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 19 Mar 2024 20:34:55 +0000 Subject: [PATCH 292/755] Use UTF-8 for ZIP creation Signed-off-by: TheKodeToad --- launcher/MMCZip.cpp | 2 ++ launcher/MMCZip.h | 1 + 2 files changed, 3 insertions(+) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 033aa0755..2fb4d13df 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -119,6 +119,7 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) { QuaZip zip(fileCompressed); + zip.setUtf8Enabled(true); QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); if (!zip.open(QuaZip::mdCreate)) { QFile::remove(fileCompressed); @@ -141,6 +142,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { QuaZip zipOut(targetJarPath); + zipOut.setUtf8Enabled(true); if (!zipOut.open(QuaZip::mdCreate)) { QFile::remove(targetJarPath); qCritical() << "Failed to open the minecraft.jar for modding"; diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 93692d0d2..43b4ab933 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -163,6 +163,7 @@ class ExportToZipTask : public Task { , m_follow_symlinks(followSymlinks) { setAbortable(true); + m_output.setUtf8Enabled(true); }; ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){}; From 09c2c6793b52639f86f2edead268b51ad4b5fffc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 20 Mar 2024 00:15:02 +0200 Subject: [PATCH 293/755] Improve UI Signed-off-by: Trial97 --- launcher/BaseVersionList.cpp | 1 - launcher/BaseVersionList.h | 1 - launcher/CMakeLists.txt | 5 +- launcher/VersionProxyModel.cpp | 9 - launcher/VersionProxyModel.h | 2 +- launcher/java/JavaMetadata.cpp | 2 +- launcher/java/JavaMetadata.h | 2 +- launcher/minecraft/OneSixVersionFormat.cpp | 9 +- launcher/minecraft/VersionFile.h | 2 +- launcher/minecraft/launch/AutoInstallJava.cpp | 60 +++-- launcher/ui/java/InstallJavaDialog.cpp | 234 ++++++++++++++++++ .../{JavaDownloader.h => InstallJavaDialog.h} | 28 +-- launcher/ui/java/JavaDownloader.cpp | 110 -------- launcher/ui/java/JavaDownloader.ui | 100 -------- launcher/ui/java/VersionList.cpp | 14 +- launcher/ui/pages/global/JavaPage.cpp | 4 +- .../pages/instance/InstanceSettingsPage.cpp | 4 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 4 +- 18 files changed, 295 insertions(+), 296 deletions(-) create mode 100644 launcher/ui/java/InstallJavaDialog.cpp rename launcher/ui/java/{JavaDownloader.h => InstallJavaDialog.h} (63%) delete mode 100644 launcher/ui/java/JavaDownloader.cpp delete mode 100644 launcher/ui/java/JavaDownloader.ui diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index 1456a17bd..afee8388a 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -112,6 +112,5 @@ QHash BaseVersionList::roleNames() const roles.insert(PathRole, "path"); roles.insert(JavaNameRole, "javaName"); roles.insert(CPUArchitectureRole, "architecture"); - roles.insert(JavaVendorRole, "javaVendor"); return roles; } diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index 7517c71b4..bc37e9e53 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -50,7 +50,6 @@ class BaseVersionList : public QAbstractListModel { PathRole, JavaNameRole, CPUArchitectureRole, - JavaVendorRole, SortRole }; using RoleList = QList; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index fcc3c2f3a..dd493682e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -440,8 +440,8 @@ set(JAVA_SOURCES java/download/ManifestDownloadTask.cpp java/download/ManifestDownloadTask.h - ui/java/JavaDownloader.h - ui/java/JavaDownloader.cpp + ui/java/InstallJavaDialog.h + ui/java/InstallJavaDialog.cpp ui/java/VersionList.h ui/java/VersionList.cpp ) @@ -1194,7 +1194,6 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ScrollMessageBox.ui ui/dialogs/BlockedModsDialog.ui ui/dialogs/ChooseProviderDialog.ui - ui/java/JavaDownloader.ui ) qt_wrap_ui(PRISM_UPDATE_UI diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index ed4c0767d..070e952a4 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -116,8 +116,6 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("Type"); case CPUArchitecture: return tr("Architecture"); - case JavaVendor: - return tr("Vendor"); case Path: return tr("Path"); case JavaName: @@ -137,8 +135,6 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("The version's type"); case CPUArchitecture: return tr("CPU Architecture"); - case JavaVendor: - return tr("Java vendor"); case Path: return tr("Filesystem path to this version"); case JavaName: @@ -175,8 +171,6 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const return sourceModel()->data(parentIndex, BaseVersionList::TypeRole); case CPUArchitecture: return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole); - case JavaVendor: - return sourceModel()->data(parentIndex, BaseVersionList::JavaVendorRole); case Path: return sourceModel()->data(parentIndex, BaseVersionList::PathRole); case JavaName: @@ -323,9 +317,6 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) if (roles.contains(BaseVersionList::CPUArchitectureRole)) { m_columns.push_back(CPUArchitecture); } - if (roles.contains(BaseVersionList::JavaVendorRole)) { - m_columns.push_back(JavaVendor); - } if (roles.contains(BaseVersionList::PathRole)) { m_columns.push_back(Path); } diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index 5a1017bee..cb55b7f14 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -9,7 +9,7 @@ class VersionFilterModel; class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: - enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaVendor }; + enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName }; using FilterMap = QHash>; public: diff --git a/launcher/java/JavaMetadata.cpp b/launcher/java/JavaMetadata.cpp index b73c54fe6..e721947a7 100644 --- a/launcher/java/JavaMetadata.cpp +++ b/launcher/java/JavaMetadata.cpp @@ -52,9 +52,9 @@ MetadataPtr parseJavaMeta(const QJsonObject& in) meta->vendor = Json::ensureString(in, "vendor", ""); meta->url = Json::ensureString(in, "url", ""); meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", "")); - meta->recommended = Json::ensureBoolean(in, "recommended", false); meta->downloadType = parseDownloadType(Json::ensureString(in, "downloadType", "")); meta->packageType = Json::ensureString(in, "packageType", ""); + meta->runtimeOS = Json::ensureString(in, "runtimeOS", "unknown"); if (in.contains("checksum")) { auto obj = Json::requireObject(in, "checksum"); diff --git a/launcher/java/JavaMetadata.h b/launcher/java/JavaMetadata.h index 640a412f1..5281f2e27 100644 --- a/launcher/java/JavaMetadata.h +++ b/launcher/java/JavaMetadata.h @@ -50,10 +50,10 @@ class Metadata : public BaseVersion { QDateTime releaseTime; QString checksumType; QString checksumHash; - bool recommended; DownloadType downloadType; QString packageType; JavaVersion version; + QString runtimeOS; }; using MetadataPtr = std::shared_ptr; diff --git a/launcher/minecraft/OneSixVersionFormat.cpp b/launcher/minecraft/OneSixVersionFormat.cpp index 56e9c8ca2..bd587beb2 100644 --- a/launcher/minecraft/OneSixVersionFormat.cpp +++ b/launcher/minecraft/OneSixVersionFormat.cpp @@ -258,14 +258,9 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc } if (root.contains("runtimes")) { - auto runtimes = requireObject(root, "runtimes"); out->runtimes = {}; - for (auto key : runtimes.keys()) { - QList list; - for (auto runtime : ensureArray(runtimes, key)) { - list.append(Java::parseJavaMeta(ensureObject(runtime))); - } - out->runtimes[key] = list; + for (auto runtime : ensureArray(root, "runtimes")) { + out->runtimes.append(Java::parseJavaMeta(ensureObject(runtime))); } } diff --git a/launcher/minecraft/VersionFile.h b/launcher/minecraft/VersionFile.h index a9c1473d2..85ac55426 100644 --- a/launcher/minecraft/VersionFile.h +++ b/launcher/minecraft/VersionFile.h @@ -155,7 +155,7 @@ class VersionFile : public ProblemContainer { /// is volatile -- may be removed as soon as it is no longer needed by something else bool m_volatile = false; - QHash> runtimes; + QList runtimes; public: // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more. diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp index ab68910a7..3ae6ffa88 100644 --- a/launcher/minecraft/launch/AutoInstallJava.cpp +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -145,39 +145,35 @@ void AutoInstallJava::setJavaPathFromPartial() void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName) { auto runtimes = version->data()->runtimes; - if (runtimes.contains(m_supported_arch)) { - for (auto java : runtimes.value(m_supported_arch)) { - if (java->name() == javaName) { - QDir javaDir(APPLICATION->javaPath()); - auto final_path = javaDir.absoluteFilePath(java->m_name); - switch (java->downloadType) { - case Java::DownloadType::Manifest: - m_current_task = - makeShared(java->url, final_path, java->checksumType, java->checksumHash); - break; - case Java::DownloadType::Archive: - m_current_task = - makeShared(java->url, final_path, java->checksumType, java->checksumHash); - break; - } - auto deletePath = [final_path] { FS::deletePath(final_path); }; - connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) { - deletePath(); - emitFailed(reason); - }); - connect(this, &Task::aborted, this, [this, deletePath] { - m_current_task->abort(); - deletePath(); - }); - connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); - connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); - connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); - connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); - connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); - connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); - m_current_task->start(); - return; + for (auto java : runtimes) { + if (java->runtimeOS == m_supported_arch && java->name() == javaName) { + QDir javaDir(APPLICATION->javaPath()); + auto final_path = javaDir.absoluteFilePath(java->m_name); + switch (java->downloadType) { + case Java::DownloadType::Manifest: + m_current_task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + break; + case Java::DownloadType::Archive: + m_current_task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + break; } + auto deletePath = [final_path] { FS::deletePath(final_path); }; + connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) { + deletePath(); + emitFailed(reason); + }); + connect(this, &Task::aborted, this, [this, deletePath] { + m_current_task->abort(); + deletePath(); + }); + connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); + connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); + connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); + connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); + connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); + connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); + m_current_task->start(); + return; } } tryNextMajorJava(); diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp new file mode 100644 index 000000000..054e5cece --- /dev/null +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "InstallJavaDialog.h" + +#include +#include +#include +#include + +#include "Application.h" +#include "FileSystem.h" +#include "java/download/ArchiveDownloadTask.h" +#include "java/download/ManifestDownloadTask.h" +#include "meta/Index.h" +#include "meta/VersionList.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/java/VersionList.h" +#include "ui/widgets/PageContainer.h" +#include "ui/widgets/VersionSelectWidget.h" + +class InstallLoaderPage : public QWidget, public BasePage { + public: + Q_OBJECT + public: + explicit InstallLoaderPage(const QString& id, const QString& iconName, const QString& name, QWidget* parent = nullptr) + : QWidget(parent), uid(id), iconName(iconName), name(name) + { + setObjectName(QStringLiteral("VersionSelectWidget")); + horizontalLayout = new QHBoxLayout(this); + horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); + horizontalLayout->setContentsMargins(0, 0, 0, 0); + + majorVersionSelect = new VersionSelectWidget(this); + majorVersionSelect->selectCurrent(); + majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta.")); + majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + horizontalLayout->addWidget(majorVersionSelect, 1); + + javaVersionSelect = new VersionSelectWidget(this); + javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); + javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + horizontalLayout->addWidget(javaVersionSelect, 4); + connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallLoaderPage::setSelectedVersion); + connect(javaVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallLoaderPage::selectedVersionChanged); + + QMetaObject::connectSlotsByName(this); + } + ~InstallLoaderPage() + { + delete horizontalLayout; + delete majorVersionSelect; + delete javaVersionSelect; + } + + //! loads the list if needed. + void initialize(Meta::VersionList::Ptr vlist) + { + vlist->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); + majorVersionSelect->initialize(vlist.get()); + } + + void setSelectedVersion(BaseVersion::Ptr version) + { + auto dcast = std::dynamic_pointer_cast(version); + if (!dcast) { + return; + } + javaVersionSelect->initialize(new Java::VersionList(dcast, this)); + javaVersionSelect->selectCurrent(); + } + + QString id() const override { return uid; } + QString displayName() const override { return name; } + QIcon icon() const override { return APPLICATION->getThemedIcon(iconName); } + + void openedImpl() override + { + if (loaded) + return; + + const auto versions = APPLICATION->metadataIndex()->get(uid); + if (!versions) + return; + + initialize(versions); + loaded = true; + } + + void setParentContainer(BasePageContainer* container) override + { + auto dialog = dynamic_cast(dynamic_cast(container)->parent()); + connect(javaVersionSelect->view(), &QAbstractItemView::doubleClicked, dialog, &QDialog::accept); + } + + BaseVersion::Ptr selectedVersion() const { return javaVersionSelect->selectedVersion(); } + void selectSearch() { javaVersionSelect->selectSearch(); } + void loadList() + { + majorVersionSelect->loadList(); + javaVersionSelect->loadList(); + } + signals: + void selectedVersionChanged(BaseVersion::Ptr version); + + private: + const QString uid; + const QString iconName; + const QString name; + bool loaded = false; + + QHBoxLayout* horizontalLayout = nullptr; + VersionSelectWidget* majorVersionSelect = nullptr; + VersionSelectWidget* javaVersionSelect = nullptr; +}; + +static InstallLoaderPage* pageCast(BasePage* page) +{ + auto result = dynamic_cast(page); + Q_ASSERT(result != nullptr); + return result; +} +namespace Java { + +InstallDialog::InstallDialog(const QString& uid, QWidget* parent) + : QDialog(parent), container(new PageContainer(this, QString(), this)), buttons(new QDialogButtonBox(this)) +{ + auto layout = new QVBoxLayout(this); + + container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + layout->addWidget(container); + + auto buttonLayout = new QHBoxLayout(this); + + auto refreshButton = new QPushButton(tr("&Refresh"), this); + connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(); }); + buttonLayout->addWidget(refreshButton); + + buttons->setOrientation(Qt::Horizontal); + buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + buttons->button(QDialogButtonBox::Ok)->setText(tr("Download")); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + buttonLayout->addWidget(buttons); + + layout->addLayout(buttonLayout); + + setWindowTitle(dialogTitle()); + setWindowModality(Qt::WindowModal); + resize(840, 480); + + for (BasePage* page : container->getPages()) { + if (page->id() == uid) + container->selectPage(page->id()); + + connect(pageCast(page), &InstallLoaderPage::selectedVersionChanged, this, [this, page] { + if (page->id() == container->selectedPage()->id()) + validate(container->selectedPage()); + }); + } + connect(container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* current) { validate(current); }); + pageCast(container->selectedPage())->selectSearch(); + validate(container->selectedPage()); +} + +QList InstallDialog::getPages() +{ + return { + // NeoForge + new InstallLoaderPage("net.minecraft.java", "", tr("Mojang")), + // Forge + new InstallLoaderPage("net.adoptium.java", "", tr("Adoptium")), + // Fabric + new InstallLoaderPage("com.azul.java", "", tr("Azul")), + }; +} + +QString InstallDialog::dialogTitle() +{ + return tr("Install Loader"); +} + +void InstallDialog::validate(BasePage* page) +{ + buttons->button(QDialogButtonBox::Ok)->setEnabled(pageCast(page)->selectedVersion() != nullptr); +} + +void InstallDialog::done(int result) +{ + if (result == Accepted) { + auto* page = pageCast(container->selectedPage()); + if (page->selectedVersion()) { + auto meta = std::dynamic_pointer_cast(page->selectedVersion()); + if (meta) { + Task::Ptr task; + auto final_path = FS::PathCombine(APPLICATION->javaPath(), meta->m_name); + switch (meta->downloadType) { + case Java::DownloadType::Manifest: + task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); + break; + case Java::DownloadType::Archive: + task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); + break; + } + auto deletePath = [final_path] { FS::deletePath(final_path); }; + connect(task.get(), &Task::failed, this, deletePath); + connect(task.get(), &Task::aborted, this, deletePath); + ProgressDialog pg(this); + pg.setSkipButton(true, tr("Abort")); + pg.execWithTask(task.get()); + } + } + } + + QDialog::done(result); +} +} // namespace Java + +#include "InstallJavaDialog.moc" \ No newline at end of file diff --git a/launcher/ui/java/JavaDownloader.h b/launcher/ui/java/InstallJavaDialog.h similarity index 63% rename from launcher/ui/java/JavaDownloader.h rename to launcher/ui/java/InstallJavaDialog.h index b8bdde41a..e228adf21 100644 --- a/launcher/ui/java/JavaDownloader.h +++ b/launcher/ui/java/InstallJavaDialog.h @@ -19,30 +19,28 @@ #pragma once #include -#include "BaseVersion.h" +#include "ui/pages/BasePageProvider.h" -namespace Ui { -class JavaDownloader; -} +class MinecraftInstance; +class PageContainer; +class PackProfile; +class QDialogButtonBox; namespace Java { - -class Downloader : public QDialog { +class InstallDialog final : public QDialog, protected BasePageProvider { Q_OBJECT public: - explicit Downloader(QWidget* parent = 0); - ~Downloader(); + explicit InstallDialog(const QString& uid = QString(), QWidget* parent = nullptr); - void accept(); + QList getPages() override; + QString dialogTitle() override; - public slots: - void refresh(); - - protected slots: - void setSelectedVersion(BaseVersion::Ptr version); + void validate(BasePage* page); + void done(int result) override; private: - Ui::JavaDownloader* ui; + PageContainer* container; + QDialogButtonBox* buttons; }; } // namespace Java diff --git a/launcher/ui/java/JavaDownloader.cpp b/launcher/ui/java/JavaDownloader.cpp deleted file mode 100644 index 24918dd76..000000000 --- a/launcher/ui/java/JavaDownloader.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2024 Trial97 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "JavaDownloader.h" - -#include - -#include - -#include "Application.h" -#include "BaseVersionList.h" -#include "FileSystem.h" -#include "QObjectPtr.h" -#include "SysInfo.h" - -#include "java/JavaMetadata.h" -#include "java/download/ArchiveDownloadTask.h" -#include "java/download/ManifestDownloadTask.h" - -#include "meta/Index.h" -#include "meta/Version.h" - -#include "meta/VersionList.h" -#include "ui/dialogs/ProgressDialog.h" -#include "ui/java/VersionList.h" -#include "ui_JavaDownloader.h" - -namespace Java { - -Downloader::Downloader(QWidget* parent) : QDialog(parent), ui(new Ui::JavaDownloader) -{ - ui->setupUi(this); - auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); - versionList->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); - ui->majorVersionSelect->initialize(versionList.get()); - ui->majorVersionSelect->selectCurrent(); - ui->majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta.")); - ui->majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); - - ui->javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); - ui->javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); - - ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Refresh")); - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Download")); - - connect(ui->majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &Downloader::setSelectedVersion); - auto reset = ui->buttonBox->button(QDialogButtonBox::Reset); - connect(reset, &QPushButton::clicked, this, &Downloader::refresh); -} - -Downloader::~Downloader() -{ - delete ui; -} - -void Downloader::setSelectedVersion(BaseVersion::Ptr version) -{ - auto dcast = std::dynamic_pointer_cast(version); - if (!dcast) { - return; - } - ui->javaVersionSelect->initialize(new Java::VersionList(dcast, this)); - ui->javaVersionSelect->selectCurrent(); -} - -void Downloader::accept() -{ - auto meta = std::dynamic_pointer_cast(ui->javaVersionSelect->selectedVersion()); - if (!meta) { - return; - } - Task::Ptr task; - auto final_path = FS::PathCombine(APPLICATION->javaPath(), meta->m_name); - switch (meta->downloadType) { - case Java::DownloadType::Manifest: - task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); - break; - case Java::DownloadType::Archive: - task = makeShared(meta->url, final_path, meta->checksumType, meta->checksumHash); - break; - } - auto deletePath = [final_path] { FS::deletePath(final_path); }; - connect(task.get(), &Task::failed, this, deletePath); - connect(task.get(), &Task::aborted, this, deletePath); - ProgressDialog pg(this); - pg.setSkipButton(true, tr("Abort")); - pg.execWithTask(task.get()); - QDialog::accept(); -} - -void Downloader::refresh() -{ - ui->majorVersionSelect->loadList(); -} -} // namespace Java diff --git a/launcher/ui/java/JavaDownloader.ui b/launcher/ui/java/JavaDownloader.ui deleted file mode 100644 index 3f6a8adc6..000000000 --- a/launcher/ui/java/JavaDownloader.ui +++ /dev/null @@ -1,100 +0,0 @@ - - - JavaDownloader - - - - 0 - 0 - 821 - 593 - - - - Dialog - - - - - - - - Major - - - - - - - - - - - - Runtime - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset - - - - - - - - VersionSelectWidget - QWidget -
    ui/widgets/VersionSelectWidget.h
    - 1 -
    -
    - - - - buttonBox - accepted() - JavaDownloader - accept() - - - 257 - 583 - - - 157 - 274 - - - - - buttonBox - rejected() - JavaDownloader - reject() - - - 325 - 583 - - - 286 - 274 - - - - -
    diff --git a/launcher/ui/java/VersionList.cpp b/launcher/ui/java/VersionList.cpp index dc454f264..78448ddaf 100644 --- a/launcher/ui/java/VersionList.cpp +++ b/launcher/ui/java/VersionList.cpp @@ -75,11 +75,9 @@ QVariant VersionList::data(const QModelIndex& index, int role) const case VersionRole: return version->version.toString(); case RecommendedRole: - return version->recommended; + return false; // do not recommend any version case JavaNameRole: return version->name(); - case JavaVendorRole: - return version->vendor; case TypeRole: return version->packageType; case Meta::VersionList::TimeRole: @@ -91,8 +89,7 @@ QVariant VersionList::data(const QModelIndex& index, int role) const BaseVersionList::RoleList VersionList::providesRoles() const { - return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, - JavaNameRole, JavaVendorRole, TypeRole, Meta::VersionList::TimeRole }; + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, JavaNameRole, TypeRole, Meta::VersionList::TimeRole }; } bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) @@ -109,11 +106,12 @@ void VersionList::sortVersions() QString versionStr = SysInfo::getSupportedJavaArchitecture(); beginResetModel(); auto runtimes = m_version->data()->runtimes; - if (!versionStr.isEmpty() && !runtimes.isEmpty() && runtimes.contains(versionStr)) { - m_vlist = runtimes.value(versionStr); + m_vlist = {}; + if (!versionStr.isEmpty() && !runtimes.isEmpty()) { + std::copy_if(runtimes.begin(), runtimes.end(), std::back_inserter(m_vlist), + [versionStr](Java::MetadataPtr val) { return val->runtimeOS == versionStr; }); std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); } else { - m_vlist = {}; qWarning() << "No Java versions found for your operating system." << SysInfo::currentSystem() << " " << SysInfo::useQTForArch(); } endResetModel(); diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 0276daed5..41a666cf2 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -39,7 +39,7 @@ #include "JavaCommon.h" #include "java/JavaInstall.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/java/JavaDownloader.h" +#include "ui/java/InstallJavaDialog.h" #include "ui_JavaPage.h" #include @@ -201,7 +201,7 @@ void JavaPage::on_javaTestBtn_clicked() void JavaPage::on_javaDownloadBtn_clicked() { - auto jdialog = new Java::Downloader(this); + auto jdialog = new Java::InstallDialog({}, this); jdialog->exec(); ui->managedJavaList->loadList(); } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 0b731e64f..c248a47f7 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -37,7 +37,7 @@ #include "InstanceSettingsPage.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/java/JavaDownloader.h" +#include "ui/java/InstallJavaDialog.h" #include "ui_InstanceSettingsPage.h" #include @@ -394,7 +394,7 @@ void InstanceSettingsPage::loadSettings() void InstanceSettingsPage::on_javaDownloadBtn_clicked() { - auto jdialog = new Java::Downloader(this); + auto jdialog = new Java::InstallDialog({}, this); jdialog->exec(); } diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 866d4c182..e2e498067 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -21,7 +21,7 @@ #include "java/JavaUtils.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/java/JavaDownloader.h" +#include "ui/java/InstallJavaDialog.h" #include "ui/widgets/VersionSelectWidget.h" #include "Application.h" @@ -351,7 +351,7 @@ void JavaSettingsWidget::on_javaBrowseBtn_clicked() void JavaSettingsWidget::on_javaDownloadBtn_clicked() { - auto jdialog = new Java::Downloader(this); + auto jdialog = new Java::InstallDialog({}, this); jdialog->exec(); } From 5f8269f5e1ac41b29ed386e1a675e972c8d5624e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 20 Mar 2024 00:29:46 +0200 Subject: [PATCH 294/755] Small class rename Signed-off-by: Trial97 --- launcher/ui/java/InstallJavaDialog.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp index 054e5cece..40616aafe 100644 --- a/launcher/ui/java/InstallJavaDialog.cpp +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -34,11 +34,11 @@ #include "ui/widgets/PageContainer.h" #include "ui/widgets/VersionSelectWidget.h" -class InstallLoaderPage : public QWidget, public BasePage { +class InstallJavaPage : public QWidget, public BasePage { public: Q_OBJECT public: - explicit InstallLoaderPage(const QString& id, const QString& iconName, const QString& name, QWidget* parent = nullptr) + explicit InstallJavaPage(const QString& id, const QString& iconName, const QString& name, QWidget* parent = nullptr) : QWidget(parent), uid(id), iconName(iconName), name(name) { setObjectName(QStringLiteral("VersionSelectWidget")); @@ -56,12 +56,12 @@ class InstallLoaderPage : public QWidget, public BasePage { javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); horizontalLayout->addWidget(javaVersionSelect, 4); - connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallLoaderPage::setSelectedVersion); - connect(javaVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallLoaderPage::selectedVersionChanged); + connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion); + connect(javaVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectedVersionChanged); QMetaObject::connectSlotsByName(this); } - ~InstallLoaderPage() + ~InstallJavaPage() { delete horizontalLayout; delete majorVersionSelect; @@ -129,9 +129,9 @@ class InstallLoaderPage : public QWidget, public BasePage { VersionSelectWidget* javaVersionSelect = nullptr; }; -static InstallLoaderPage* pageCast(BasePage* page) +static InstallJavaPage* pageCast(BasePage* page) { - auto result = dynamic_cast(page); + auto result = dynamic_cast(page); Q_ASSERT(result != nullptr); return result; } @@ -168,7 +168,7 @@ InstallDialog::InstallDialog(const QString& uid, QWidget* parent) if (page->id() == uid) container->selectPage(page->id()); - connect(pageCast(page), &InstallLoaderPage::selectedVersionChanged, this, [this, page] { + connect(pageCast(page), &InstallJavaPage::selectedVersionChanged, this, [this, page] { if (page->id() == container->selectedPage()->id()) validate(container->selectedPage()); }); @@ -182,11 +182,11 @@ QList InstallDialog::getPages() { return { // NeoForge - new InstallLoaderPage("net.minecraft.java", "", tr("Mojang")), + new InstallJavaPage("net.minecraft.java", "", tr("Mojang")), // Forge - new InstallLoaderPage("net.adoptium.java", "", tr("Adoptium")), + new InstallJavaPage("net.adoptium.java", "", tr("Adoptium")), // Fabric - new InstallLoaderPage("com.azul.java", "", tr("Azul")), + new InstallJavaPage("com.azul.java", "", tr("Azul")), }; } From 703470e57d615b1bb1998a253865c8fe7fadcccd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 20 Mar 2024 08:24:40 +0200 Subject: [PATCH 295/755] Hopefully fix codeql Signed-off-by: Trial97 --- launcher/ui/java/InstallJavaDialog.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/java/InstallJavaDialog.h b/launcher/ui/java/InstallJavaDialog.h index e228adf21..c98c1deae 100644 --- a/launcher/ui/java/InstallJavaDialog.h +++ b/launcher/ui/java/InstallJavaDialog.h @@ -27,7 +27,7 @@ class PackProfile; class QDialogButtonBox; namespace Java { -class InstallDialog final : public QDialog, protected BasePageProvider { +class InstallDialog final : public QDialog, public BasePageProvider { Q_OBJECT public: From 1c809f0fb1ef477d0315a7d1af174852b3b831e8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 20 Mar 2024 17:39:10 +0200 Subject: [PATCH 296/755] Added more logs for auto java install Signed-off-by: Trial97 --- launcher/minecraft/launch/AutoInstallJava.cpp | 19 ++++++++++++++++++- launcher/ui/java/InstallJavaDialog.cpp | 1 + launcher/ui/java/InstallJavaDialog.h | 2 +- launcher/ui/pages/global/JavaPage.cpp | 5 ++--- launcher/ui/pages/global/JavaPage.ui | 8 -------- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp index 3ae6ffa88..1dc91003f 100644 --- a/launcher/minecraft/launch/AutoInstallJava.cpp +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -91,7 +91,21 @@ void AutoInstallJava::executeTask() emit progressReportingRequest(); return; } + if (m_supported_arch.isEmpty()) { + emit logLine(tr("Your system(%1 %2) is not compatible with auto java download. Using the default java path.") + .arg(SysInfo::currentSystem(), SysInfo::useQTForArch()), + MessageLevel::Warning); + emitSucceeded(); + return; + } auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); + if (wantedJavaName.isEmpty()) { + emit logLine(tr("Your meta informtation is old or doesn't have the information necesary to determine what java should be used. " + "Using the default java path."), + MessageLevel::Warning); + emitSucceeded(); + return; + } QDir javaDir(APPLICATION->javaPath()); auto wantedJavaPath = javaDir.absoluteFilePath(wantedJavaName); if (QFileInfo::exists(wantedJavaPath)) { @@ -136,7 +150,8 @@ void AutoInstallJava::setJavaPathFromPartial() if (QFileInfo::exists(finalPath)) { setJavaPath(finalPath); } else { - emit logLine(tr("No compatible java version was found. Using the default one."), MessageLevel::Warning); + emit logLine(tr("No compatible java version was found(the binary file doesn't exists). Using the default one."), + MessageLevel::Warning); emitSucceeded(); } return; @@ -188,6 +203,8 @@ void AutoInstallJava::tryNextMajorJava() auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); auto majorJavaVersions = packProfile->getProfile()->getCompatibleJavaMajors(); if (m_majorJavaVersionIndex >= majorJavaVersions.length()) { + emit logLine(tr("No Java versions found for your operating system: %1 %2").arg(SysInfo::currentSystem(), SysInfo::useQTForArch()), + MessageLevel::Warning); emit logLine(tr("No compatible java version was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); return; diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp index 40616aafe..874fc9133 100644 --- a/launcher/ui/java/InstallJavaDialog.cpp +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -72,6 +72,7 @@ class InstallJavaPage : public QWidget, public BasePage { void initialize(Meta::VersionList::Ptr vlist) { vlist->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); + vlist->sort(1); majorVersionSelect->initialize(vlist.get()); } diff --git a/launcher/ui/java/InstallJavaDialog.h b/launcher/ui/java/InstallJavaDialog.h index c98c1deae..525e750aa 100644 --- a/launcher/ui/java/InstallJavaDialog.h +++ b/launcher/ui/java/InstallJavaDialog.h @@ -27,7 +27,7 @@ class PackProfile; class QDialogButtonBox; namespace Java { -class InstallDialog final : public QDialog, public BasePageProvider { +class InstallDialog final : public QDialog, private BasePageProvider { Q_OBJECT public: diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 41a666cf2..6d8b95c24 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -66,8 +66,8 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) if (BuildConfig.JAVA_DOWNLOADER_ENABLED) { ui->managedJavaList->initialize(new JavaInstallList(this, true)); ui->managedJavaList->selectCurrent(); - ui->managedJavaList->setEmptyString(tr("No java versions are currently available in the meta")); - ui->managedJavaList->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + ui->managedJavaList->setEmptyString(tr("No managed java versions are installed")); + ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed java list!")); connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked()); if (!ui->autodetectJavaCheckBox->isChecked()) @@ -75,7 +75,6 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) }); } else { ui->autodownloadCheckBox->setHidden(true); - ui->javaDownloadBtn->setHidden(true); ui->tabWidget->tabBar()->hide(); } diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index dc1668ee3..c8f62e075 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -178,13 +178,6 @@ - - - - Download Java - - - @@ -424,7 +417,6 @@ permGenSpinBox javaPathTextBox javaBrowseBtn - javaDownloadBtn javaDetectBtn javaTestBtn skipCompatibilityCheckbox From 93f19bda6eccf31642bfeeb323b435d9da678e63 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Thu, 21 Mar 2024 00:07:06 +0200 Subject: [PATCH 297/755] Update launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index 820e03ec9..84c52c386 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -129,6 +129,7 @@ bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, Q .arg("help.minecraft.net")); return true; } + // the following codes where copied from: https://github.com/PrismarineJS/prismarine-auth/pull/44 case 2148916236: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account requires proof of age to play. Please login to %1 to provide proof of age.") From ab7fc2e46cd1ebe3d29436c8939de57d681a24a2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 21 Mar 2024 00:42:04 +0200 Subject: [PATCH 298/755] Fixed archive java download Signed-off-by: Trial97 --- launcher/MMCZip.cpp | 4 ++++ launcher/java/download/ArchiveDownloadTask.cpp | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 265eb8b7a..33f5f88fc 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -510,6 +510,10 @@ bool ExportToZipTask::abort() void ExtractZipTask::executeTask() { + if (!m_input->isOpen() && !m_input->open(QuaZip::mdUnzip)) { + emitFailed(tr("Unable to open supplied zip file.")); + return; + } m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); }); connect(&m_zip_watcher, &QFutureWatcher::finished, this, &ExtractZipTask::finish); m_zip_watcher.setFuture(m_zip_future); diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index 34f60260e..d62d4d8e2 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -35,7 +35,7 @@ void ArchiveDownloadTask::executeTask() // JRE found ! download the zip setStatus(tr("Downloading Java")); - MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.toLocalFile()); + MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.fileName()); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); auto action = Net::Download::makeCached(m_url, entry); @@ -70,12 +70,16 @@ void ArchiveDownloadTask::extractJava(QString input) { setStatus(tr("Extracting java")); auto zip = std::make_shared(input); + if (!zip->open(QuaZip::mdUnzip)) { + emitFailed(tr("Unable to open supplied zip file.")); + return; + } auto files = zip->getFileNameList(); if (files.isEmpty()) { emitFailed("Empty archive"); return; } - auto zipTask = makeShared(input, m_final_path, files[0]); + auto zipTask = makeShared(zip, m_final_path, files[0]); auto progressStep = std::make_shared(); connect(zipTask.get(), &Task::finished, this, [this, progressStep] { From 37d348e60ee97995f7fdbc4c98c3a240992cfed2 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:16:28 +0100 Subject: [PATCH 299/755] fix: include libbz2.so.1.0 fixes portable installs not working on fedora anymore i did this only on portable builds and not system installs so that it doesn't create problems to people packaging them, as to fix system installs i would have to put it in bin/ Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9cb737df6..89e51ed99 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -496,7 +496,6 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt - cd ${{ env.INSTALL_DIR }} tar --owner root --group root -czf ../PrismLauncher.tar.gz * @@ -505,9 +504,12 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + + # workaround to make portable installs to work on fedora + mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib + cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib + for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - - cd ${{ env.INSTALL_PORTABLE_DIR }} tar -czf ../PrismLauncher-portable.tar.gz * From 1a6dfd04d66edb667f2ceb6646620b219dc17daf Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 21 Mar 2024 19:46:57 +0200 Subject: [PATCH 300/755] Made tar.gz parser Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/BaseVersionList.cpp | 9 + launcher/BaseVersionList.h | 1 + launcher/CMakeLists.txt | 2 + launcher/FileSystem.cpp | 3 + launcher/Untar.cpp | 262 ++++++++++++++++++ launcher/Untar.h | 46 +++ launcher/VersionProxyModel.cpp | 9 + launcher/VersionProxyModel.h | 2 +- .../java/download/ArchiveDownloadTask.cpp | 23 ++ launcher/meta/VersionList.cpp | 7 + .../minecraft/launch/VerifyJavaInstall.cpp | 1 - launcher/ui/java/InstallJavaDialog.cpp | 12 +- launcher/ui/java/VersionList.cpp | 7 + launcher/ui/widgets/JavaSettingsWidget.cpp | 2 +- 15 files changed, 381 insertions(+), 6 deletions(-) create mode 100644 launcher/Untar.cpp create mode 100644 launcher/Untar.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index b0929a0ae..f7fa3aedd 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -869,6 +869,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("icons", QDir("cache/icons").absolutePath()); m_metacache->addBase("meta", QDir("meta").absolutePath()); + m_metacache->addBase("java", QDir("cache/java").absolutePath()); m_metacache->Load(); qDebug() << "<> Cache initialized."; } diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index afee8388a..22077c962 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -78,6 +78,14 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const case TypeRole: return version->typeString(); + case JavaMajorRole: { + auto major = version->name(); + if (major.startsWith("java")) { + major = "Java " + major.mid(4); + } + return major; + } + default: return QVariant(); } @@ -112,5 +120,6 @@ QHash BaseVersionList::roleNames() const roles.insert(PathRole, "path"); roles.insert(JavaNameRole, "javaName"); roles.insert(CPUArchitectureRole, "architecture"); + roles.insert(JavaMajorRole, "javaMajor"); return roles; } diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index bc37e9e53..673d13562 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -49,6 +49,7 @@ class BaseVersionList : public QAbstractListModel { BranchRole, PathRole, JavaNameRole, + JavaMajorRole, CPUArchitectureRole, SortRole }; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index dd493682e..4f8ab598e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -24,6 +24,8 @@ set(CORE_SOURCES NullInstance.h MMCZip.h MMCZip.cpp + Untar.h + Untar.cpp StringUtils.h StringUtils.cpp QVariantUtils.h diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index f9be91a2a..8320dfe40 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -276,6 +276,9 @@ bool ensureFolderPathExists(const QFileInfo folderPath) { QDir dir; QString ensuredPath = folderPath.filePath(); + if (folderPath.exists()) + return true; + bool success = dir.mkpath(ensuredPath); return success; } diff --git a/launcher/Untar.cpp b/launcher/Untar.cpp new file mode 100644 index 000000000..521c504a6 --- /dev/null +++ b/launcher/Untar.cpp @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023-2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Untar.h" +#include +#include +#include +#include +#include +#include +#include +#include "FileSystem.h" + +// adaptation of the: +// - https://github.com/madler/zlib/blob/develop/contrib/untgz/untgz.c +// - https://en.wikipedia.org/wiki/Tar_(computing) +// - https://github.com/euroelessar/cutereader/blob/master/karchive/src/ktar.cpp + +#define BLOCKSIZE 512 +#define SHORTNAMESIZE 100 + +enum class TypeFlag : char { + Regular = '0', // regular file + ARegular = 0, // regular file + Link = '1', // link + Symlink = '2', // reserved + Character = '3', // character special + Block = '4', // block special + Directory = '5', // directory + FIFO = '6', // FIFO special + Contiguous = '7', // reserved + // Posix stuff + GlobalPosixHeader = 'g', + ExtendedPosixHeader = 'x', + // 'A'– 'Z' Vendor specific extensions(POSIX .1 - 1988) + // GNU + GNULongLink = 'K', /* long link name */ + GNULongName = 'L', /* long file name */ +}; + +struct Header { /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + TypeFlag typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[155]; /* 345 */ + /* 500 */ +}; + +union Buffer { + char buffer[BLOCKSIZE]; + struct Header header; +}; + +bool readLonglink(QIODevice* in, Buffer buffer, QByteArray& longlink) +{ + qint64 n = 0; + qint64 size = strtoll(buffer.header.size, NULL, 8); + size--; // ignore trailing null + if (errno == ERANGE) { + qCritical() << "The filename size can't be read"; + return false; + } + if (size < 0) { + qCritical() << "The filename size is negative"; + return false; + } + longlink.resize(size + (BLOCKSIZE - size % BLOCKSIZE)); // make the size divisible by BLOCKSIZE + for (qint64 offset = 0; offset < longlink.size(); offset += BLOCKSIZE) { + n = in->read(longlink.data() + offset, BLOCKSIZE); + if (n != BLOCKSIZE) { + qCritical() << "The expected blocksize was not respected for the name"; + return false; + } + } + longlink.truncate(qstrlen(longlink.constData())); + return true; +} + +bool Tar::extract(QIODevice* in, QString dst) +{ + Buffer buffer; + QString name, symlink, firstFolderName; + bool doNotReset = false; + while (true) { + auto n = in->read(buffer.buffer, BLOCKSIZE); + if (n != BLOCKSIZE) { // allways expect complete blocks + qCritical() << "The expected blocksize was not respected"; + return false; + } + if (buffer.header.name[0] == 0) { // end of archive + return true; + } + int mode = strtol(buffer.header.mode, NULL, 8) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions + if (errno == ERANGE) { + qCritical() << "The file mode can't be read"; + return false; + } + // there are names that are exactly 100 bytes long + // and neither longlink nor \0 terminated (bug:101472) + if (name.isEmpty()) { + name = QFile::decodeName(QByteArray(buffer.header.name, qstrnlen(buffer.header.name, 100))); + if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) { + name = name.mid(firstFolderName.size()); + } + } + if (symlink.isEmpty()) + symlink = QFile::decodeName(QByteArray(buffer.header.linkname, qstrnlen(buffer.header.linkname, 100))); + switch (buffer.header.typeflag) { + case TypeFlag::Regular: + /* fallthrough */ + case TypeFlag::ARegular: { + auto fileName = FS::PathCombine(dst, name); + if (!FS::ensureFilePathExists(fileName)) { + qCritical() << "Can't ensure the file path to exist: " << fileName; + return false; + } + QFile out(fileName); + if (!out.open(QFile::WriteOnly, QFile::Permission(mode))) { + qCritical() << "Can't open file:" << fileName; + return false; + } + qint64 size = strtoll(buffer.header.size, NULL, 8); + if (errno == ERANGE) { + qCritical() << "The file size can't be read"; + return false; + } + while (size > 0) { + QByteArray tmp(BLOCKSIZE, 0); + n = in->read(tmp.data(), BLOCKSIZE); + if (n != BLOCKSIZE) { + qCritical() << "The expected blocksize was not respected when reading file"; + return false; + } + tmp.truncate(qMin(BLOCKSIZE, size)); + out.write(tmp); + size -= BLOCKSIZE; + } + QFile::setPermissions(fileName, QFile::Permissions(mode)); + break; + } + case TypeFlag::Directory: { + if (firstFolderName.isEmpty()) { + firstFolderName = name; + break; + } + auto folderPath = FS::PathCombine(dst, name); + if (!FS::ensureFolderPathExists(folderPath)) { + qCritical() << "Can't ensure that folder exists: " << folderPath; + return false; + } + break; + } + case TypeFlag::GNULongLink: { + doNotReset = true; + QByteArray longlink; + if (readLonglink(in, buffer, longlink)) { + symlink = QFile::decodeName(longlink.constData()); + } else { + qCritical() << "Failed to read long link"; + return false; + } + break; + } + case TypeFlag::GNULongName: { + doNotReset = true; + QByteArray longlink; + if (readLonglink(in, buffer, longlink)) { + name = QFile::decodeName(longlink.constData()); + } else { + qCritical() << "Failed to read long name"; + return false; + } + break; + } + case TypeFlag::Link: + /* fallthrough */ + case TypeFlag::Symlink: { + auto fileName = FS::PathCombine(dst, name); + if (!FS::create_link(FS::PathCombine(QFileInfo(fileName).path(), symlink), fileName)()) { // do not use symlinks + qCritical() << "Can't create link for:" << fileName << " to:" << FS::PathCombine(QFileInfo(fileName).path(), symlink); + return false; + } + FS::ensureFilePathExists(fileName); + QFile::setPermissions(fileName, QFile::Permissions(mode)); + break; + } + case TypeFlag::Character: + /* fallthrough */ + case TypeFlag::Block: + /* fallthrough */ + case TypeFlag::FIFO: + /* fallthrough */ + case TypeFlag::Contiguous: + /* fallthrough */ + case TypeFlag::GlobalPosixHeader: + /* fallthrough */ + case TypeFlag::ExtendedPosixHeader: + /* fallthrough */ + default: + break; + } + if (!doNotReset) { + name.truncate(0); + symlink.truncate(0); + } + doNotReset = false; + } + return true; +} + +bool GZTar::extract(QString src, QString dst) +{ + QuaGzipFile a(src); + if (!a.open(QIODevice::ReadOnly)) { + qCritical() << "Can't open tar file:" << src; + return false; + } + return Tar::extract(&a, dst); +} \ No newline at end of file diff --git a/launcher/Untar.h b/launcher/Untar.h new file mode 100644 index 000000000..54010e085 --- /dev/null +++ b/launcher/Untar.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023-2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +// this is a hack used for the java downloader (feel free to remove it in favor of a library) +// both extract functions will extract the first folder inside dest(disregarding the prefix) +namespace Tar { +bool extract(QIODevice* in, QString dst); +}; + +namespace GZTar { +bool extract(QString src, QString dst); +}; \ No newline at end of file diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 070e952a4..62cf4c221 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -120,6 +120,8 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("Path"); case JavaName: return tr("Java Name"); + case JavaMajor: + return tr("Major"); case Time: return tr("Released"); } @@ -139,6 +141,8 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, return tr("Filesystem path to this version"); case JavaName: return tr("The alternative name of the java version"); + case JavaMajor: + return tr("The java major version"); case Time: return tr("Release date of this version"); } @@ -175,6 +179,8 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const return sourceModel()->data(parentIndex, BaseVersionList::PathRole); case JavaName: return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole); + case JavaMajor: + return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole); case Time: return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate(); default: @@ -323,6 +329,9 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) if (roles.contains(BaseVersionList::JavaNameRole)) { m_columns.push_back(JavaName); } + if (roles.contains(BaseVersionList::JavaMajorRole)) { + m_columns.push_back(JavaMajor); + } if (roles.contains(Meta::VersionList::TimeRole)) { m_columns.push_back(Time); } diff --git a/launcher/VersionProxyModel.h b/launcher/VersionProxyModel.h index cb55b7f14..8aea25795 100644 --- a/launcher/VersionProxyModel.h +++ b/launcher/VersionProxyModel.h @@ -9,7 +9,7 @@ class VersionFilterModel; class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: - enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName }; + enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor }; using FilterMap = QHash>; public: diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index d62d4d8e2..358860610 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -21,6 +21,7 @@ #include "MMCZip.h" #include "Application.h" +#include "Untar.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" #include "tasks/Task.h" @@ -69,6 +70,28 @@ void ArchiveDownloadTask::executeTask() void ArchiveDownloadTask::extractJava(QString input) { setStatus(tr("Extracting java")); + if (input.endsWith("tar")) { + setStatus(tr("Extracting java(the progress will not be reported for tar)")); + QFile in(input); + if (!in.open(QFile::ReadOnly)) { + emitFailed(tr("Unable to open supplied tar file.")); + return; + } + if (!Tar::extract(&in, QDir(m_final_path).absolutePath())) { + emitFailed(tr("Unable to extract supplied tar file.")); + return; + } + emitSucceeded(); + return; + } else if (input.endsWith("tar.gz") || input.endsWith("taz") || input.endsWith("tgz")) { + setStatus(tr("Extracting java(the progress will not be reported for tar)")); + if (!GZTar::extract(input, QDir(m_final_path).absolutePath())) { + emitFailed(tr("Unable to extract supplied tar file.")); + return; + } + emitSucceeded(); + return; + } auto zip = std::make_shared(input); if (!zip->open(QuaZip::mdUnzip)) { emitFailed(tr("Unable to open supplied zip file.")); diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index 76b914b6a..66412d6a4 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -92,6 +92,13 @@ QVariant VersionList::data(const QModelIndex& index, int role) const return QVariant::fromValue(version); case RecommendedRole: return version->isRecommended(); + case JavaMajorRole: { + auto major = version->version(); + if (major.startsWith("java")) { + major = "Java " + major.mid(4); + } + return major; + } // FIXME: this should be determined in whatever view/proxy is used... // case LatestRole: return version == getLatestStable(); default: diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index f26b850eb..5f8b35b02 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -54,7 +54,6 @@ void VerifyJavaInstall::executeTask() auto javaArchitecture = settings->get("JavaArchitecture").toString(); auto maxMemAlloc = settings->get("MaxMemAlloc").toInt(); - emit logLine(tr("Java architecture is x%1.").arg(javaArchitecture), MessageLevel::Info); if (javaArchitecture == "32" && maxMemAlloc > 2048) { emit logLine(tr("Max memory allocation exceeds the supported value.\n" "The selected java is 32-bit and doesn't support more than 2048MiB of RAM.\n" diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp index 874fc9133..78de3e20a 100644 --- a/launcher/ui/java/InstallJavaDialog.cpp +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -19,16 +19,19 @@ #include "InstallJavaDialog.h" #include +#include #include #include #include #include "Application.h" +#include "BaseVersionList.h" #include "FileSystem.h" #include "java/download/ArchiveDownloadTask.h" #include "java/download/ManifestDownloadTask.h" #include "meta/Index.h" #include "meta/VersionList.h" +#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/java/VersionList.h" #include "ui/widgets/PageContainer.h" @@ -71,8 +74,7 @@ class InstallJavaPage : public QWidget, public BasePage { //! loads the list if needed. void initialize(Meta::VersionList::Ptr vlist) { - vlist->setProvidedRoles({ BaseVersionList::VersionRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); - vlist->sort(1); + vlist->setProvidedRoles({ BaseVersionList::JavaMajorRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole }); majorVersionSelect->initialize(vlist.get()); } @@ -219,7 +221,11 @@ void InstallDialog::done(int result) break; } auto deletePath = [final_path] { FS::deletePath(final_path); }; - connect(task.get(), &Task::failed, this, deletePath); + connect(task.get(), &Task::failed, this, [this, &deletePath](QString reason) { + QString error = QString("Java download failed: %1").arg(reason); + CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); + deletePath(); + }); connect(task.get(), &Task::aborted, this, deletePath); ProgressDialog pg(this); pg.setSkipButton(true, tr("Abort")); diff --git a/launcher/ui/java/VersionList.cpp b/launcher/ui/java/VersionList.cpp index 78448ddaf..5e385604d 100644 --- a/launcher/ui/java/VersionList.cpp +++ b/launcher/ui/java/VersionList.cpp @@ -78,6 +78,13 @@ QVariant VersionList::data(const QModelIndex& index, int role) const return false; // do not recommend any version case JavaNameRole: return version->name(); + case JavaMajorRole: { + auto major = version->version.toString(); + if (major.startsWith("java")) { + major = "Java " + major.mid(4); + } + return major; + } case TypeRole: return version->packageType; case Meta::VersionList::TimeRole: diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index e2e498067..1645f2b5f 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -180,7 +180,7 @@ void JavaSettingsWidget::initialize() tr("%1 can automatically download the correct Java version for each version of Minecraft..\n" "Do you want to enable Java auto-download?\n") .arg(BuildConfig.LAUNCHER_DISPLAYNAME), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) + QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ->exec(); if (button == QMessageBox::Yes) { m_autodetectJavaCheckBox->setChecked(true); From 3046822272ee97a831667db1c8a222eb304c259e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 21 Mar 2024 19:53:52 +0200 Subject: [PATCH 301/755] Fixed buid Signed-off-by: Trial97 --- launcher/Untar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/Untar.cpp b/launcher/Untar.cpp index 521c504a6..b2f05038c 100644 --- a/launcher/Untar.cpp +++ b/launcher/Untar.cpp @@ -158,10 +158,11 @@ bool Tar::extract(QIODevice* in, QString dst) return false; } QFile out(fileName); - if (!out.open(QFile::WriteOnly, QFile::Permission(mode))) { + if (!out.open(QFile::WriteOnly)) { qCritical() << "Can't open file:" << fileName; return false; } + out.setPermissions(QFile::Permissions(mode)); qint64 size = strtoll(buffer.header.size, NULL, 8); if (errno == ERANGE) { qCritical() << "The file size can't be read"; @@ -178,7 +179,6 @@ bool Tar::extract(QIODevice* in, QString dst) out.write(tmp); size -= BLOCKSIZE; } - QFile::setPermissions(fileName, QFile::Permissions(mode)); break; } case TypeFlag::Directory: { From a7029a9e53cd3d9a2371cd26c257beb898251667 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 21 Mar 2024 20:01:40 +0200 Subject: [PATCH 302/755] Fixed qMin Signed-off-by: Trial97 --- launcher/Untar.cpp | 2 +- launcher/Untar.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/Untar.cpp b/launcher/Untar.cpp index b2f05038c..bae6c5d17 100644 --- a/launcher/Untar.cpp +++ b/launcher/Untar.cpp @@ -175,7 +175,7 @@ bool Tar::extract(QIODevice* in, QString dst) qCritical() << "The expected blocksize was not respected when reading file"; return false; } - tmp.truncate(qMin(BLOCKSIZE, size)); + tmp.truncate(qMin(qint64(BLOCKSIZE), size)); out.write(tmp); size -= BLOCKSIZE; } diff --git a/launcher/Untar.h b/launcher/Untar.h index 54010e085..50e3a16e3 100644 --- a/launcher/Untar.h +++ b/launcher/Untar.h @@ -39,8 +39,8 @@ // both extract functions will extract the first folder inside dest(disregarding the prefix) namespace Tar { bool extract(QIODevice* in, QString dst); -}; +} namespace GZTar { bool extract(QString src, QString dst); -}; \ No newline at end of file +} \ No newline at end of file From 0b87e4c03b55c96966255eb8440b0c4e7b182c63 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 22 Mar 2024 12:29:15 +0200 Subject: [PATCH 303/755] Fiexed codeql warning Signed-off-by: Trial97 --- launcher/Untar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Untar.cpp b/launcher/Untar.cpp index bae6c5d17..1d11dfd60 100644 --- a/launcher/Untar.cpp +++ b/launcher/Untar.cpp @@ -94,7 +94,7 @@ union Buffer { struct Header header; }; -bool readLonglink(QIODevice* in, Buffer buffer, QByteArray& longlink) +bool readLonglink(QIODevice* in, Buffer& buffer, QByteArray& longlink) { qint64 n = 0; qint64 size = strtoll(buffer.header.size, NULL, 8); From c0754b80a2b8612ce2874888c0f40b41b2eac765 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 22 Mar 2024 22:17:58 +0200 Subject: [PATCH 304/755] Add resize to last column Signed-off-by: Trial97 --- launcher/ui/pages/global/JavaPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 6d8b95c24..fda4f8705 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -65,6 +65,7 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) if (BuildConfig.JAVA_DOWNLOADER_ENABLED) { ui->managedJavaList->initialize(new JavaInstallList(this, true)); + ui->managedJavaList->setResizeOn(2); ui->managedJavaList->selectCurrent(); ui->managedJavaList->setEmptyString(tr("No managed java versions are installed")); ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed java list!")); From 3a833b659a581274f15f609be12bfaa1a9efd1ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 24 Mar 2024 00:19:57 +0000 Subject: [PATCH 305/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/9af9c1c87ed3e3ed271934cb896e0cdd33dae212' (2024-03-15) → 'github:nixos/nixpkgs/20bc93ca7b2158ebc99b8cef987a2173a81cde35' (2024-03-23) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/5df5a70ad7575f6601d91f0efec95dd9bc619431' (2024-02-15) → 'github:cachix/pre-commit-hooks.nix/e611897ddfdde3ed3eaac4758635d7177ff78673' (2024-03-20) • Updated input 'pre-commit-hooks/flake-utils': 'github:numtide/flake-utils/4022d587cbbfd70fe950c1e2083a02621806a725' (2023-12-04) → 'github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a' (2024-03-11) • Updated input 'pre-commit-hooks/gitignore': 'github:hercules-ci/gitignore.nix/43e1aa1308018f37118e34d3a9cb4f5e75dc11d5' (2023-12-29) → 'github:hercules-ci/gitignore.nix/637db329424fd7e46cf4185293b9cc8c88c95394' (2024-02-28) --- flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flake.lock b/flake.lock index ab1026c08..492f7855e 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -62,11 +62,11 @@ ] }, "locked": { - "lastModified": 1703887061, - "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1710534455, - "narHash": "sha256-huQT4Xs0y4EeFKn2BTBVYgEwJSv8SDlm82uWgMnCMmI=", + "lastModified": 1711200738, + "narHash": "sha256-dkJmk/ET/tRV4007O6kU101UEg1svUwiyk/zEEX9Tdg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9af9c1c87ed3e3ed271934cb896e0cdd33dae212", + "rev": "20bc93ca7b2158ebc99b8cef987a2173a81cde35", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1708018599, - "narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=", + "lastModified": 1710923068, + "narHash": "sha256-6hOpUiuxuwpXXc/xfJsBUJeqqgGI+JMJuLo45aG3cKc=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431", + "rev": "e611897ddfdde3ed3eaac4758635d7177ff78673", "type": "github" }, "original": { From 765bdf67c95ffee27d4b8342982b3e7bcc382c4b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 27 Mar 2024 00:56:12 +0200 Subject: [PATCH 306/755] Changed the trait name Signed-off-by: Trial97 --- launcher/minecraft/MinecraftInstance.cpp | 2 +- .../org/prismlauncher/launcher/impl/StandardLauncher.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4a72ae1e4..1de822b7f 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -665,7 +665,7 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine } if (serverToJoin && !serverToJoin->address.isEmpty()) { - if (profile->hasTrait("quickPlay")) { + if (profile->hasTrait("feature:is_quick_play_multiplayer")) { args_pattern += " --quickPlayMultiplayer " + serverToJoin->address + ':' + QString::number(serverToJoin->port); } else { args_pattern += " --server " + serverToJoin->address; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 70ee015bd..49e5d518f 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -68,7 +68,7 @@ public final class StandardLauncher extends AbstractLauncher { super(params); List traits = params.getList("traits", Collections.emptyList()); - quickPlaySupported = traits.contains("quickPlay"); + quickPlaySupported = traits.contains("feature:is_quick_play_multiplayer"); } @Override From 93f6315b16de3b2170d0263d7befcac5dcfaa01d Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Wed, 27 Mar 2024 19:34:04 +0200 Subject: [PATCH 307/755] Update CMakeLists.txt Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- CMakeLists.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40ccff595..f4298aedb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,7 +220,17 @@ set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") # Java downloader -option(ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ON) +set(ENABLE_JAVA_DOWNLOADER_DEFAULT ON) + +# Although we recommend enabling this, we cannot guarantee binary compatibility on +# differing Linux/BSD/etc distributions. Downstream packagers should be explicitly opt-ing into this +# feature if they know it will work with their distribution. +if(UNIX AND NOT APPLE) + set(ENABLE_JAVA_DOWNLOADER_DEFAULT OFF) +endif() + +# Java downloader +option(ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ${ENABLE_JAVA_DOWNLOADER_DEFAULT}) # Native libraries if(UNIX AND APPLE) From 45028ddc6142b019d28dadd31b8c87433adea312 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Mar 2024 14:58:05 +0200 Subject: [PATCH 308/755] Added java downloader to CI build Signed-off-by: Trial97 --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9cb737df6..500aca5cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -274,23 +274,23 @@ jobs: - name: Configure CMake (macOS) if: runner.os == 'macOS' && matrix.qt_ver == 6 run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja - name: Configure CMake (macOS-Legacy) if: runner.os == 'macOS' && matrix.qt_ver == 5 run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja - name: Configure CMake (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' shell: msys2 {0} run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -305,7 +305,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja ## # BUILD From 81874f22f74e3d254e16c3054abb28881b50d01a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 29 Mar 2024 00:10:59 +0200 Subject: [PATCH 309/755] Fixed tar extract Signed-off-by: Trial97 --- launcher/Untar.cpp | 96 +++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/launcher/Untar.cpp b/launcher/Untar.cpp index 1d11dfd60..f1963e7aa 100644 --- a/launcher/Untar.cpp +++ b/launcher/Untar.cpp @@ -33,13 +33,11 @@ * limitations under the License. */ #include "Untar.h" -#include -#include #include #include +#include #include #include -#include #include "FileSystem.h" // adaptation of the: @@ -69,40 +67,30 @@ enum class TypeFlag : char { GNULongName = 'L', /* long file name */ }; -struct Header { /* byte offset */ - char name[100]; /* 0 */ - char mode[8]; /* 100 */ - char uid[8]; /* 108 */ - char gid[8]; /* 116 */ - char size[12]; /* 124 */ - char mtime[12]; /* 136 */ - char chksum[8]; /* 148 */ - TypeFlag typeflag; /* 156 */ - char linkname[100]; /* 157 */ - char magic[6]; /* 257 */ - char version[2]; /* 263 */ - char uname[32]; /* 265 */ - char gname[32]; /* 297 */ - char devmajor[8]; /* 329 */ - char devminor[8]; /* 337 */ - char prefix[155]; /* 345 */ - /* 500 */ -}; +// struct Header { /* byte offset */ +// char name[100]; /* 0 */ +// char mode[8]; /* 100 */ +// char uid[8]; /* 108 */ +// char gid[8]; /* 116 */ +// char size[12]; /* 124 */ +// char mtime[12]; /* 136 */ +// char chksum[8]; /* 148 */ +// TypeFlag typeflag; /* 156 */ +// char linkname[100]; /* 157 */ +// char magic[6]; /* 257 */ +// char version[2]; /* 263 */ +// char uname[32]; /* 265 */ +// char gname[32]; /* 297 */ +// char devmajor[8]; /* 329 */ +// char devminor[8]; /* 337 */ +// char prefix[155]; /* 345 */ +// /* 500 */ +// }; -union Buffer { - char buffer[BLOCKSIZE]; - struct Header header; -}; - -bool readLonglink(QIODevice* in, Buffer& buffer, QByteArray& longlink) +bool readLonglink(QIODevice* in, qint64 size, QByteArray& longlink) { qint64 n = 0; - qint64 size = strtoll(buffer.header.size, NULL, 8); size--; // ignore trailing null - if (errno == ERANGE) { - qCritical() << "The filename size can't be read"; - return false; - } if (size < 0) { qCritical() << "The filename size is negative"; return false; @@ -119,36 +107,51 @@ bool readLonglink(QIODevice* in, Buffer& buffer, QByteArray& longlink) return true; } +int getOctal(char* buffer, int maxlenght, bool* ok) +{ + return QByteArray(buffer, qstrnlen(buffer, maxlenght)).toInt(ok, 8); +} + +QString decodeName(char* name) +{ + return QFile::decodeName(QByteArray(name, qstrnlen(name, 100))); +} bool Tar::extract(QIODevice* in, QString dst) { - Buffer buffer; + char buffer[BLOCKSIZE]; QString name, symlink, firstFolderName; - bool doNotReset = false; + bool doNotReset = false, ok; while (true) { - auto n = in->read(buffer.buffer, BLOCKSIZE); + auto n = in->read(buffer, BLOCKSIZE); if (n != BLOCKSIZE) { // allways expect complete blocks qCritical() << "The expected blocksize was not respected"; return false; } - if (buffer.header.name[0] == 0) { // end of archive + if (buffer[0] == 0) { // end of archive return true; } - int mode = strtol(buffer.header.mode, NULL, 8) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions - if (errno == ERANGE) { + int mode = getOctal(buffer + 100, 8, &ok) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions + if (!ok) { qCritical() << "The file mode can't be read"; return false; } // there are names that are exactly 100 bytes long // and neither longlink nor \0 terminated (bug:101472) + if (name.isEmpty()) { - name = QFile::decodeName(QByteArray(buffer.header.name, qstrnlen(buffer.header.name, 100))); + name = decodeName(buffer); if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) { name = name.mid(firstFolderName.size()); } } if (symlink.isEmpty()) - symlink = QFile::decodeName(QByteArray(buffer.header.linkname, qstrnlen(buffer.header.linkname, 100))); - switch (buffer.header.typeflag) { + symlink = decodeName(buffer); + qint64 size = getOctal(buffer + 124, 12, &ok); + if (!ok) { + qCritical() << "The file size can't be read"; + return false; + } + switch (TypeFlag(buffer[156])) { case TypeFlag::Regular: /* fallthrough */ case TypeFlag::ARegular: { @@ -163,11 +166,6 @@ bool Tar::extract(QIODevice* in, QString dst) return false; } out.setPermissions(QFile::Permissions(mode)); - qint64 size = strtoll(buffer.header.size, NULL, 8); - if (errno == ERANGE) { - qCritical() << "The file size can't be read"; - return false; - } while (size > 0) { QByteArray tmp(BLOCKSIZE, 0); n = in->read(tmp.data(), BLOCKSIZE); @@ -196,7 +194,7 @@ bool Tar::extract(QIODevice* in, QString dst) case TypeFlag::GNULongLink: { doNotReset = true; QByteArray longlink; - if (readLonglink(in, buffer, longlink)) { + if (readLonglink(in, size, longlink)) { symlink = QFile::decodeName(longlink.constData()); } else { qCritical() << "Failed to read long link"; @@ -207,7 +205,7 @@ bool Tar::extract(QIODevice* in, QString dst) case TypeFlag::GNULongName: { doNotReset = true; QByteArray longlink; - if (readLonglink(in, buffer, longlink)) { + if (readLonglink(in, size, longlink)) { name = QFile::decodeName(longlink.constData()); } else { qCritical() << "Failed to read long name"; From e6a9badb738caee498ebe74c90714fe01af07f0b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 30 Mar 2024 10:42:48 +0000 Subject: [PATCH 310/755] chore(deps): update korthout/backport-action action to v2.5.0 --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 5df88fe86..60bd86eec 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -25,7 +25,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Create backport PRs - uses: korthout/backport-action@v2.4.1 + uses: korthout/backport-action@v2.5.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- From f1351a143e70937242e37e633717640c54ad604e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 31 Mar 2024 00:20:10 +0000 Subject: [PATCH 311/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/20bc93ca7b2158ebc99b8cef987a2173a81cde35' (2024-03-23) → 'github:nixos/nixpkgs/807c549feabce7eddbf259dbdcec9e0600a0660d' (2024-03-29) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/e611897ddfdde3ed3eaac4758635d7177ff78673' (2024-03-20) → 'github:cachix/pre-commit-hooks.nix/c11e43aed6f17336c25cd120eac886b96c455731' (2024-03-30) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 492f7855e..dcc028b2d 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711200738, - "narHash": "sha256-dkJmk/ET/tRV4007O6kU101UEg1svUwiyk/zEEX9Tdg=", + "lastModified": 1711715736, + "narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "20bc93ca7b2158ebc99b8cef987a2173a81cde35", + "rev": "807c549feabce7eddbf259dbdcec9e0600a0660d", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1710923068, - "narHash": "sha256-6hOpUiuxuwpXXc/xfJsBUJeqqgGI+JMJuLo45aG3cKc=", + "lastModified": 1711760932, + "narHash": "sha256-DqUTQ2iAAqSDwMhKBqvi24v0Oc7pD3LCK/0FCG//TdA=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e611897ddfdde3ed3eaac4758635d7177ff78673", + "rev": "c11e43aed6f17336c25cd120eac886b96c455731", "type": "github" }, "original": { From 71998cb7d99504d5a6dc16f0c2af606f10c33a94 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 1 Apr 2024 15:22:09 +0100 Subject: [PATCH 312/755] Rework filter widget Signed-off-by: TheKodeToad --- launcher/ui/widgets/ModFilterWidget.cpp | 198 ++++++------- launcher/ui/widgets/ModFilterWidget.h | 10 +- launcher/ui/widgets/ModFilterWidget.ui | 374 ++++++++++-------------- 3 files changed, 250 insertions(+), 332 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index f419537c7..b44a573d8 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -46,9 +46,9 @@ #include "Application.h" #include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, bool extended, QWidget* parent) { - return unique_qobject_ptr(new ModFilterWidget(instance, extendedSupport, parent)); + return unique_qobject_ptr(new ModFilterWidget(instance, extended, parent)); } class VersionBasicModel : public QIdentityProxyModel { @@ -65,55 +65,50 @@ class VersionBasicModel : public QIdentityProxyModel { } }; -ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWidget* parent) : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) { ui->setupUi(this); m_versions_proxy = new VersionProxyModel(this); - m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release")); auto proxy = new VersionBasicModel(this); proxy->setSourceModel(m_versions_proxy); - if (!extendedSupport) { - ui->versionsSimpleCb->setModel(proxy); - ui->versionsCb->hide(); - ui->snapshotsCb->hide(); - ui->envBox->hide(); + if (extended) { + ui->versions->setSourceModel(proxy); + ui->versions->setSeparator(", "); + ui->version->hide(); } else { - ui->versionsCb->setSourceModel(proxy); - ui->versionsCb->setSeparator("| "); - ui->versionsSimpleCb->hide(); + ui->version->setModel(proxy); + ui->versions->hide(); + ui->showAllVersions->hide(); + ui->environmentGroup->hide(); } - ui->versionsCb->setStyleSheet("combobox-popup: 0;"); - ui->versionsSimpleCb->setStyleSheet("combobox-popup: 0;"); - connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); - connect(ui->versionsCb, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); - connect(ui->versionsSimpleCb, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); + ui->versions->setStyleSheet("combobox-popup: 0;"); + ui->version->setStyleSheet("combobox-popup: 0;"); + connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); + connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); - connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->fabricCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->quiltCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->liteLoaderCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->cauldronCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - ui->liteLoaderCb->hide(); - ui->cauldronCb->hide(); + connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->serverEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); - connect(ui->clientEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + if (extended) { + connect(ui->clientSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + connect(ui->serverSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + } - connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); - - connect(ui->releaseCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - connect(ui->betaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - - connect(ui->categoriesList, &QListWidget::itemClicked, this, &ModFilterWidget::onCategoryClicked); + connect(ui->hideInstalled, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); setHidden(true); loadVersionList(); @@ -147,8 +142,8 @@ void ModFilterWidget::loadVersionList() auto task = m_version_list->getLoadTask(); connect(task.get(), &Task::failed, [this] { - ui->versionsCb->setEnabled(false); - ui->snapshotsCb->setEnabled(false); + ui->versions->setEnabled(false); + ui->showAllVersions->setEnabled(false); }); connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); @@ -165,35 +160,35 @@ void ModFilterWidget::loadVersionList() void ModFilterWidget::prepareBasicFilter() { m_filter->hideInstalled = false; - m_filter->side = ""; // or "both"t + m_filter->side = ""; // or "both" auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); - ui->neoForgeCb->setChecked(loaders & ModPlatform::NeoForge); - ui->forgeCb->setChecked(loaders & ModPlatform::Forge); - ui->fabricCb->setChecked(loaders & ModPlatform::Fabric); - ui->quiltCb->setChecked(loaders & ModPlatform::Quilt); - ui->liteLoaderCb->setChecked(loaders & ModPlatform::LiteLoader); - ui->cauldronCb->setChecked(loaders & ModPlatform::Cauldron); + ui->neoForge->setChecked(loaders & ModPlatform::NeoForge); + ui->forge->setChecked(loaders & ModPlatform::Forge); + ui->fabric->setChecked(loaders & ModPlatform::Fabric); + ui->quilt->setChecked(loaders & ModPlatform::Quilt); m_filter->loaders = loaders; auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); - m_filter->versions.push_front(Version{ def }); - ui->versionsCb->setCheckedItems({ def }); - ui->versionsSimpleCb->setCurrentIndex(ui->versionsSimpleCb->findText(def)); + m_filter->versions.emplace_front(def); + ui->versions->setCheckedItems({ def }); + ui->version->setCurrentIndex(ui->version->findText(def)); } -void ModFilterWidget::onIncludeSnapshotsChanged() +void ModFilterWidget::onShowAllVersionsChanged() { - QString filter = "(release)"; - if (ui->snapshotsCb->isChecked()) - filter += "|(snapshot)"; - m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); + if (ui->showAllVersions->isChecked()) + m_versions_proxy->clearFilters(); + else + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release")); } void ModFilterWidget::onVersionFilterChanged(int) { - auto versions = ui->versionsCb->checkedItems(); + auto versions = ui->versions->checkedItems(); m_filter->versions.clear(); - for (auto version : versions) - m_filter->versions.push_back(version); + + for (const QString& version : versions) + m_filter->versions.emplace_back(version); + m_filter_changed = true; emit filterChanged(); } @@ -201,18 +196,14 @@ void ModFilterWidget::onVersionFilterChanged(int) void ModFilterWidget::onLoadersFilterChanged() { ModPlatform::ModLoaderTypes loaders; - if (ui->neoForgeCb->isChecked()) + if (ui->neoForge->isChecked()) loaders |= ModPlatform::NeoForge; - if (ui->forgeCb->isChecked()) + if (ui->forge->isChecked()) loaders |= ModPlatform::Forge; - if (ui->fabricCb->isChecked()) + if (ui->fabric->isChecked()) loaders |= ModPlatform::Fabric; - if (ui->quiltCb->isChecked()) + if (ui->quilt->isChecked()) loaders |= ModPlatform::Quilt; - if (ui->cauldronCb->isChecked()) - loaders |= ModPlatform::Cauldron; - if (ui->liteLoaderCb->isChecked()) - loaders |= ModPlatform::LiteLoader; m_filter_changed = loaders != m_filter->loaders; m_filter->loaders = loaders; if (m_filter_changed) @@ -224,14 +215,17 @@ void ModFilterWidget::onLoadersFilterChanged() void ModFilterWidget::onSideFilterChanged() { QString side; - if (ui->serverEnv->isChecked()) - side = "server"; - if (ui->clientEnv->isChecked()) { - if (side.isEmpty()) + + if (ui->clientSide->isChecked() != ui->serverSide->isChecked()) { + if (ui->clientSide->isChecked()) side = "client"; else - side = ""; // or both + side = "server"; + } else { + // both are checked or none are checked; in either case no filtering will happen + side = ""; } + m_filter_changed = side != m_filter->side; m_filter->side = side; if (m_filter_changed) @@ -242,7 +236,7 @@ void ModFilterWidget::onSideFilterChanged() void ModFilterWidget::onHideInstalledFilterChanged() { - auto hide = ui->hide_installed->isChecked(); + auto hide = ui->hideInstalled->isChecked(); m_filter_changed = hide != m_filter->hideInstalled; m_filter->hideInstalled = hide; if (m_filter_changed) @@ -251,60 +245,36 @@ void ModFilterWidget::onHideInstalledFilterChanged() emit filterUnchanged(); } -void ModFilterWidget::onReleaseFilterChanged() -{ - std::list releases; - if (ui->releaseCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Release)); - if (ui->betaCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Beta)); - if (ui->alphaCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Alpha)); - if (ui->unknownCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Unknown)); - m_filter_changed = releases != m_filter->releases; - m_filter->releases = releases; - if (m_filter_changed) - emit filterChanged(); - else - emit filterUnchanged(); -} - -void ModFilterWidget::onVersionFilterTextChanged(QString version) +void ModFilterWidget::onVersionFilterTextChanged(const QString& version) { m_filter->versions.clear(); - m_filter->versions.push_front(version); + m_filter->versions.emplace_back(version); m_filter_changed = true; emit filterChanged(); } -void ModFilterWidget::setCategories(QList categories) +void ModFilterWidget::setCategories(const QList& categories) { - ui->categoriesList->clear(); m_categories = categories; - for (auto cat : categories) { - auto item = new QListWidgetItem(cat.name, ui->categoriesList); - item->setFlags(item->flags() & (~Qt::ItemIsUserCheckable)); - item->setCheckState(Qt::Unchecked); - ui->categoriesList->addItem(item); + + delete ui->categoryGroup->layout(); + auto layout = new QVBoxLayout(ui->categoryGroup); + + for (const auto& category : categories) { + auto checkbox = new QCheckBox(category.name); + layout->addWidget(checkbox); + + const QString id = category.id; + connect(checkbox, &QCheckBox::toggled, this, [this, id](bool checked) { + if (checked) + m_filter->categoryIds.append(id); + else + m_filter->categoryIds.removeOne(id); + + m_filter_changed = true; + emit filterChanged(); + }); } } -void ModFilterWidget::onCategoryClicked(QListWidgetItem* itm) -{ - if (itm) - itm->setCheckState(itm->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); - m_filter->categoryIds.clear(); - for (auto i = 0; i < ui->categoriesList->count(); i++) { - auto item = ui->categoriesList->item(i); - if (item->checkState() == Qt::Checked) { - auto c = std::find_if(m_categories.cbegin(), m_categories.cend(), [item](auto v) { return v.name == item->text(); }); - if (c != m_categories.cend()) - m_filter->categoryIds << c->id; - } - } - m_filter_changed = true; - emit filterChanged(); -}; - #include "ModFilterWidget.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index dd98d9afa..4358dd91d 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -73,7 +73,7 @@ class ModFilterWidget : public QTabWidget { bool operator!=(const Filter& other) const { return !(*this == other); } }; - static unique_qobject_ptr create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); + static unique_qobject_ptr create(MinecraftInstance* instance, bool extended, QWidget* parent = nullptr); virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; @@ -84,7 +84,7 @@ class ModFilterWidget : public QTabWidget { void filterUnchanged(); public slots: - void setCategories(QList); + void setCategories(const QList&); private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); @@ -94,13 +94,11 @@ class ModFilterWidget : public QTabWidget { private slots: void onVersionFilterChanged(int); - void onVersionFilterTextChanged(QString version); - void onReleaseFilterChanged(); + void onVersionFilterTextChanged(const QString& version); void onLoadersFilterChanged(); void onSideFilterChanged(); void onHideInstalledFilterChanged(); - void onIncludeSnapshotsChanged(); - void onCategoryClicked(QListWidgetItem* item); + void onShowAllVersionsChanged(); private: Ui::ModFilterWidget* ui; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 56cd33fa6..27f3b8610 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -1,237 +1,187 @@ ModFilterWidget - + 0 0 - 460 - 132 + 553 + 604 - - - 0 - 0 - + + Form - - 0 - - - - Minecraft versions - - - - - - Include Snapshots + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 553 + 604 + - - - - - - - 0 - 0 - - - - 5 - - - QComboBox::AdjustToContentsOnFirstShow - - - - - - - - - - - Loaders - - - - QLayout::SetMinimumSize - - - - - Fabric - - - - - - - NeoForge - - - - - - - Forge - - - - - - - false - - - Cauldron - - - - - - - Quilt - - - - - - - false - - - LiteLoader - - - - - - - - Release type - - - - - - Release - - - - - - - Beta - - - - - - - Alpha - - - - - - - Unknown - - - - - - - - Categories - - - - - - QAbstractScrollArea::AdjustToContents - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::ScrollPerItem - - - QListView::LeftToRight - - - true - - - QListView::ListMode - - - 0 - - - - - - - - Others - - - - - - Environments - - - - QLayout::SetDefaultConstraint - + - - - Client + + + Category + + + false + + + false - + + + Loader + + + false + + + false + + + + + + NeoForge + + + + + + + Forge + + + + + + + Fabric + + + + + + + Quilt + + + + + + + + + + Version + + + false + + + false + + + + + + + + + + + + Show all versions + + + + + + + + + + Environment + + + false + + + false + + + + + + Client + + + + + + + Server + + + + + + + + - Server + Hide installed items + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - - Instaled status - - - - - - Hide already installed - - - - - - - - + + + From 71904b52c1cd6addd467a44b9dffd9a2f96f30e4 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 1 Apr 2024 15:31:10 +0100 Subject: [PATCH 313/755] Make option groups plural Signed-off-by: TheKodeToad --- launcher/ui/widgets/ModFilterWidget.ui | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 27f3b8610..5f1e661e4 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -47,7 +47,7 @@ - Category + Categories false @@ -60,7 +60,7 @@ - Loader + Loaders false @@ -103,7 +103,7 @@ - Version + Versions false @@ -112,12 +112,6 @@ false - - - - - - @@ -125,13 +119,19 @@ + + + + + + - Environment + Environments false From 186a6d0c523cf63461fb4d883f0e1ead026bcb6d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 1 Apr 2024 17:00:10 +0100 Subject: [PATCH 314/755] Make filters vertical Signed-off-by: TheKodeToad --- launcher/ui/pages/modplatform/ModPage.cpp | 3 +- .../ui/pages/modplatform/ResourcePage.cpp | 6 +- launcher/ui/pages/modplatform/ResourcePage.ui | 72 +++++++++++-------- launcher/ui/widgets/ModFilterWidget.ui | 17 +++-- 4 files changed, 57 insertions(+), 41 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 8fd42b427..3561afd38 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -68,10 +68,9 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) if (m_filter_widget) disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); + m_ui->horizontalLayout->replaceWidget(m_filter_widget == nullptr ? m_ui->filterWidget : m_filter_widget.get(), widget.get()); m_filter_widget.swap(widget); - m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount()); - m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ResourcePage::updateVersionList); diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 35c4afc0d..112b01b20 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -71,7 +71,7 @@ ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_in m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - m_ui->gridLayout_3->addWidget(&m_fetch_progress, 0, 0, 1, m_ui->gridLayout_3->columnCount()); + layout()->replaceWidget(m_ui->fetchProgress, &m_fetch_progress); m_ui->packView->setItemDelegate(new ProjectItemDelegate(this)); m_ui->packView->installEventFilter(this); @@ -93,8 +93,10 @@ void ResourcePage::retranslate() void ResourcePage::openedImpl() { - if (!supportsFiltering()) + if (!supportsFiltering()) { m_ui->resourceFilterButton->setVisible(false); + m_ui->filterWidget->hide(); + } //: String in the search bar of the mod downloading dialog m_ui->searchEdit->setPlaceholderText(tr("Search for %1...").arg(resourcesString())); diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index 73a9d3b1a..b4f48f074 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -11,19 +11,12 @@ - - - - - - false - - - false - - + + + + - + Qt::ScrollBarAlwaysOff @@ -39,19 +32,19 @@ + + + + false + + + false + + + - - - - Search - - - - - - - + @@ -74,13 +67,6 @@ - - - - Filter options - - - @@ -88,6 +74,26 @@ + + + + + + + Search + + + + + + + + + + Filter options + + + @@ -96,6 +102,12 @@ QTextBrowser
    ui/widgets/ProjectDescriptionPage.h
    + + ProgressWidget + QWidget +
    ui/widgets/ProgressWidget.h
    + 1 +
    searchEdit diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 5f1e661e4..13727167f 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -6,10 +6,16 @@ 0 0 - 553 - 604 + 310 + 600
    + + + 310 + 16777215 + + Form @@ -28,9 +34,6 @@ - - QFrame::NoFrame - true @@ -39,8 +42,8 @@ 0 0 - 553 - 604 + 308 + 598 From 5f11b2e77fbf14f9f9e5459b80a2308bc211d03f Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Tue, 2 Apr 2024 19:10:40 +0200 Subject: [PATCH 315/755] chore: update to Qt 6.7.0 Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14c725b63..44779386f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,7 +76,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: '' - qt_version: '6.6.2' + qt_version: '6.7.0' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -88,7 +88,7 @@ jobs: qt_ver: 6 qt_host: windows qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.6.2' + qt_version: '6.7.0' qt_modules: 'qt5compat qtimageformats' qt_tools: '' @@ -98,7 +98,7 @@ jobs: qt_ver: 6 qt_host: mac qt_arch: '' - qt_version: '6.6.2' + qt_version: '6.7.0' qt_modules: 'qt5compat qtimageformats' qt_tools: '' From 450b73328e20941eba12be3b7c7f8bb13dc0f944 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 3 Apr 2024 19:34:35 +0300 Subject: [PATCH 316/755] Fixed crash on non-latin instance name Signed-off-by: Trial97 --- .../minecraft/launch/LauncherPartLaunch.cpp | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index aa94edb5d..dcfb3d148 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -74,16 +74,36 @@ QString shortPathName(const QString& file) auto input = file.toStdWString(); std::wstring output; long length = GetShortPathNameW(input.c_str(), NULL, 0); + if (length == 0) + return {}; // NOTE: this resizing might seem weird... // when GetShortPathNameW fails, it returns length including null character // when it succeeds, it returns length excluding null character // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx output.resize(length); - GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length); + if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0) + return {}; output.resize(length - 1); QString ret = QString::fromStdWString(output); return ret; } + +QString getShortPathName(const QString& file) +{ + auto path = shortPathName(file); + if (!path.isEmpty()) + return path; + // the path can not be getted due to the file/folder not existing + // so create the parrent folder + // and assume that we can concatenate the short path of the parent folder with the file name + // usually the 8 bit characters are in the instance name not in the name of the end files/folders we need + FS::ensureFilePathExists(file); + QFileInfo a(file); + auto partialShortPath = shortPathName(a.path()); + if (!partialShortPath.isEmpty()) + return FS::PathCombine(partialShortPath, a.fileName()); + return file; +} #endif // if the string survives roundtrip through local 8bit encoding... @@ -137,7 +157,7 @@ void LauncherPartLaunch::executeTask() auto natPath = minecraftInstance->getNativePath(); #ifdef Q_OS_WIN if (!fitsInLocal8bit(natPath)) { - args << "-Djava.library.path=" + shortPathName(natPath); + args << "-Djava.library.path=" + getShortPathName(natPath); } else { args << "-Djava.library.path=" + natPath; } @@ -150,7 +170,7 @@ void LauncherPartLaunch::executeTask() QStringList processed; for (auto& item : classPath) { if (!fitsInLocal8bit(item)) { - processed << shortPathName(item); + processed << getShortPathName(item); } else { processed << item; } From 0d372d2fbfd76b54f156faeca6a31fff80875999 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Wed, 3 Apr 2024 21:04:26 +0200 Subject: [PATCH 317/755] fix(nix): add jdk21 Signed-off-by: Sefa Eyeoglu --- nix/pkg/wrapper.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nix/pkg/wrapper.nix b/nix/pkg/wrapper.nix index cd356c8d7..1bcff1f9b 100644 --- a/nix/pkg/wrapper.nix +++ b/nix/pkg/wrapper.nix @@ -15,6 +15,7 @@ openal, jdk8, jdk17, + jdk21, gamemode, flite, mesa-demos, @@ -24,7 +25,7 @@ gamemodeSupport ? stdenv.isLinux, textToSpeechSupport ? stdenv.isLinux, controllerSupport ? stdenv.isLinux, - jdks ? [jdk17 jdk8], + jdks ? [jdk21 jdk17 jdk8], additionalLibs ? [], additionalPrograms ? [], }: let From 8ecab305ac67afb70a278d286df13cc85134dec2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 4 Apr 2024 13:49:27 +0300 Subject: [PATCH 318/755] Moved the creation of natives folder in ExtractNatives task Signed-off-by: Trial97 --- launcher/minecraft/launch/ExtractNatives.cpp | 1 + launcher/minecraft/launch/ExtractNatives.h | 2 -- .../minecraft/launch/LauncherPartLaunch.cpp | 36 ++++++------------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/launcher/minecraft/launch/ExtractNatives.cpp b/launcher/minecraft/launch/ExtractNatives.cpp index 8f3cac4d1..405008f40 100644 --- a/launcher/minecraft/launch/ExtractNatives.cpp +++ b/launcher/minecraft/launch/ExtractNatives.cpp @@ -79,6 +79,7 @@ void ExtractNatives::executeTask() auto settings = minecraftInstance->settings(); auto outputPath = minecraftInstance->getNativePath(); + FS::ensureFolderPathExists(outputPath); auto javaVersion = minecraftInstance->getJavaVersion(); bool jniHackEnabled = javaVersion.major() >= 8; for (const auto& source : toExtract) { diff --git a/launcher/minecraft/launch/ExtractNatives.h b/launcher/minecraft/launch/ExtractNatives.h index 2ab8816bd..4837a9dbb 100644 --- a/launcher/minecraft/launch/ExtractNatives.h +++ b/launcher/minecraft/launch/ExtractNatives.h @@ -16,8 +16,6 @@ #pragma once #include -#include -#include "minecraft/auth/AuthSession.h" // FIXME: temporary wrapper for existing task. class ExtractNatives : public LaunchStep { diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index dcfb3d148..3662f2bb6 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -88,20 +88,15 @@ QString shortPathName(const QString& file) return ret; } -QString getShortPathName(const QString& file) +QString getPathNameInLocal8bit(const QString& file) { - auto path = shortPathName(file); - if (!path.isEmpty()) - return path; - // the path can not be getted due to the file/folder not existing - // so create the parrent folder - // and assume that we can concatenate the short path of the parent folder with the file name - // usually the 8 bit characters are in the instance name not in the name of the end files/folders we need - FS::ensureFilePathExists(file); - QFileInfo a(file); - auto partialShortPath = shortPathName(a.path()); - if (!partialShortPath.isEmpty()) - return FS::PathCombine(partialShortPath, a.fileName()); + if (!fitsInLocal8bit(file)) { + auto path = shortPathName(file); + if (!path.isEmpty()) { + return path; + } + // in case shortPathName fails just return the path as is + } return file; } #endif @@ -156,24 +151,15 @@ void LauncherPartLaunch::executeTask() auto natPath = minecraftInstance->getNativePath(); #ifdef Q_OS_WIN - if (!fitsInLocal8bit(natPath)) { - args << "-Djava.library.path=" + getShortPathName(natPath); - } else { - args << "-Djava.library.path=" + natPath; - } -#else - args << "-Djava.library.path=" + natPath; + natPath = getPathNameInLocal8bit(natPath); #endif + args << "-Djava.library.path=" + natPath; args << "-cp"; #ifdef Q_OS_WIN QStringList processed; for (auto& item : classPath) { - if (!fitsInLocal8bit(item)) { - processed << getShortPathName(item); - } else { - processed << item; - } + processed << getPathNameInLocal8bit(item); } args << processed.join(';'); #else From 3ddcadcdd1f72f8135333986e55cf583204da1e2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 4 Apr 2024 18:01:24 +0300 Subject: [PATCH 319/755] Moved getPathNameInLocal8bit inside FileSystem namespace Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 40 +++++++++++++++++ launcher/FileSystem.h | 4 ++ launcher/InstanceImportTask.h | 4 -- launcher/java/JavaChecker.cpp | 3 ++ .../minecraft/launch/LauncherPartLaunch.cpp | 45 +------------------ 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index f9be91a2a..31c1c0fc6 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -1585,4 +1585,44 @@ uintmax_t hardLinkCount(const QString& path) return count; } +#ifdef Q_OS_WIN +// returns 8.3 file format from long path +QString shortPathName(const QString& file) +{ + auto input = file.toStdWString(); + std::wstring output; + long length = GetShortPathNameW(input.c_str(), NULL, 0); + if (length == 0) + return {}; + // NOTE: this resizing might seem weird... + // when GetShortPathNameW fails, it returns length including null character + // when it succeeds, it returns length excluding null character + // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx + output.resize(length); + if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0) + return {}; + output.resize(length - 1); + QString ret = QString::fromStdWString(output); + return ret; +} + +// if the string survives roundtrip through local 8bit encoding... +bool fitsInLocal8bit(const QString& string) +{ + return string == QString::fromLocal8Bit(string.toLocal8Bit()); +} + +QString getPathNameInLocal8bit(const QString& file) +{ + if (!fitsInLocal8bit(file)) { + auto path = shortPathName(file); + if (!path.isEmpty()) { + return path; + } + // in case shortPathName fails just return the path as is + } + return file; +} +#endif + } // namespace FS diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index f13fb9f28..428507ff7 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -551,4 +551,8 @@ bool canLink(const QString& src, const QString& dst); uintmax_t hardLinkCount(const QString& path); +#ifdef Q_OS_WIN +QString getPathNameInLocal8bit(const QString& file); +#endif + } // namespace FS diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index a1cf2560b..28efd7ec5 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -47,9 +47,6 @@ #include class QuaZip; -namespace Flame { -class FileResolvingTask; -} class InstanceImportTask : public InstanceTask { Q_OBJECT @@ -79,7 +76,6 @@ class InstanceImportTask : public InstanceTask { private: /* data */ NetJob::Ptr m_filesNetJob; - shared_qobject_ptr m_modIdResolver; QUrl m_sourceUrl; QString m_archivePath; bool m_downloadRequired = false; diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp index 20caba189..fc8da55c2 100644 --- a/launcher/java/JavaChecker.cpp +++ b/launcher/java/JavaChecker.cpp @@ -55,6 +55,9 @@ void JavaChecker::performCheck() qDebug() << "Java checker library could not be found. Please check your installation."; return; } +#ifdef Q_OS_WIN + checkerJar = FS::getPathNameInLocal8bit(checkerJar); +#endif QStringList args; diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index 3662f2bb6..4e021c4a8 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -66,47 +66,6 @@ LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent) connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state); } -#ifdef Q_OS_WIN -// returns 8.3 file format from long path -#include -QString shortPathName(const QString& file) -{ - auto input = file.toStdWString(); - std::wstring output; - long length = GetShortPathNameW(input.c_str(), NULL, 0); - if (length == 0) - return {}; - // NOTE: this resizing might seem weird... - // when GetShortPathNameW fails, it returns length including null character - // when it succeeds, it returns length excluding null character - // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx - output.resize(length); - if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0) - return {}; - output.resize(length - 1); - QString ret = QString::fromStdWString(output); - return ret; -} - -QString getPathNameInLocal8bit(const QString& file) -{ - if (!fitsInLocal8bit(file)) { - auto path = shortPathName(file); - if (!path.isEmpty()) { - return path; - } - // in case shortPathName fails just return the path as is - } - return file; -} -#endif - -// if the string survives roundtrip through local 8bit encoding... -bool fitsInLocal8bit(const QString& string) -{ - return string == QString::fromLocal8Bit(string.toLocal8Bit()); -} - void LauncherPartLaunch::executeTask() { QString jarPath = APPLICATION->getJarPath("NewLaunch.jar"); @@ -151,7 +110,7 @@ void LauncherPartLaunch::executeTask() auto natPath = minecraftInstance->getNativePath(); #ifdef Q_OS_WIN - natPath = getPathNameInLocal8bit(natPath); + natPath = FS::getPathNameInLocal8bit(natPath); #endif args << "-Djava.library.path=" + natPath; @@ -159,7 +118,7 @@ void LauncherPartLaunch::executeTask() #ifdef Q_OS_WIN QStringList processed; for (auto& item : classPath) { - processed << getPathNameInLocal8bit(item); + processed << FS::getPathNameInLocal8bit(item); } args << processed.join(';'); #else From e704fbbcec4268e2ff41db7f851ddd3e961eb8b9 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:53:30 +0200 Subject: [PATCH 320/755] fix: update flatpak manifest for java 21 backport of https://github.com/flathub/org.prismlauncher.PrismLauncher/pull/54 for our CI builds Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- flatpak/org.prismlauncher.PrismLauncher.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml index c3ac132b1..b4c6e8143 100644 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -3,6 +3,7 @@ runtime: org.kde.Platform runtime-version: 5.15-23.08 sdk: org.kde.Sdk sdk-extensions: + - org.freedesktop.Sdk.Extension.openjdk21 - org.freedesktop.Sdk.Extension.openjdk17 - org.freedesktop.Sdk.Extension.openjdk8 @@ -50,6 +51,8 @@ modules: buildsystem: simple build-commands: - mkdir -p /app/jdk/ + - /usr/lib/sdk/openjdk21/install.sh + - mv /app/jre /app/jdk/21 - /usr/lib/sdk/openjdk17/install.sh - mv /app/jre /app/jdk/17 - /usr/lib/sdk/openjdk8/install.sh From 8056a2fa460b11739b080d96deda35cd8897c40c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 5 Apr 2024 23:12:31 +0100 Subject: [PATCH 321/755] Replace invalid characters when extracting ZIP Signed-off-by: TheKodeToad --- launcher/FileSystem.cpp | 19 ++++++++++++++----- launcher/FileSystem.h | 2 ++ launcher/MMCZip.cpp | 5 ++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index f9be91a2a..7bfa24de9 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -801,15 +801,24 @@ QString NormalizePath(QString path) } } -QString badFilenameChars = "\"\\/?<>:;*|!+\r\n"; +static const QString BAD_PATH_CHARS = "\"?<>:;*|!+\r\n"; +static const QString BAD_FILENAME_CHARS = BAD_PATH_CHARS + "\\/"; QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) { - for (int i = 0; i < string.length(); i++) { - if (badFilenameChars.contains(string[i])) { + for (int i = 0; i < string.length(); i++) + if (string.at(i).toLatin1() < ' ' || BAD_FILENAME_CHARS.contains(string.at(i))) string[i] = replaceWith; - } - } + + return string; +} + +QString RemoveInvalidPathChars(QString string, QChar replaceWith) +{ + for (int i = 0; i < string.length(); i++) + if (string.at(i) < ' ' || BAD_PATH_CHARS.contains(string.at(i))) + string[i] = replaceWith; + return string; } diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index f13fb9f28..1c9ba2672 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -342,6 +342,8 @@ QString NormalizePath(QString path); QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-'); +QString RemoveInvalidPathChars(QString string, QChar replaceWith = '-'); + QString DirNameFromString(QString string, QString inDir = "."); /// Checks if the a given Path contains "!" diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 033aa0755..6f7c83952 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -286,10 +286,13 @@ std::optional extractSubDir(QuaZip* zip, const QString& subdir, con do { QString file_name = zip->getCurrentFileName(); +#ifdef Q_OS_WIN + file_name = FS::RemoveInvalidPathChars(file_name); +#endif if (!file_name.startsWith(subdir)) continue; - auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, subdir.size())); + auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(subdir.size())); auto original_name = relative_file_name; // Fix subdirs/files ending with a / getting transformed into absolute paths From 9986fe4c45be7f3ce22fdce64812677f16cf7b4c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 6 Apr 2024 13:28:42 +0100 Subject: [PATCH 322/755] Fix unicode characters being invalid in filenames Signed-off-by: TheKodeToad --- launcher/FileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 7bfa24de9..93e001f64 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -807,7 +807,7 @@ static const QString BAD_FILENAME_CHARS = BAD_PATH_CHARS + "\\/"; QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) { for (int i = 0; i < string.length(); i++) - if (string.at(i).toLatin1() < ' ' || BAD_FILENAME_CHARS.contains(string.at(i))) + if (string.at(i) < ' ' || BAD_FILENAME_CHARS.contains(string.at(i))) string[i] = replaceWith; return string; From 74fcfbae3219612e8715e6fcb21194b78f951389 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 7 Apr 2024 00:20:19 +0000 Subject: [PATCH 323/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2' (2024-03-01) → 'github:hercules-ci/flake-parts/9126214d0a59633752a136528f5f3b9aa8565b7d' (2024-04-01) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/807c549feabce7eddbf259dbdcec9e0600a0660d' (2024-03-29) → 'github:nixos/nixpkgs/9e7f26f82acb057498335362905fde6fea4ca50a' (2024-04-06) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/c11e43aed6f17336c25cd120eac886b96c455731' (2024-03-30) → 'github:cachix/pre-commit-hooks.nix/e35aed5fda3cc79f88ed7f1795021e559582093a' (2024-04-02) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index dcc028b2d..b84278d0e 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1709336216, - "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", "type": "github" }, "original": { @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711715736, - "narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=", + "lastModified": 1712420723, + "narHash": "sha256-VnG0Eu394Ga2FCe8Q66m6OEQF8iAqjDYsjmtl+N2omk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "807c549feabce7eddbf259dbdcec9e0600a0660d", + "rev": "9e7f26f82acb057498335362905fde6fea4ca50a", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1711760932, - "narHash": "sha256-DqUTQ2iAAqSDwMhKBqvi24v0Oc7pD3LCK/0FCG//TdA=", + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "c11e43aed6f17336c25cd120eac886b96c455731", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", "type": "github" }, "original": { From 1c3c9d24573d4f3cf25580129d3611f5082dcc29 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 7 Apr 2024 13:48:34 +0300 Subject: [PATCH 324/755] Added the new ftb settings path Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/import_ftb/ListModel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index 52671ea8e..f3c737977 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -44,7 +44,9 @@ QString getFTBRoot() QString getDynamicPath() { - auto settingsPath = FS::PathCombine(getFTBRoot(), "bin", "settings.json"); + auto settingsPath = FS::PathCombine(getFTBRoot(), "storage", "settings.json"); + if (!QFileInfo::exists(settingsPath)) + settingsPath = FS::PathCombine(getFTBRoot(), "bin", "settings.json"); if (!QFileInfo::exists(settingsPath)) { qWarning() << "The ftb app setings doesn't exist."; return {}; From 9cc58fe62c776dd206bb6dd99c886ce491662214 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 7 Apr 2024 21:25:08 +0300 Subject: [PATCH 325/755] Fixed crash on invalid curseforge link on import Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ImportPage.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index ae4bd49d7..ed7ebfad9 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -123,6 +123,10 @@ void ImportPage::updateState() // need to find the download link for the modpack // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE QUrlQuery query(url); + if (query.allQueryItemValues("addonId").isEmpty() || query.allQueryItemValues("fileId").isEmpty()) { + qDebug() << "Invalid curseforge link:" << url; + return; + } auto addonId = query.allQueryItemValues("addonId")[0]; auto fileId = query.allQueryItemValues("fileId")[0]; auto array = std::make_shared(); From f91f97e56dd27fe4fb355a800e853c782f92baa6 Mon Sep 17 00:00:00 2001 From: ColonelGerdauf Date: Sun, 7 Apr 2024 16:49:39 -0400 Subject: [PATCH 326/755] Renaming and removing redundancies old's do not make sense here Signed-off-by: ColonelGerdauf --- launcher/ui/pages/modplatform/CustomPage.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/launcher/ui/pages/modplatform/CustomPage.cpp b/launcher/ui/pages/modplatform/CustomPage.cpp index 068fb3a36..d2b73008d 100644 --- a/launcher/ui/pages/modplatform/CustomPage.cpp +++ b/launcher/ui/pages/modplatform/CustomPage.cpp @@ -55,7 +55,6 @@ CustomPage::CustomPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(par connect(ui->alphaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); connect(ui->betaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); connect(ui->snapshotFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); - connect(ui->oldSnapshotFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); connect(ui->releaseFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); connect(ui->experimentsFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); connect(ui->refreshBtn, &QPushButton::clicked, this, &CustomPage::refresh); @@ -96,13 +95,11 @@ void CustomPage::filterChanged() { QStringList out; if (ui->alphaFilter->isChecked()) - out << "(old_alpha)"; + out << "(alpha)"; if (ui->betaFilter->isChecked()) - out << "(old_beta)"; + out << "(beta)"; if (ui->snapshotFilter->isChecked()) out << "(snapshot)"; - if (ui->oldSnapshotFilter->isChecked()) - out << "(old_snapshot)"; if (ui->releaseFilter->isChecked()) out << "(release)"; if (ui->experimentsFilter->isChecked()) From a877f9146de17a09b4803f6241f675d9a2ba739c Mon Sep 17 00:00:00 2001 From: ColonelGerdauf Date: Sun, 7 Apr 2024 16:53:53 -0400 Subject: [PATCH 327/755] Change of parameters To help deal with adjustments Signed-off-by: ColonelGerdauf --- launcher/ui/pages/modplatform/CustomPage.ui | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/launcher/ui/pages/modplatform/CustomPage.ui b/launcher/ui/pages/modplatform/CustomPage.ui index 23351ccd4..fda3e8a2e 100644 --- a/launcher/ui/pages/modplatform/CustomPage.ui +++ b/launcher/ui/pages/modplatform/CustomPage.ui @@ -93,16 +93,6 @@ - - - - Old Snapshots - - - true - - - @@ -286,7 +276,6 @@ tabWidget releaseFilter snapshotFilter - oldSnapshotFilter betaFilter alphaFilter experimentsFilter From 9f48694eb2be5f62efb122b554c7066e49f3383d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 9 Apr 2024 08:28:09 +0300 Subject: [PATCH 328/755] Fixed application close on open file dialog Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 9f54e7dd9..bb8751ccc 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -225,6 +225,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // Don't quit on hiding the last window this->setQuitOnLastWindowClosed(false); + this->setQuitLockEnabled(false); // Commandline parsing QCommandLineParser parser; From 8121a6536bda27221123842b11f118851904fe5e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 12 Apr 2024 23:45:31 +0300 Subject: [PATCH 329/755] Fixed pack update Signed-off-by: Trial97 --- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 6 ++++-- .../modplatform/modrinth/ModrinthInstanceCreationTask.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 824fdce7e..a688cea87 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -20,6 +20,7 @@ #include "ui/pages/modplatform/OptionalModDialog.h" #include +#include #include bool ModrinthCreationTask::abort() @@ -58,6 +59,7 @@ bool ModrinthCreationTask::updateInstance() return false; auto version_name = inst->getManagedPackVersionName(); + m_root_path = QFileInfo(inst->gameRoot()).fileName(); auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : ""; if (shouldConfirmUpdate()) { @@ -173,7 +175,7 @@ bool ModrinthCreationTask::createInstance() FS::ensureFilePathExists(new_index_place); QFile::rename(index_path, new_index_place); - auto mcPath = FS::PathCombine(m_stagingPath, "minecraft"); + auto mcPath = FS::PathCombine(m_stagingPath, m_root_path); auto override_path = FS::PathCombine(m_stagingPath, "overrides"); if (QFile::exists(override_path)) { @@ -234,7 +236,7 @@ bool ModrinthCreationTask::createInstance() m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network())); - auto root_modpack_path = FS::PathCombine(m_stagingPath, "minecraft"); + auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path); auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path); for (auto file : m_files) { diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h index 1bd5b7de9..f07734a58 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h @@ -46,4 +46,6 @@ class ModrinthCreationTask final : public InstanceCreationTask { NetJob::Ptr m_files_job; std::optional m_instance; + + QString m_root_path = "minecraft"; }; From cb8936ed68ca8c05017f9827dcdcbd49a97c58ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 14 Apr 2024 00:21:24 +0000 Subject: [PATCH 330/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/9e7f26f82acb057498335362905fde6fea4ca50a' (2024-04-06) → 'github:nixos/nixpkgs/a0c9e3aee1000ac2bfb0e5b98c94c946a5d180a9' (2024-04-12) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/e35aed5fda3cc79f88ed7f1795021e559582093a' (2024-04-02) → 'github:cachix/pre-commit-hooks.nix/40e6053ecb65fcbf12863338a6dcefb3f55f1bf8' (2024-04-12) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index b84278d0e..408c26cbd 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1712420723, - "narHash": "sha256-VnG0Eu394Ga2FCe8Q66m6OEQF8iAqjDYsjmtl+N2omk=", + "lastModified": 1712883908, + "narHash": "sha256-icE1IJE9fHcbDfJ0+qWoDdcBXUoZCcIJxME4lMHwvSM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9e7f26f82acb057498335362905fde6fea4ca50a", + "rev": "a0c9e3aee1000ac2bfb0e5b98c94c946a5d180a9", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1712055707, - "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "lastModified": 1712897695, + "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", + "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", "type": "github" }, "original": { From 77aead6470acfc0eab53321e64b3f35b1c7885ee Mon Sep 17 00:00:00 2001 From: DylanJOrr Date: Tue, 16 Apr 2024 15:51:18 -0400 Subject: [PATCH 331/755] Rename object name "userInterfaceTab" from "generalTab" The "User Interface" tab was innacurately named "General" which I just went in and fixed. This also renamed the object for respective vertical spacers. Signed-off-by: SolidStateDj --- launcher/ui/pages/global/LauncherPage.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 18b52e1b8..928ec8103 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -237,7 +237,7 @@ - + Qt::Vertical @@ -251,7 +251,7 @@ - + User Interface @@ -374,7 +374,7 @@ - + Qt::Vertical From 252406ba3e793b4b096ed1e7bde43c2d2a9c2eca Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 19 Apr 2024 21:19:36 +0100 Subject: [PATCH 332/755] Support neoforge.mods.toml Signed-off-by: TheKodeToad --- launcher/minecraft/mod/tasks/LocalModParseTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index e9e12d86a..3982f3338 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -469,7 +469,7 @@ bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level) QuaZipFile file(&zip); - if (zip.setCurrentFile("META-INF/mods.toml")) { + if (zip.setCurrentFile("META-INF/mods.toml") || zip.setCurrentFile("META-INF/neoforge.mods.toml")) { if (!file.open(QIODevice::ReadOnly)) { zip.close(); return false; From 89230c4f8a69a66b46fdd00544ccfaf444a13652 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 20 Apr 2024 18:01:25 +0300 Subject: [PATCH 333/755] Moved the export modlist out of the sub-menu Signed-off-by: Trial97 --- .../ui/pages/instance/ExternalResourcesPage.ui | 17 ++++++++++++++--- launcher/ui/pages/instance/ModFolderPage.cpp | 7 +++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index ff08e12d2..9d6f61db0 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -70,15 +70,15 @@ - - true - Actions Qt::ToolButtonTextOnly + + true + RightToolBarArea @@ -171,6 +171,17 @@ Try to check or update all selected resources (all resources if none are selected)
    + + + true + + + Export modlist + + + Export mod's metadata to text + + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 8b9924470..f045ab6c0 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -112,10 +112,6 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); actionRemoveItemMetadata->setEnabled(false); - auto actionExportMetadata = updateMenu->addAction(tr("Export metadata")); - actionExportMetadata->setToolTip(tr("Export mod's metadata to text")); - connect(actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata); - ui->actionUpdateItem->setMenu(updateMenu); ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); @@ -126,6 +122,9 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr ui->actionsToolbar->addAction(ui->actionVisitItemPage); connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); + ui->actionsToolbar->insertActionAfter(ui->actionVisitItemPage, ui->actionExportMetadata); + connect(ui->actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata); + auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, From 210841606c72644e427e49921868982155d77622 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 21 Apr 2024 00:19:37 +0000 Subject: [PATCH 334/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/a0c9e3aee1000ac2bfb0e5b98c94c946a5d180a9' (2024-04-12) → 'github:nixos/nixpkgs/fd16bb6d3bcca96039b11aa52038fafeb6e4f4be' (2024-04-20) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 408c26cbd..0fbfca9cc 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1712883908, - "narHash": "sha256-icE1IJE9fHcbDfJ0+qWoDdcBXUoZCcIJxME4lMHwvSM=", + "lastModified": 1713596654, + "narHash": "sha256-LJbHQQ5aX1LVth2ST+Kkse/DRzgxlVhTL1rxthvyhZc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "a0c9e3aee1000ac2bfb0e5b98c94c946a5d180a9", + "rev": "fd16bb6d3bcca96039b11aa52038fafeb6e4f4be", "type": "github" }, "original": { From 9f4654ede91a81a3d1099c949164690e295995a4 Mon Sep 17 00:00:00 2001 From: William Gray Date: Sun, 21 Apr 2024 03:35:27 -0500 Subject: [PATCH 335/755] Update JavaUtils.cpp Signed-off-by: William Gray --- launcher/java/JavaUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 074bf54df..639eaf4e7 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -413,6 +413,8 @@ QList JavaUtils::FindJavaPaths() scanJavaDirs(FS::PathCombine(home, ".jdks")); // javas downloaded by sdkman scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); + // javas downloaded by gradle (toolchains) + scanJavaDirs(FS::PathCombine(home, ".gradle/jdks")); javas.append(getMinecraftJavaBundle()); javas = addJavasFromEnv(javas); From a96f957b020355f51975ecdaa0486febd997ccf3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 21 Apr 2024 14:48:27 +0300 Subject: [PATCH 336/755] Removed java from appimage Signed-off-by: Trial97 --- .github/scripts/prepare_JREs.sh | 41 --------------------------------- .github/workflows/build.yml | 10 -------- 2 files changed, 51 deletions(-) delete mode 100755 .github/scripts/prepare_JREs.sh diff --git a/.github/scripts/prepare_JREs.sh b/.github/scripts/prepare_JREs.sh deleted file mode 100755 index ee713f81f..000000000 --- a/.github/scripts/prepare_JREs.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -URL_JDK8="https://api.adoptium.net/v3/binary/version/jdk8u312-b07/linux/x64/jre/hotspot/normal/eclipse" -URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse" - -mkdir -p JREs -pushd JREs - -wget --content-disposition "$URL_JDK8" -wget --content-disposition "$URL_JDK17" - -for file in *; -do - mkdir temp - - re='(OpenJDK([[:digit:]]+)U-jre_x64_linux_hotspot_([[:digit:]]+)(.*).tar.gz)' - if [[ $file =~ $re ]]; - then - version_major=${BASH_REMATCH[2]} - version_trailing=${BASH_REMATCH[4]} - - if [ $version_major = 17 ]; - then - hyphen='-' - else - hyphen='' - fi - - version_edit=$(echo $version_trailing | sed -e 's/_/+/g' | sed -e 's/b/-b/g') - dir_name=jdk$hyphen$version_major$version_edit-jre - mkdir jre$version_major - tar -xzf $file -C temp - pushd temp/$dir_name - cp -r . ../../jre$version_major - popd - fi - - rm -rf temp -done - -popd diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 44779386f..bc8129fba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -259,7 +259,6 @@ jobs: wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" - ${{ github.workspace }}/.github/scripts/prepare_JREs.sh sudo apt install libopengl0 - name: Add QT_HOST_PATH var (Windows MSVC arm64) @@ -528,13 +527,8 @@ jobs: chmod +x linuxdeploy-*.AppImage - mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines - cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk - - cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk - cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ @@ -542,10 +536,6 @@ jobs: cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib" - LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server" - LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64" - LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server" - LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" export LD_LIBRARY_PATH chmod +x AppImageUpdate-x86_64.AppImage From 1c8d333bfd93bcafe5514f024adaf1236d1aed26 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 21 Apr 2024 15:07:10 +0300 Subject: [PATCH 337/755] Fixed build Signed-off-by: Trial97 --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc8129fba..f3896550f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -527,6 +527,7 @@ jobs: chmod +x linuxdeploy-*.AppImage + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines From dbfd535b38d799a773b3843709bbeae7f5a9e7d3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 23 Apr 2024 08:52:17 +0300 Subject: [PATCH 338/755] Fix bundled java detection on mac Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 639eaf4e7..35d9ec610 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -441,13 +441,13 @@ QString JavaUtils::getJavaCheckPath() QStringList getMinecraftJavaBundle() { - QString partialPath; QString executable = "java"; QStringList processpaths; #if defined(Q_OS_OSX) - partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); + processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime")); #elif defined(Q_OS_WIN32) - partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); + QString partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); + processpaths << FS::PathCombine(partialPath, ".minecraft", "runtime"); executable += "w.exe"; // add the microsoft store version of the launcher to the search. the current path is: @@ -457,10 +457,8 @@ QStringList getMinecraftJavaBundle() minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); processpaths << minecraftMSStorePath; #else - partialPath = QDir::homePath(); + processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime"); #endif - auto minecraftDataPath = FS::PathCombine(partialPath, ".minecraft", "runtime"); - processpaths << minecraftDataPath; QStringList javas; while (!processpaths.isEmpty()) { From dac70278462913b7c0191c56a04e62529d5c774b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 23 Apr 2024 18:59:31 +0300 Subject: [PATCH 339/755] Fixed windows appdata path Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 35d9ec610..d1d58b0cf 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -446,16 +446,16 @@ QStringList getMinecraftJavaBundle() #if defined(Q_OS_OSX) processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime")); #elif defined(Q_OS_WIN32) - QString partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); - processpaths << FS::PathCombine(partialPath, ".minecraft", "runtime"); + auto appDataPath = QProcessEnvironment::systemEnvironment().value("AppData", ""); + processpaths << FS::PathCombine(QFileInfo(appDataPath).absolutePath(), ".minecraft", "runtime"); executable += "w.exe"; // add the microsoft store version of the launcher to the search. the current path is: // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime + auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); auto minecraftMSStorePath = - FS::PathCombine(QFileInfo(partialPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); - minecraftMSStorePath = FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); - processpaths << minecraftMSStorePath; + FS::PathCombine(QFileInfo(localAppDataPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); + processpaths << FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); #else processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime"); #endif From 73c3794c1bfa9d3cd01339039ad8597971b5fb53 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 23 Apr 2024 19:03:19 +0300 Subject: [PATCH 340/755] Changed env variable Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index d1d58b0cf..c0bb39d72 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -446,10 +446,11 @@ QStringList getMinecraftJavaBundle() #if defined(Q_OS_OSX) processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime")); #elif defined(Q_OS_WIN32) - auto appDataPath = QProcessEnvironment::systemEnvironment().value("AppData", ""); - processpaths << FS::PathCombine(QFileInfo(appDataPath).absolutePath(), ".minecraft", "runtime"); executable += "w.exe"; + auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", ""); + processpaths << FS::PathCombine(QFileInfo(appDataPath).absolutePath(), ".minecraft", "runtime"); + // add the microsoft store version of the launcher to the search. the current path is: // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); From 10d1720da5c350250390fa8b38a5c4db6429462b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 23 Apr 2024 19:08:14 +0300 Subject: [PATCH 341/755] Removed duplicate local Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index c0bb39d72..b12461f44 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -455,7 +455,7 @@ QStringList getMinecraftJavaBundle() // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); auto minecraftMSStorePath = - FS::PathCombine(QFileInfo(localAppDataPath).absolutePath(), "Local", "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); + FS::PathCombine(QFileInfo(localAppDataPath).absolutePath(), "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); processpaths << FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); #else processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime"); From 5ae98f4f901200647ea3b4ff855248705f28fd3b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 24 Apr 2024 15:49:05 +0300 Subject: [PATCH 342/755] Fixed windows bundled java path Signed-off-by: Trial97 --- launcher/java/JavaUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index b12461f44..3627cec39 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -449,13 +449,13 @@ QStringList getMinecraftJavaBundle() executable += "w.exe"; auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", ""); - processpaths << FS::PathCombine(QFileInfo(appDataPath).absolutePath(), ".minecraft", "runtime"); + processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime"); // add the microsoft store version of the launcher to the search. the current path is: // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); auto minecraftMSStorePath = - FS::PathCombine(QFileInfo(localAppDataPath).absolutePath(), "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); + FS::PathCombine(QFileInfo(localAppDataPath).absoluteFilePath(), "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); processpaths << FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); #else processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime"); From c902da446174840c38fee8c8ff20ab2b21699739 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 24 Apr 2024 22:02:55 +0300 Subject: [PATCH 343/755] Fix image width in project description Signed-off-by: Trial97 --- .../ui/widgets/VariableSizedImageObject.cpp | 67 +++++++++++++++---- .../ui/widgets/VariableSizedImageObject.h | 13 +++- 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index cebf2a5f1..a4e0872cb 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "Application.h" @@ -36,6 +37,30 @@ QSizeF VariableSizedImageObject::intrinsicSize(QTextDocument* doc, int posInDocu auto image = qvariant_cast(format.property(ImageData)); auto size = image.size(); + if (size.isEmpty()) // can't resize a empty image + return { size }; + + // calculate the new image size based on the properties + int width = 0; + int height = 0; + auto widthVar = format.property(QTextFormat::ImageWidth); + if (widthVar.isValid()) { + width = widthVar.toInt(); + } + auto heigthVar = format.property(QTextFormat::ImageHeight); + if (heigthVar.isValid()) { + height = heigthVar.toInt(); + } + if (width != 0 && height != 0) { + size.setWidth(width); + size.setHeight(height); + } else if (width != 0) { + size.setHeight((width * size.height()) / size.width()); + size.setWidth(width); + } else if (height != 0) { + size.setWidth((height * size.width()) / size.height()); + size.setHeight(height); + } // Get the width of the text content to make the image similar sized. // doc->textWidth() includes the margin, so we need to remove it. @@ -46,6 +71,7 @@ QSizeF VariableSizedImageObject::intrinsicSize(QTextDocument* doc, int posInDocu return { size }; } + void VariableSizedImageObject::drawObject(QPainter* painter, const QRectF& rect, QTextDocument* doc, @@ -57,7 +83,20 @@ void VariableSizedImageObject::drawObject(QPainter* painter, if (m_fetching_images.contains(image_url)) return; - loadImage(doc, image_url, posInDocument); + auto meta = std::make_shared(); + meta->posInDocument = posInDocument; + meta->url = image_url; + + auto widthVar = format.property(QTextFormat::ImageWidth); + if (widthVar.isValid()) { + meta->width = widthVar.toInt(); + } + auto heigthVar = format.property(QTextFormat::ImageHeight); + if (heigthVar.isValid()) { + meta->height = heigthVar.toInt(); + } + + loadImage(doc, meta); return; } @@ -72,16 +111,19 @@ void VariableSizedImageObject::flush() m_fetching_images.clear(); } -void VariableSizedImageObject::parseImage(QTextDocument* doc, QImage image, int posInDocument) +void VariableSizedImageObject::parseImage(QTextDocument* doc, std::shared_ptr meta) { QTextCursor cursor(doc); - cursor.setPosition(posInDocument); + cursor.setPosition(meta->posInDocument); cursor.setKeepPositionOnInsert(true); auto image_char_format = cursor.charFormat(); image_char_format.setObjectType(QTextFormat::ImageObject); - image_char_format.setProperty(ImageData, image); + image_char_format.setProperty(ImageData, meta->image); + image_char_format.setProperty(QTextFormat::ImageName, meta->url.toDisplayString()); + image_char_format.setProperty(QTextFormat::ImageWidth, meta->width); + image_char_format.setProperty(QTextFormat::ImageHeight, meta->height); // Qt doesn't allow us to modify the properties of an existing object in the document. // So we remove the old one and add the new one with the ImageData property set. @@ -89,23 +131,24 @@ void VariableSizedImageObject::parseImage(QTextDocument* doc, QImage image, int cursor.insertText(QString(QChar::ObjectReplacementCharacter), image_char_format); } -void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, int posInDocument) +void VariableSizedImageObject::loadImage(QTextDocument* doc, std::shared_ptr meta) { - m_fetching_images.insert(source); + m_fetching_images.insert(meta->url); MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry( m_meta_entry, - QString("images/%1").arg(QString(QCryptographicHash::hash(source.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); + QString("images/%1").arg(QString(QCryptographicHash::hash(meta->url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); - auto job = new NetJob(QString("Load Image: %1").arg(source.fileName()), APPLICATION->network()); - job->addNetAction(Net::ApiDownload::makeCached(source, entry)); + auto job = new NetJob(QString("Load Image: %1").arg(meta->url.fileName()), APPLICATION->network()); + job->addNetAction(Net::ApiDownload::makeCached(meta->url, entry)); auto full_entry_path = entry->getFullPath(); - auto source_url = source; - auto loadImage = [this, doc, full_entry_path, source_url, posInDocument](const QImage& image) { + auto source_url = meta->url; + auto loadImage = [this, doc, full_entry_path, source_url, meta](const QImage& image) { doc->addResource(QTextDocument::ImageResource, source_url, image); - parseImage(doc, image, posInDocument); + meta->image = image; + parseImage(doc, meta); // This size hack is needed to prevent the content from being laid out in an area smaller // than the total width available (weird). diff --git a/launcher/ui/widgets/VariableSizedImageObject.h b/launcher/ui/widgets/VariableSizedImageObject.h index ca67af0c9..df3ab4f77 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.h +++ b/launcher/ui/widgets/VariableSizedImageObject.h @@ -22,6 +22,7 @@ #include #include #include +#include /** Custom image text object to be used instead of the normal one in ProjectDescriptionPage. * @@ -32,6 +33,14 @@ class VariableSizedImageObject final : public QObject, public QTextObjectInterfa Q_OBJECT Q_INTERFACES(QTextObjectInterface) + struct ImageMetadata { + int posInDocument; + QUrl url; + QImage image; + int width; + int height; + }; + public: QSizeF intrinsicSize(QTextDocument* doc, int posInDocument, const QTextFormat& format) override; void drawObject(QPainter* painter, const QRectF& rect, QTextDocument* doc, int posInDocument, const QTextFormat& format) override; @@ -49,13 +58,13 @@ class VariableSizedImageObject final : public QObject, public QTextObjectInterfa private: /** Adds the image to the document, in the given position. */ - void parseImage(QTextDocument* doc, QImage image, int posInDocument); + void parseImage(QTextDocument* doc, std::shared_ptr meta); /** Loads an image from an external source, and adds it to the document. * * This uses m_meta_entry to cache the image. */ - void loadImage(QTextDocument* doc, const QUrl& source, int posInDocument); + void loadImage(QTextDocument* doc, std::shared_ptr meta); private: QString m_meta_entry; From 1961e2081fc8f0787622820a33543751b8aef0a7 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 25 Apr 2024 00:48:40 +0300 Subject: [PATCH 344/755] Removed scroll from version checkbox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 4 ++-- launcher/ui/widgets/ModFilterWidget.cpp | 9 +++++++++ launcher/ui/widgets/ModFilterWidget.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 8117baa58..189f7ebb4 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -68,10 +68,10 @@ class CheckComboModel : public QIdentityProxyModel { QStringList checked; }; -CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",") +CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ") { QLineEdit* lineEdit = new QLineEdit(this); - lineEdit->setReadOnly(false); + lineEdit->setReadOnly(true); setLineEdit(lineEdit); lineEdit->disconnect(this); setInsertPolicy(QComboBox::NoInsert); diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index b44a573d8..ead573aee 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -89,6 +89,8 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi ui->versions->setStyleSheet("combobox-popup: 0;"); ui->version->setStyleSheet("combobox-popup: 0;"); + ui->versions->installEventFilter(this); + ui->version->installEventFilter(this); connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); @@ -115,6 +117,13 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi prepareBasicFilter(); } +bool ModFilterWidget::eventFilter(QObject* obj, QEvent* evt) +{ + if ((obj != ui->versions && obj != ui->version) || evt->type() != QEvent::Wheel) + return QTabWidget::eventFilter(obj, evt); + return true; +} + auto ModFilterWidget::getFilter() -> std::shared_ptr { m_filter_changed = false; diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 4358dd91d..35125c910 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -85,6 +85,7 @@ class ModFilterWidget : public QTabWidget { public slots: void setCategories(const QList&); + bool eventFilter(QObject* obj, QEvent* evt); private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); From a30c7193a2e0b90e16e866ec351beb5cf3b9f807 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Thu, 25 Apr 2024 16:55:15 +0200 Subject: [PATCH 345/755] remove server side decorations FIXME and document why its needed Signed-off-by: Jan200101 --- launcher/ui/MainWindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 85573314d..6bbb10532 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -231,7 +231,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi setInstanceActionsEnabled(false); // add a close button at the end of the main toolbar when running on gamescope / steam deck - // FIXME: detect if we don't have server side decorations instead + // this is only needed on gamescope because it defaults to an X11/XWayland session and + // does not implement decorations if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") { ui->mainToolBar->addAction(ui->actionCloseWindow); } From dedea2c05d8503fea0d6b7c0adea25e64c8d511c Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Thu, 25 Apr 2024 14:56:22 -0400 Subject: [PATCH 346/755] Added a naive implementation of a
    inserter Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 426067bf6..8b1f11f33 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -24,7 +24,15 @@ QString markdownToHTML(const QString& markdown) char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE); QString htmlStr(buffer); - + int first_pos = htmlStr.indexOf(""); + int img_pos = 0; + while( first_pos != -1 ) + { + img_pos = htmlStr.indexOf(""); + first_pos = htmlStr.indexOf("", first_pos+5); + } free(buffer); return htmlStr; From 2cfe482298826bf0bdb948cca65f72930e142859 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Thu, 25 Apr 2024 15:40:24 -0400 Subject: [PATCH 347/755] Formatting and fixed img_pos allowed as negative Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 8b1f11f33..31e026dc7 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -24,16 +24,20 @@ QString markdownToHTML(const QString& markdown) char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE); QString htmlStr(buffer); - int first_pos = htmlStr.indexOf(""); - int img_pos = 0; - while( first_pos != -1 ) - { - img_pos = htmlStr.indexOf(""); - first_pos = htmlStr.indexOf("", first_pos+5); - } + free(buffer); + + // Insert a breakpoint between a and tag as this can cause visual bugs + int first_pos = htmlStr.indexOf(""); + int img_pos; + while (first_pos != -1) { + img_pos = htmlStr.indexOf(" -1) // 5 is the size of the tag + htmlStr.insert(img_pos, "
    "); + + first_pos = htmlStr.indexOf("", first_pos + 5); + } return htmlStr; } \ No newline at end of file From e1ed317c137c2ea3f4f54cb5a1c65e9f309d3ad1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Apr 2024 00:16:48 +0300 Subject: [PATCH 348/755] Updated CheckComboBox to look more like QComboBox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 56 +++++++++++++------------ launcher/ui/widgets/CheckComboBox.h | 5 ++- launcher/ui/widgets/ModFilterWidget.cpp | 9 ---- launcher/ui/widgets/ModFilterWidget.h | 1 - 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 189f7ebb4..f8411dd79 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -26,6 +26,7 @@ #include #include #include +#include class CheckComboModel : public QIdentityProxyModel { Q_OBJECT @@ -70,12 +71,6 @@ class CheckComboModel : public QIdentityProxyModel { CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ") { - QLineEdit* lineEdit = new QLineEdit(this); - lineEdit->setReadOnly(true); - setLineEdit(lineEdit); - lineEdit->disconnect(this); - setInsertPolicy(QComboBox::NoInsert); - view()->installEventFilter(this); view()->window()->installEventFilter(this); view()->viewport()->installEventFilter(this); @@ -89,9 +84,9 @@ void CheckComboBox::setSourceModel(QAbstractItemModel* new_model) model()->disconnect(this); QComboBox::setModel(proxy); connect(this, QOverload::of(&QComboBox::activated), this, &CheckComboBox::toggleCheckState); - connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::updateCheckedItems); - connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::updateCheckedItems); - connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::updateCheckedItems); + connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::emitCheckedItemsChanged); + connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::emitCheckedItemsChanged); + connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::emitCheckedItemsChanged); } void CheckComboBox::hidePopup() @@ -100,15 +95,9 @@ void CheckComboBox::hidePopup() QComboBox::hidePopup(); } -void CheckComboBox::updateCheckedItems() +void CheckComboBox::emitCheckedItemsChanged() { - QStringList items = checkedItems(); - if (items.isEmpty()) - setEditText(defaultText()); - else - setEditText(items.join(separator())); - - emit checkedItemsChanged(items); + emit checkedItemsChanged(checkedItems()); } QString CheckComboBox::defaultText() const @@ -118,10 +107,7 @@ QString CheckComboBox::defaultText() const void CheckComboBox::setDefaultText(const QString& text) { - if (m_default_text != text) { - m_default_text = text; - updateCheckedItems(); - } + m_default_text = text; } QString CheckComboBox::separator() const @@ -131,10 +117,7 @@ QString CheckComboBox::separator() const void CheckComboBox::setSeparator(const QString& separator) { - if (m_separator != separator) { - m_separator = separator; - updateCheckedItems(); - } + m_separator = separator; } bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) @@ -157,6 +140,8 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) case QEvent::MouseButtonRelease: containerMousePress = false; break; + case QEvent::Wheel: + return receiver == this; default: break; } @@ -170,7 +155,7 @@ void CheckComboBox::toggleCheckState(int index) Qt::CheckState state = static_cast(value.toInt()); setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole); } - updateCheckedItems(); + emitCheckedItemsChanged(); } Qt::CheckState CheckComboBox::itemCheckState(int index) const @@ -198,4 +183,23 @@ void CheckComboBox::setCheckedItems(const QStringList& items) } } +void CheckComboBox::paintEvent(QPaintEvent*) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + QStringList items = checkedItems(); + if (items.isEmpty()) + opt.currentText = defaultText(); + else + opt.currentText = items.join(separator()); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} + #include "CheckComboBox.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h index 4d6daf5a4..876c6e3e1 100644 --- a/launcher/ui/widgets/CheckComboBox.h +++ b/launcher/ui/widgets/CheckComboBox.h @@ -49,8 +49,11 @@ class CheckComboBox : public QComboBox { signals: void checkedItemsChanged(const QStringList& items); + protected: + void paintEvent(QPaintEvent*) override; + private: - void updateCheckedItems(); + void emitCheckedItemsChanged(); bool eventFilter(QObject* receiver, QEvent* event) override; void toggleCheckState(int index); diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index ead573aee..b44a573d8 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -89,8 +89,6 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi ui->versions->setStyleSheet("combobox-popup: 0;"); ui->version->setStyleSheet("combobox-popup: 0;"); - ui->versions->installEventFilter(this); - ui->version->installEventFilter(this); connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); @@ -117,13 +115,6 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi prepareBasicFilter(); } -bool ModFilterWidget::eventFilter(QObject* obj, QEvent* evt) -{ - if ((obj != ui->versions && obj != ui->version) || evt->type() != QEvent::Wheel) - return QTabWidget::eventFilter(obj, evt); - return true; -} - auto ModFilterWidget::getFilter() -> std::shared_ptr { m_filter_changed = false; diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 35125c910..4358dd91d 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -85,7 +85,6 @@ class ModFilterWidget : public QTabWidget { public slots: void setCategories(const QList&); - bool eventFilter(QObject* obj, QEvent* evt); private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); From 3c9a207192e680262d44f25b509a77d74dd6dbe0 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Fri, 26 Apr 2024 22:04:50 +0300 Subject: [PATCH 349/755] Update launcher/ui/widgets/VariableSizedImageObject.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/widgets/VariableSizedImageObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index a4e0872cb..3dd9d5634 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -37,7 +37,7 @@ QSizeF VariableSizedImageObject::intrinsicSize(QTextDocument* doc, int posInDocu auto image = qvariant_cast(format.property(ImageData)); auto size = image.size(); - if (size.isEmpty()) // can't resize a empty image + if (size.isEmpty()) // can't resize an empty image return { size }; // calculate the new image size based on the properties From 6ecd2c7f314d0e5fe7c8ce18ab520e3a38e8d717 Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:51:23 -0400 Subject: [PATCH 350/755] Update launcher/Markdown.cpp Co-authored-by: Alexandru Ionut Tripon Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/Markdown.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 31e026dc7..f9081057c 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -26,18 +26,23 @@ QString markdownToHTML(const QString& markdown) QString htmlStr(buffer); free(buffer); - - // Insert a breakpoint between a and tag as this can cause visual bugs - int first_pos = htmlStr.indexOf(""); - int img_pos; - while (first_pos != -1) { - img_pos = htmlStr.indexOf(" -1) // 5 is the size of the tag - htmlStr.insert(img_pos, "
    "); + int pos = htmlStr.indexOf(""); + int imgPos; + while (pos != -1) { + pos = pos + 5; // 5 is the size of the tag + imgPos = htmlStr.indexOf("", first_pos + 5); + auto textBetween = htmlStr.mid(pos, imgPos - pos).trimmed(); // trim all white spaces + + if (textBetween.isEmpty()) + htmlStr.insert(pos, "
    "); + + pos = htmlStr.indexOf("", pos); } + return htmlStr; } \ No newline at end of file From a988415028fafb1414420573b13d4e1113efe03e Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:59:51 -0400 Subject: [PATCH 351/755] Pleasing the format check Co-authored-by: Alexandru Ionut Tripon Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/Markdown.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index f9081057c..1e7fb9f35 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -43,6 +43,5 @@ QString markdownToHTML(const QString& markdown) pos = htmlStr.indexOf("", pos); } - return htmlStr; } \ No newline at end of file From e3f55f68657f63c15933dc0fdde4319390600a98 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 26 Apr 2024 20:43:57 +0100 Subject: [PATCH 352/755] Use proxy style to force ActivateItemOnSingleClick off Signed-off-by: TheKodeToad --- launcher/CMakeLists.txt | 2 ++ launcher/ui/themes/HintOverrideProxyStyle.cpp | 30 ++++++++++++++++ launcher/ui/themes/HintOverrideProxyStyle.h | 34 +++++++++++++++++++ launcher/ui/themes/ITheme.cpp | 4 ++- launcher/ui/themes/SystemTheme.cpp | 7 +++- 5 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 launcher/ui/themes/HintOverrideProxyStyle.cpp create mode 100644 launcher/ui/themes/HintOverrideProxyStyle.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 99acf8fc5..0d74966a8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -827,6 +827,8 @@ SET(LAUNCHER_SOURCES ui/themes/DarkTheme.h ui/themes/ITheme.cpp ui/themes/ITheme.h + ui/themes/HintOverrideProxyStyle.cpp + ui/themes/HintOverrideProxyStyle.h ui/themes/SystemTheme.cpp ui/themes/SystemTheme.h ui/themes/IconTheme.cpp diff --git a/launcher/ui/themes/HintOverrideProxyStyle.cpp b/launcher/ui/themes/HintOverrideProxyStyle.cpp new file mode 100644 index 000000000..80e821349 --- /dev/null +++ b/launcher/ui/themes/HintOverrideProxyStyle.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2024 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "HintOverrideProxyStyle.h" + +int HintOverrideProxyStyle::styleHint(QStyle::StyleHint hint, + const QStyleOption* option, + const QWidget* widget, + QStyleHintReturn* returnData) const +{ + if (hint == QStyle::SH_ItemView_ActivateItemOnSingleClick) + return 0; + + return QProxyStyle::styleHint(hint, option, widget, returnData); +} diff --git a/launcher/ui/themes/HintOverrideProxyStyle.h b/launcher/ui/themes/HintOverrideProxyStyle.h new file mode 100644 index 000000000..09b296018 --- /dev/null +++ b/launcher/ui/themes/HintOverrideProxyStyle.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2024 TheKodeToad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +/// Used to override platform-specific behaviours which the launcher does work well with. +class HintOverrideProxyStyle : public QProxyStyle { + Q_OBJECT + public: + HintOverrideProxyStyle(QStyle* style) : QProxyStyle(style) {} + + int styleHint(QStyle::StyleHint hint, + const QStyleOption* option = nullptr, + const QWidget* widget = nullptr, + QStyleHintReturn* returnData = nullptr) const override; +}; diff --git a/launcher/ui/themes/ITheme.cpp b/launcher/ui/themes/ITheme.cpp index 316b0f2ed..046ae16b4 100644 --- a/launcher/ui/themes/ITheme.cpp +++ b/launcher/ui/themes/ITheme.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Tayou + * Copyright (C) 2024 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,12 +37,13 @@ #include #include #include "Application.h" +#include "HintOverrideProxyStyle.h" #include "rainbow.h" void ITheme::apply(bool) { APPLICATION->setStyleSheet(QString()); - QApplication::setStyle(QStyleFactory::create(qtTheme())); + QApplication::setStyle(new HintOverrideProxyStyle(QStyleFactory::create(qtTheme()))); if (hasColorScheme()) { QApplication::setPalette(colorScheme()); } diff --git a/launcher/ui/themes/SystemTheme.cpp b/launcher/ui/themes/SystemTheme.cpp index 7ad144c7a..cefe664db 100644 --- a/launcher/ui/themes/SystemTheme.cpp +++ b/launcher/ui/themes/SystemTheme.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Tayou + * Copyright (C) 2024 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +38,7 @@ #include #include #include +#include "HintOverrideProxyStyle.h" #include "ThemeManager.h" SystemTheme::SystemTheme() @@ -64,8 +66,11 @@ void SystemTheme::apply(bool initial) { // See https://github.com/MultiMC/Launcher/issues/1790 // or https://github.com/PrismLauncher/PrismLauncher/issues/490 - if (initial) + if (initial) { + QApplication::setStyle(new HintOverrideProxyStyle(QStyleFactory::create(qtTheme()))); return; + } + ITheme::apply(initial); } From 5348a90a15676ed178f790648d60f4d3c3c23cf1 Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Fri, 26 Apr 2024 18:21:17 -0400 Subject: [PATCH 353/755] Fix img tag allowing img.... Co-authored-by: TheKodeToad Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/Markdown.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 1e7fb9f35..4ee1c0d28 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -31,7 +31,7 @@ QString markdownToHTML(const QString& markdown) int imgPos; while (pos != -1) { pos = pos + 5; // 5 is the size of the tag - imgPos = htmlStr.indexOf(" Date: Sat, 27 Apr 2024 07:16:54 +0000 Subject: [PATCH 354/755] chore(deps): update hendrikmuhs/ccache-action action to v1.2.13 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3896550f..a4b103714 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,7 +160,7 @@ jobs: - name: Setup ccache if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug' - uses: hendrikmuhs/ccache-action@v1.2.12 + uses: hendrikmuhs/ccache-action@v1.2.13 with: key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }} From ccf0b01de796ae439a7811648e5805bbe29dc2e0 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 27 Apr 2024 10:18:04 +0200 Subject: [PATCH 355/755] chore!: make install_bundle work on linux, removed prebuilt tarballs Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 22 ---------------------- .github/workflows/trigger_release.yml | 4 ---- CMakeLists.txt | 6 +++++- launcher/CMakeLists.txt | 17 +++++++++-------- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3896550f..701812ffe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -490,14 +490,6 @@ jobs: ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY } - - name: Package (Linux) - if: runner.os == 'Linux' - run: | - cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} - for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt - cd ${{ env.INSTALL_DIR }} - tar --owner root --group root -czf ../PrismLauncher.tar.gz * - - name: Package (Linux, portable) if: runner.os == 'Linux' run: | @@ -590,13 +582,6 @@ jobs: name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-Setup.exe - - name: Upload binary tarball (Linux, Qt 5) - if: runner.os == 'Linux' && matrix.qt_ver != 6 - uses: actions/upload-artifact@v4 - with: - name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }} - path: PrismLauncher.tar.gz - - name: Upload binary tarball (Linux, portable, Qt 5) if: runner.os == 'Linux' && matrix.qt_ver != 6 uses: actions/upload-artifact@v4 @@ -604,13 +589,6 @@ jobs: name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-portable.tar.gz - - name: Upload binary tarball (Linux, Qt 6) - if: runner.os == 'Linux' && matrix.qt_ver !=5 - uses: actions/upload-artifact@v4 - with: - name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }} - path: PrismLauncher.tar.gz - - name: Upload binary tarball (Linux, portable, Qt 6) if: runner.os == 'Linux' && matrix.qt_ver != 5 uses: actions/upload-artifact@v4 diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index fa22c96d5..134281b2c 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -46,9 +46,7 @@ jobs: run: | mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.zip PrismLauncher-macOS-Legacy-${{ env.VERSION }}.zip @@ -92,11 +90,9 @@ jobs: draft: true prerelease: false files: | - PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage.zsync - PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e46bb605..5bf45be88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -417,7 +417,11 @@ elseif(UNIX) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR}) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}") - + + if (INSTALL_BUNDLE STREQUAL full) + set(PLUGIN_DEST_DIR "./plugins/") + endif() + if(Launcher_ManPage) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6") endif() diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 99acf8fc5..fc025521a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1492,7 +1492,6 @@ if(INSTALL_BUNDLE STREQUAL "full") CONFIGURATIONS Debug RelWithDebInfo "" DESTINATION ${PLUGIN_DEST_DIR} COMPONENT Runtime - PATTERN "*qopensslbackend*" EXCLUDE PATTERN "*qcertonlybackend*" EXCLUDE ) install( @@ -1503,14 +1502,16 @@ if(INSTALL_BUNDLE STREQUAL "full") REGEX "dd\\." EXCLUDE REGEX "_debug\\." EXCLUDE REGEX "\\.dSYM" EXCLUDE - PATTERN "*qopensslbackend*" EXCLUDE PATTERN "*qcertonlybackend*" EXCLUDE ) endif() - configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" - @ONLY - ) - install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime) + # ignore fixup_bundle on Linux + if (NOT (UNIX AND NOT APPLE)) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" + @ONLY + ) + install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime) + endif() endif() From 890fd0b09c1cabe136e51bae92a41270de7f093e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 28 Apr 2024 00:20:12 +0000 Subject: [PATCH 356/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/fd16bb6d3bcca96039b11aa52038fafeb6e4f4be' (2024-04-20) → 'github:nixos/nixpkgs/d6f6eb2a984f2ba9a366c31e4d36d65465683450' (2024-04-27) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/40e6053ecb65fcbf12863338a6dcefb3f55f1bf8' (2024-04-12) → 'github:cachix/pre-commit-hooks.nix/6fb82e44254d6a0ece014ec423cb62d92435336f' (2024-04-24) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 0fbfca9cc..24898ecba 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1713596654, - "narHash": "sha256-LJbHQQ5aX1LVth2ST+Kkse/DRzgxlVhTL1rxthvyhZc=", + "lastModified": 1714213793, + "narHash": "sha256-Yg5D5LhyAZvd3DZrQQfJAVK8K3TkUYKooFtH1ulM0mw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "fd16bb6d3bcca96039b11aa52038fafeb6e4f4be", + "rev": "d6f6eb2a984f2ba9a366c31e4d36d65465683450", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1712897695, - "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "lastModified": 1713954846, + "narHash": "sha256-RWFafuSb5nkWGu8dDbW7gVb8FOQOPqmX/9MlxUUDguw=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "rev": "6fb82e44254d6a0ece014ec423cb62d92435336f", "type": "github" }, "original": { From a5b059ebad1221d10eeb5d4b953817441fabfd1d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 29 Apr 2024 00:27:15 +0300 Subject: [PATCH 357/755] Fixed curseforge export Signed-off-by: Trial97 --- launcher/MMCZip.h | 18 ++++++++++++++---- .../modplatform/flame/FlamePackExportTask.cpp | 11 ++++++----- .../modrinth/ModrinthPackExportTask.cpp | 2 +- launcher/ui/dialogs/ExportInstanceDialog.cpp | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 43b4ab933..b28eb195c 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -154,7 +154,12 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q #if defined(LAUNCHER_APPLICATION) class ExportToZipTask : public Task { public: - ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) + ExportToZipTask(QString outputPath, + QDir dir, + QFileInfoList files, + QString destinationPrefix = "", + bool followSymlinks = false, + bool utf8Enabled = false) : m_output_path(outputPath) , m_output(outputPath) , m_dir(dir) @@ -163,10 +168,15 @@ class ExportToZipTask : public Task { , m_follow_symlinks(followSymlinks) { setAbortable(true); - m_output.setUtf8Enabled(true); + m_output.setUtf8Enabled(utf8Enabled); }; - ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) - : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){}; + ExportToZipTask(QString outputPath, + QString dir, + QFileInfoList files, + QString destinationPrefix = "", + bool followSymlinks = false, + bool utf8Enabled = false) + : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks, utf8Enabled){}; virtual ~ExportToZipTask() = default; diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 3a2028fd1..569181732 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -201,7 +201,7 @@ void FlamePackExportTask::makeApiRequest() << " reason: " << parseError.errorString(); qWarning() << *response; - failed(parseError.errorString()); + emitFailed(parseError.errorString()); return; } @@ -213,6 +213,7 @@ void FlamePackExportTask::makeApiRequest() if (dataArr.isEmpty()) { qWarning() << "No matches found for fingerprint search!"; + getProjectsInfo(); return; } for (auto match : dataArr) { @@ -243,9 +244,9 @@ void FlamePackExportTask::makeApiRequest() qDebug() << doc; } pendingHashes.clear(); + getProjectsInfo(); }); - connect(task.get(), &Task::finished, this, &FlamePackExportTask::getProjectsInfo); - connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::emitFailed); + connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::getProjectsInfo); task->start(); } @@ -279,7 +280,7 @@ void FlamePackExportTask::getProjectsInfo() qWarning() << "Error while parsing JSON response from CurseForge projects task at " << parseError.offset << " reason: " << parseError.errorString(); qWarning() << *response; - failed(parseError.errorString()); + emitFailed(parseError.errorString()); return; } @@ -333,7 +334,7 @@ void FlamePackExportTask::buildZip() setStatus(tr("Adding files...")); setProgress(4, 5); - auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true, false); zipTask->addExtraFile("manifest.json", generateIndex()); zipTask->addExtraFile("modlist.html", generateHTML()); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index c704708ad..7e52153b9 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -200,7 +200,7 @@ void ModrinthPackExportTask::buildZip() { setStatus(tr("Adding files...")); - auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true, true); zipTask->addExtraFile("modrinth.index.json", generateIndex()); zipTask->setExcludeFiles(resolvedFiles.keys()); diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index 703736d68..9f2b3ac42 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -146,7 +146,7 @@ void ExportInstanceDialog::doExport() return; } - auto task = makeShared(output, m_instance->instanceRoot(), files, "", true); + auto task = makeShared(output, m_instance->instanceRoot(), files, "", true, true); connect(task.get(), &Task::failed, this, [this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); From be8ac0b5bbd9cce5e8b36d625ca5628295004d1c Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Sat, 27 Apr 2024 10:35:26 +0200 Subject: [PATCH 358/755] fix: make portable builds bundle Qt Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 35 +++++++------ CMakeLists.txt | 10 +++- launcher/CMakeLists.txt | 82 ++++++++++++++++++++++++++++--- launcher/install_prereqs.cmake.in | 1 - 4 files changed, 103 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 701812ffe..11adfe68b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -210,7 +210,7 @@ jobs: - name: Install Qt (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 6 run: | - sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 + sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 qtwayland5 - name: Install host Qt (Windows MSVC arm64) if: runner.os == 'Windows' && matrix.architecture == 'arm64' @@ -490,20 +490,6 @@ jobs: ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY } - - name: Package (Linux, portable) - if: runner.os == 'Linux' - run: | - cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} - cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - - # workaround to make portable installs to work on fedora - mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib - cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib - - for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - cd ${{ env.INSTALL_PORTABLE_DIR }} - tar -czf ../PrismLauncher-portable.tar.gz * - - name: Package AppImage (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 shell: bash @@ -550,6 +536,25 @@ jobs: mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" + - name: Package (Linux, portable) + if: runner.os == 'Linux' + run: | + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_PORTABLE_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -DINSTALL_BUNDLE=full -G Ninja + cmake --install ${{ env.BUILD_DIR }} + cmake --install ${{ env.BUILD_DIR }} --component portable + + mkdir ${{ env.INSTALL_PORTABLE_DIR }}/lib + cp /lib/x86_64-linux-gnu/libbz2.so.1.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib + cp /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 ${{ env.INSTALL_PORTABLE_DIR }}/lib + cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib + cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_PORTABLE_DIR }}/lib + cp /usr/lib/x86_64-linux-gnu/libffi.so.7 ${{ env.INSTALL_PORTABLE_DIR }}/lib + mv ${{ env.INSTALL_PORTABLE_DIR }}/bin/*.so* ${{ env.INSTALL_PORTABLE_DIR }}/lib + + for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + cd ${{ env.INSTALL_PORTABLE_DIR }} + tar -czf ../PrismLauncher-portable.tar.gz * + ## # UPLOAD BUILDS ## diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bf45be88..63408ec21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -419,7 +419,15 @@ elseif(UNIX) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}") if (INSTALL_BUNDLE STREQUAL full) - set(PLUGIN_DEST_DIR "./plugins/") + set(PLUGIN_DEST_DIR "plugins") + set(BUNDLE_DEST_DIR ".") + set(RESOURCES_DEST_DIR ".") + + # Apps to bundle + set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${Launcher_APP_BINARY_NAME}") + + # directories to look for dependencies + set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() if(Launcher_ManPage) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index fc025521a..03ef3ae27 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1505,13 +1505,79 @@ if(INSTALL_BUNDLE STREQUAL "full") PATTERN "*qcertonlybackend*" EXCLUDE ) endif() - # ignore fixup_bundle on Linux - if (NOT (UNIX AND NOT APPLE)) - configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" - @ONLY - ) - install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime) + # Wayland support + if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-client") + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client" + CONFIGURATIONS Debug RelWithDebInfo "" + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + ) + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-client" + CONFIGURATIONS Release MinSizeRel + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + REGEX "dd\\." EXCLUDE + REGEX "_debug\\." EXCLUDE + REGEX "\\.dSYM" EXCLUDE + ) endif() + if(EXISTS "${QT_PLUGINS_DIR}/wayland-graphics-integration-server") + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server" + CONFIGURATIONS Debug RelWithDebInfo "" + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + ) + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-graphics-integration-server" + CONFIGURATIONS Release MinSizeRel + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + REGEX "dd\\." EXCLUDE + REGEX "_debug\\." EXCLUDE + REGEX "\\.dSYM" EXCLUDE + ) + endif() + if(EXISTS "${QT_PLUGINS_DIR}/wayland-decoration-client") + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client" + CONFIGURATIONS Debug RelWithDebInfo "" + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + ) + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-decoration-client" + CONFIGURATIONS Release MinSizeRel + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + REGEX "dd\\." EXCLUDE + REGEX "_debug\\." EXCLUDE + REGEX "\\.dSYM" EXCLUDE + ) + endif() + if(EXISTS "${QT_PLUGINS_DIR}/wayland-shell-integration") + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration" + CONFIGURATIONS Debug RelWithDebInfo "" + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + ) + install( + DIRECTORY "${QT_PLUGINS_DIR}/wayland-shell-integration" + CONFIGURATIONS Release MinSizeRel + DESTINATION ${PLUGIN_DEST_DIR} + COMPONENT Runtime + REGEX "dd\\." EXCLUDE + REGEX "_debug\\." EXCLUDE + REGEX "\\.dSYM" EXCLUDE + ) + endif() + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" + @ONLY + ) + install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime) endif() diff --git a/launcher/install_prereqs.cmake.in b/launcher/install_prereqs.cmake.in index e4408d161..acbce9650 100644 --- a/launcher/install_prereqs.cmake.in +++ b/launcher/install_prereqs.cmake.in @@ -1,5 +1,4 @@ set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@") - file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@") function(gp_resolved_file_type_override resolved_file type_var) if(resolved_file MATCHES "^/(usr/)?lib/libQt") From cb9a83320c70ebd6802651066251d31f8da59bf6 Mon Sep 17 00:00:00 2001 From: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:51:31 +0200 Subject: [PATCH 359/755] chore: make Qt 5 Linux use official Qt builds Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> --- .github/workflows/build.yml | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11adfe68b..6854fe5d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,6 +54,10 @@ jobs: include: - os: ubuntu-20.04 qt_ver: 5 + qt_host: linux + qt_arch: "" + qt_version: "5.12.8" + qt_modules: "" - os: ubuntu-20.04 qt_ver: 6 @@ -61,7 +65,6 @@ jobs: qt_arch: "" qt_version: "6.2.4" qt_modules: "qt5compat qtimageformats" - qt_tools: "" - os: windows-2022 name: "Windows-MinGW-w64" @@ -78,7 +81,6 @@ jobs: qt_arch: '' qt_version: '6.7.0' qt_modules: 'qt5compat qtimageformats' - qt_tools: '' - os: windows-2022 name: "Windows-MSVC-arm64" @@ -90,7 +92,6 @@ jobs: qt_arch: 'win64_msvc2019_arm64' qt_version: '6.7.0' qt_modules: 'qt5compat qtimageformats' - qt_tools: '' - os: macos-12 name: macOS @@ -100,7 +101,6 @@ jobs: qt_arch: '' qt_version: '6.7.0' qt_modules: 'qt5compat qtimageformats' - qt_tools: '' - os: macos-12 name: macOS-Legacy @@ -109,7 +109,6 @@ jobs: qt_host: mac qt_version: "5.15.2" qt_modules: "" - qt_tools: "" runs-on: ${{ matrix.os }} @@ -207,11 +206,6 @@ jobs: brew update brew install ninja extra-cmake-modules - - name: Install Qt (Linux) - if: runner.os == 'Linux' && matrix.qt_ver != 6 - run: | - sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 qtwayland5 - - name: Install host Qt (Windows MSVC arm64) if: runner.os == 'Windows' && matrix.architecture == 'arm64' uses: jurplel/install-qt-action@v3 @@ -223,20 +217,18 @@ jobs: target: "desktop" arch: "" modules: ${{ matrix.qt_modules }} - tools: ${{ matrix.qt_tools }} cache: ${{ inputs.is_qt_cached }} cache-key-prefix: host-qt-arm64-windows dir: ${{ github.workspace }}\HostQt set-env: false - - name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC) - if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '') + - name: Install Qt (macOS, Linux & Windows MSVC) + if: matrix.msystem == '' uses: jurplel/install-qt-action@v3 with: aqtversion: "==3.1.*" py7zrversion: ">=0.20.2" version: ${{ matrix.qt_version }} - host: ${{ matrix.qt_host }} target: "desktop" arch: ${{ matrix.qt_arch }} modules: ${{ matrix.qt_modules }} @@ -432,12 +424,6 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - cd ${{ env.INSTALL_DIR }} - if ("${{ matrix.qt_ver }}" -eq "5") - { - Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll - Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll - } cd ${{ github.workspace }} Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt From 48f3ca56bafcf1a7e10b865a910221eefe6b84d0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 30 Apr 2024 22:38:01 +0300 Subject: [PATCH 360/755] Fixed NetRequest not failing on sink init Signed-off-by: Trial97 --- launcher/net/NetRequest.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 728c0e077..526fe77a5 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -68,7 +68,8 @@ void NetRequest::executeTask() if (getState() == Task::State::AbortedByUser) { qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString(); - emitAborted(); + emit aborted(); + emit finished(); return; } @@ -85,10 +86,12 @@ void NetRequest::executeTask() break; case State::Inactive: case State::Failed: - emitFailed(); + emit failed("Failed to initilize sink"); + emit finished(); return; case State::AbortedByUser: - emitAborted(); + emit aborted(); + emit finished(); return; } From e06812037560036b2c1ade1e75b06fce1f8a13bf Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 30 Apr 2024 22:56:03 +0300 Subject: [PATCH 361/755] Fix invalid characters filename download on windows Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameInstanceCreationTask.cpp | 7 ++++++- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 8 ++++++-- launcher/net/HttpMetaCache.cpp | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index ef552c3c2..a1f10c156 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -537,7 +537,12 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) selectedOptionalMods = optionalModDialog.getResult(); } for (const auto& result : results) { - auto relpath = FS::PathCombine(result.targetFolder, result.fileName); + auto fileName = result.fileName; +#ifdef Q_OS_WIN + fileName = FS::RemoveInvalidPathChars(fileName); +#endif + auto relpath = FS::PathCombine(result.targetFolder, fileName); + if (!result.required && !selectedOptionalMods.contains(relpath)) { relpath += ".disabled"; } diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index a688cea87..3b875103b 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -240,11 +240,15 @@ bool ModrinthCreationTask::createInstance() auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path); for (auto file : m_files) { - auto file_path = FS::PathCombine(root_modpack_path, file.path); + auto fileName = file.path; +#ifdef Q_OS_WIN + fileName = FS::RemoveInvalidPathChars(fileName); +#endif + auto file_path = FS::PathCombine(root_modpack_path, fileName); if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) { // This means we somehow got out of the root folder, so abort here to prevent exploits setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") - .arg(file.path)); + .arg(fileName)); return false; } diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index f37bc0bf8..648155412 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -84,6 +84,9 @@ auto HttpMetaCache::getEntry(QString base, QString resource_path) -> MetaEntryPt auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr { +#ifdef Q_OS_WIN + resource_path = FS::RemoveInvalidPathChars(resource_path); +#endif auto entry = getEntry(base, resource_path); // it's not present? generate a default stale entry if (!entry) { From 5400c24c7324213a1af102c96e8bd6f9be672006 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 30 Apr 2024 23:04:50 +0300 Subject: [PATCH 362/755] Ensure valid file names for resource names Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameModIndex.cpp | 3 +++ launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 345883c17..70d07c201 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -138,6 +138,9 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.version = Json::requireString(obj, "displayName"); file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); +#ifdef Q_OS_WIN + file.fileName = FS::RemoveInvalidPathChars(file.fileName); +#endif ModPlatform::IndexedVersionType::VersionType ver_type; switch (Json::requireInteger(obj, "releaseType")) { diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index c1c30ab5f..561976dc3 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -226,6 +226,9 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t if (parent.contains("url")) { file.downloadUrl = Json::requireString(parent, "url"); file.fileName = Json::requireString(parent, "filename"); +#ifdef Q_OS_WIN + file.fileName = FS::RemoveInvalidPathChars(file.fileName); +#endif file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1); auto hash_list = Json::requireObject(parent, "hashes"); From 30ce8ecb5ff08bcec110b3b291025cfd72d4d4fe Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 30 Apr 2024 23:20:24 +0300 Subject: [PATCH 363/755] Fixed imports Signed-off-by: Trial97 --- launcher/modplatform/flame/FlameModIndex.cpp | 1 + launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 70d07c201..83a28fa2b 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -1,5 +1,6 @@ #include "FlameModIndex.h" +#include "FileSystem.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 561976dc3..4671a330d 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -17,6 +17,7 @@ */ #include "ModrinthPackIndex.h" +#include "FileSystem.h" #include "ModrinthAPI.h" #include "Json.h" From cec27fd014c2eb3177fad58384a74b0aa0cf37c1 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Tue, 30 Apr 2024 22:40:04 -0400 Subject: [PATCH 364/755] Moved html patch to StringUtils Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 18 +----------------- launcher/StringUtils.cpp | 20 ++++++++++++++++++++ launcher/StringUtils.h | 2 ++ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 4ee1c0d28..a624791df 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -26,22 +26,6 @@ QString markdownToHTML(const QString& markdown) QString htmlStr(buffer); free(buffer); - - int pos = htmlStr.indexOf(""); - int imgPos; - while (pos != -1) { - pos = pos + 5; // 5 is the size of the tag - imgPos = htmlStr.indexOf(""); - - pos = htmlStr.indexOf("", pos); - } - + return htmlStr; } \ No newline at end of file diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 72ccdfbff..db53f353e 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -212,3 +212,23 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular right = s.mid(end); return qMakePair(left, right); } + +QString StringUtils::htmlListPatch(QString htmlStr) +{ + int pos = htmlStr.indexOf(""); + int imgPos; + while (pos != -1) { + pos = pos + 5; // 5 is the size of the tag + imgPos = htmlStr.indexOf(""); + + pos = htmlStr.indexOf("", pos); + } + return htmlStr; +} \ No newline at end of file diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 9d2bdd85e..624ee41a3 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -85,4 +85,6 @@ QPair splitFirst(const QString& s, const QString& sep, Qt::Cas QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); QPair splitFirst(const QString& s, const QRegularExpression& re); +QString htmlListPatch(QString htmlStr); + } // namespace StringUtils From e3d64608b947eeb774a4ff317c3984bcac8d83be Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Tue, 30 Apr 2024 23:28:50 -0400 Subject: [PATCH 365/755] Switched to a Regex expression Signed-off-by: SabrePenguin --- launcher/StringUtils.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index db53f353e..125b8ca6f 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -215,11 +215,13 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular QString StringUtils::htmlListPatch(QString htmlStr) { - int pos = htmlStr.indexOf(""); - int imgPos; + QRegularExpression match("|"); + int pos = htmlStr.indexOf(match); + int imgPos, dist; while (pos != -1) { - pos = pos + 5; // 5 is the size of the tag - imgPos = htmlStr.indexOf("", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index + pos = pos + dist; + imgPos = htmlStr.indexOf(""); - pos = htmlStr.indexOf("", pos); + pos = htmlStr.indexOf(match, pos); } return htmlStr; } \ No newline at end of file From b9c010ffb2bcf8c08a38014079064eeba0af2fd9 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Tue, 30 Apr 2024 23:43:36 -0400 Subject: [PATCH 366/755] Added patch to all setHtml calls Signed-off-by: SabrePenguin --- launcher/ui/dialogs/AboutDialog.cpp | 5 +++-- launcher/ui/dialogs/ExportToModListDialog.cpp | 5 +++-- launcher/ui/dialogs/ModUpdateDialog.cpp | 3 ++- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 3 ++- launcher/ui/pages/instance/ManagedPackPage.cpp | 8 +++++--- launcher/ui/pages/modplatform/ResourcePage.cpp | 5 +++-- launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp | 3 ++- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 3 ++- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 7 +++++-- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 3 ++- launcher/ui/pages/modplatform/technic/TechnicPage.cpp | 3 ++- launcher/updater/prismupdater/UpdaterDialogs.cpp | 3 ++- 12 files changed, 33 insertions(+), 18 deletions(-) diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 17b79ecaa..40c852c37 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -39,6 +39,7 @@ #include "BuildConfig.h" #include "Markdown.h" #include "ui_AboutDialog.h" +#include "StringUtils.h" #include #include @@ -139,10 +140,10 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia setWindowTitle(tr("About %1").arg(launcherName)); QString chtml = getCreditsHtml(); - ui->creditsText->setHtml(chtml); + ui->creditsText->setHtml(StringUtils::htmlListPatch(chtml)); QString lhtml = getLicenseHtml(); - ui->licenseText->setHtml(lhtml); + ui->licenseText->setHtml(StringUtils::htmlListPatch(lhtml)); ui->urlLabel->setOpenExternalLinks(true); diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index a343f555a..ab85cf584 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -26,6 +26,7 @@ #include "minecraft/mod/ModFolderModel.h" #include "modplatform/helpers/ExportToModList.h" #include "ui_ExportToModListDialog.h" +#include "StringUtils.h" #include #include @@ -143,10 +144,10 @@ void ExportToModListDialog::triggerImp() case ExportToModList::CUSTOM: return; case ExportToModList::HTML: - ui->resultText->setHtml(txt); + ui->resultText->setHtml(StringUtils::htmlListPatch(txt)); break; case ExportToModList::MARKDOWN: - ui->resultText->setHtml(markdownToHTML(txt)); + ui->resultText->setHtml(StringUtils::htmlListPatch(markdownToHTML(txt))); break; case ExportToModList::PLAINTXT: break; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 54893d775..a08b36ec1 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -7,6 +7,7 @@ #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" #include "ui_ReviewMessageBox.h" +#include "StringUtils.h" #include "Markdown.h" @@ -473,7 +474,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri break; } - changelog_area->setHtml(text); + changelog_area->setHtml(StringUtils::htmlListPatch(text)); changelog_area->setOpenExternalLinks(true); changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 5eebe87a3..797b763c2 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -26,6 +26,7 @@ #include "BuildConfig.h" #include "Markdown.h" #include "ui_UpdateAvailableDialog.h" +#include "StringUtils.h" UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, const QString& availableVersion, @@ -43,7 +44,7 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64)); auto releaseNotesHtml = markdownToHTML(releaseNotes); - ui->releaseNotes->setHtml(releaseNotesHtml); + ui->releaseNotes->setHtml(StringUtils::htmlListPatch(releaseNotesHtml)); ui->releaseNotes->setOpenExternalLinks(true); connect(ui->skipButton, &QPushButton::clicked, this, [this]() { diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 2210d0263..9c3fcc354 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -20,6 +20,7 @@ #include "InstanceTask.h" #include "Json.h" #include "Markdown.h" +#include "StringUtils.h" #include "modplatform/modrinth/ModrinthPackManifest.h" @@ -332,7 +333,7 @@ void ModrinthManagedPackPage::suggestVersion() } auto version = m_pack.versions.at(index); - ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8())); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(markdownToHTML(version.changelog.toUtf8()))); ManagedPackPage::suggestVersion(); } @@ -420,7 +421,7 @@ void FlameManagedPackPage::parseManagedPack() "Don't worry though, it will ask you to update this instance instead, so you'll not lose this instance!" ""); - ui->changelogTextBrowser->setHtml(message); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(message)); return; } @@ -502,7 +503,8 @@ void FlameManagedPackPage::suggestVersion() } auto version = m_pack.versions.at(index); - ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId)); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch( + m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId))); ManagedPackPage::suggestVersion(); } diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index ae48e5523..b9c706c6c 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -45,6 +45,7 @@ #include "Markdown.h" +#include "StringUtils.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ResourceModel.h" #include "ui/widgets/ProjectItem.h" @@ -234,8 +235,8 @@ void ResourcePage::updateUi() text += "
    "; - m_ui->packDescription->setHtml( - text + (current_pack->extraData.body.isEmpty() ? current_pack->description : markdownToHTML(current_pack->extraData.body))); + m_ui->packDescription->setHtml(StringUtils::htmlListPatch( + text + (current_pack->extraData.body.isEmpty() ? current_pack->description : markdownToHTML(current_pack->extraData.body)))); m_ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index e492830c6..d79b7621a 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -39,6 +39,7 @@ #include "ui_AtlPage.h" #include "BuildConfig.h" +#include "StringUtils.h" #include "AtlUserInteractionSupportImpl.h" #include "modplatform/atlauncher/ATLPackInstallTask.h" @@ -144,7 +145,7 @@ void AtlPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex selected = filterModel->data(first, Qt::UserRole).value(); - ui->packDescription->setHtml(selected.description.replace("\n", "
    ")); + ui->packDescription->setHtml(StringUtils::htmlListPatch(selected.description.replace("\n", "
    "))); for (const auto& version : selected.versions) { ui->versionSelectionBox->addItem(version.version); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f1fd9b5d8..e851e47be 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -46,6 +46,7 @@ #include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" +#include "StringUtils.h" #include "net/ApiDownload.h" @@ -292,6 +293,6 @@ void FlamePage::updateUi() text += "
    "; text += api.getModDescription(current.addonId).toUtf8(); - ui->packDescription->setHtml(text + current.description); + ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description)); ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 0ecaf4625..683d2e81d 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -37,6 +37,7 @@ #include "Page.h" #include "ui/widgets/ProjectItem.h" #include "ui_Page.h" +#include "StringUtils.h" #include @@ -260,8 +261,10 @@ void Page::onPackSelectionChanged(Modpack* pack) { ui->versionSelectionBox->clear(); if (pack) { - currentModpackInfo->setHtml("Pack by " + pack->author + "" + "
    Minecraft " + pack->mcVersion + "
    " + "
    " + - pack->description + "
    • " + pack->mods.replace(";", "
    • ") + "
    "); + currentModpackInfo->setHtml(StringUtils::htmlListPatch( + "Pack by " + pack->author + "" + "
    Minecraft " + pack->mcVersion + + "
    " + "
    " + + pack->description + "
    • " + pack->mods.replace(";", "
    • ") + "
    ")); bool currentAdded = false; for (int i = 0; i < pack->oldVersions.size(); i++) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index da5fe1e7b..b44afda7e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -44,6 +44,7 @@ #include "InstanceImportTask.h" #include "Json.h" #include "Markdown.h" +#include "StringUtils.h" #include "ui/widgets/ProjectItem.h" @@ -304,7 +305,7 @@ void ModrinthPage::updateUI() text += markdownToHTML(current.extra.body.toUtf8()); - ui->packDescription->setHtml(text + current.description); + ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description)); ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 6b1ec8cb5..72b7814c9 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -47,6 +47,7 @@ #include "TechnicModel.h" #include "modplatform/technic/SingleZipPackInstallTask.h" #include "modplatform/technic/SolderPackInstallTask.h" +#include "StringUtils.h" #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" @@ -233,7 +234,7 @@ void TechnicPage::metadataLoaded() text += "

    "; - ui->packDescription->setHtml(text + current.description); + ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description)); // Strip trailing forward-slashes from Solder URL's if (current.isSolder) { diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 395b658db..06dc161b1 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -26,6 +26,7 @@ #include #include "Markdown.h" +#include "StringUtils.h" SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const QList& releases, QWidget* parent) : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) @@ -96,7 +97,7 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget QString body = markdownToHTML(release.body.toUtf8()); m_selectedRelease = release; - ui->changelogTextBrowser->setHtml(body); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(body)); } SelectReleaseAssetDialog::SelectReleaseAssetDialog(const QList& assets, QWidget* parent) From 51f4ede7977c3bb6d0d2f3866d3faf57f3b43dab Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Wed, 1 May 2024 00:11:53 -0400 Subject: [PATCH 367/755] Fixing CI format issues Signed-off-by: SabrePenguin --- launcher/ui/dialogs/AboutDialog.cpp | 2 +- launcher/ui/dialogs/ExportToModListDialog.cpp | 2 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 2 +- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 2 +- launcher/ui/pages/instance/ManagedPackPage.cpp | 4 ++-- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 9 ++++----- launcher/ui/pages/modplatform/technic/TechnicPage.cpp | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 40c852c37..b652ba991 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -38,8 +38,8 @@ #include "Application.h" #include "BuildConfig.h" #include "Markdown.h" -#include "ui_AboutDialog.h" #include "StringUtils.h" +#include "ui_AboutDialog.h" #include #include diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index ab85cf584..7debf0cd8 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -22,11 +22,11 @@ #include #include "FileSystem.h" #include "Markdown.h" +#include "StringUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/helpers/ExportToModList.h" #include "ui_ExportToModListDialog.h" -#include "StringUtils.h" #include #include diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index a08b36ec1..6649bee4e 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -3,11 +3,11 @@ #include "CustomMessageBox.h" #include "ProgressDialog.h" #include "ScrollMessageBox.h" +#include "StringUtils.h" #include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" #include "ui_ReviewMessageBox.h" -#include "StringUtils.h" #include "Markdown.h" diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 797b763c2..810a1f089 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -25,8 +25,8 @@ #include "Application.h" #include "BuildConfig.h" #include "Markdown.h" -#include "ui_UpdateAvailableDialog.h" #include "StringUtils.h" +#include "ui_UpdateAvailableDialog.h" UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, const QString& availableVersion, diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 9c3fcc354..a47403926 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -503,8 +503,8 @@ void FlameManagedPackPage::suggestVersion() } auto version = m_pack.versions.at(index); - ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch( - m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId))); + ui->changelogTextBrowser->setHtml( + StringUtils::htmlListPatch(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId))); ManagedPackPage::suggestVersion(); } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index e851e47be..d3473412a 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -43,10 +43,10 @@ #include "FlameModel.h" #include "InstanceImportTask.h" #include "Json.h" +#include "StringUtils.h" #include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" -#include "StringUtils.h" #include "net/ApiDownload.h" diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 683d2e81d..80345883c 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -35,9 +35,9 @@ */ #include "Page.h" +#include "StringUtils.h" #include "ui/widgets/ProjectItem.h" #include "ui_Page.h" -#include "StringUtils.h" #include @@ -261,10 +261,9 @@ void Page::onPackSelectionChanged(Modpack* pack) { ui->versionSelectionBox->clear(); if (pack) { - currentModpackInfo->setHtml(StringUtils::htmlListPatch( - "Pack by " + pack->author + "" + "
    Minecraft " + pack->mcVersion + - "
    " + "
    " + - pack->description + "
    • " + pack->mods.replace(";", "
    • ") + "
    ")); + currentModpackInfo->setHtml(StringUtils::htmlListPatch("Pack by " + pack->author + "" + "
    Minecraft " + pack->mcVersion + + "
    " + "
    " + + pack->description + "
    • " + pack->mods.replace(";", "
    • ") + "
    ")); bool currentAdded = false; for (int i = 0; i < pack->oldVersions.size(); i++) { diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 72b7814c9..391c10122 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -44,10 +44,10 @@ #include "BuildConfig.h" #include "Json.h" +#include "StringUtils.h" #include "TechnicModel.h" #include "modplatform/technic/SingleZipPackInstallTask.h" #include "modplatform/technic/SolderPackInstallTask.h" -#include "StringUtils.h" #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" From 814f84efab9af2fa59156d3e6101ce7fd8cffcb9 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Wed, 1 May 2024 00:21:32 -0400 Subject: [PATCH 368/755] Fixed more clang formatting Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 2 +- launcher/StringUtils.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index a624791df..426067bf6 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -26,6 +26,6 @@ QString markdownToHTML(const QString& markdown) QString htmlStr(buffer); free(buffer); - + return htmlStr; } \ No newline at end of file diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 125b8ca6f..e05affde1 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -219,7 +219,7 @@ QString StringUtils::htmlListPatch(QString htmlStr) int pos = htmlStr.indexOf(match); int imgPos, dist; while (pos != -1) { - dist = htmlStr.indexOf(">", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index + dist = htmlStr.indexOf(">", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index pos = pos + dist; imgPos = htmlStr.indexOf("versionSelectionBox->clear(); if (pack) { currentModpackInfo->setHtml(StringUtils::htmlListPatch("Pack by " + pack->author + "" + "
    Minecraft " + pack->mcVersion + - "
    " + "
    " + - pack->description + "
    • " + pack->mods.replace(";", "
    • ") + "
    ")); + "
    " + "
    " + pack->description + "
    • " + + pack->mods.replace(";", "
    • ") + "
    ")); bool currentAdded = false; for (int i = 0; i < pack->oldVersions.size(); i++) { From ce873e4a0dd517fd68cb49bab037362c9616672b Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Wed, 1 May 2024 12:49:34 -0400 Subject: [PATCH 369/755] Regex correction Co-authored-by: TheKodeToad Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/StringUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index e05affde1..82147c16a 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -215,7 +215,7 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular QString StringUtils::htmlListPatch(QString htmlStr) { - QRegularExpression match("|"); + QRegularExpression match("<\\s/\\s*ul\\s*>"); int pos = htmlStr.indexOf(match); int imgPos, dist; while (pos != -1) { From 0c76e7ab20f24ecc0a804732aca6ccf3ebd1e0c3 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Wed, 1 May 2024 13:00:55 -0400 Subject: [PATCH 370/755] Made Regex static and const, removed dist Signed-off-by: SabrePenguin --- launcher/StringUtils.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 82147c16a..edda9f247 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -213,14 +213,14 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular return qMakePair(left, right); } +static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>"); + QString StringUtils::htmlListPatch(QString htmlStr) { - QRegularExpression match("<\\s/\\s*ul\\s*>"); - int pos = htmlStr.indexOf(match); - int imgPos, dist; + int pos = htmlStr.indexOf(ulMatcher); + int imgPos; while (pos != -1) { - dist = htmlStr.indexOf(">", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index - pos = pos + dist; + pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the tag. Add one for zeroeth index imgPos = htmlStr.indexOf(""); - pos = htmlStr.indexOf(match, pos); + pos = htmlStr.indexOf(ulMatcher, pos); } return htmlStr; } \ No newline at end of file From 5d36ed9cf99d202aaceb63acaae27cd260192f01 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 5 May 2024 00:19:38 +0000 Subject: [PATCH 371/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/9126214d0a59633752a136528f5f3b9aa8565b7d' (2024-04-01) → 'github:hercules-ci/flake-parts/e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e' (2024-05-02) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/d6f6eb2a984f2ba9a366c31e4d36d65465683450' (2024-04-27) → 'github:nixos/nixpkgs/5fd8536a9a5932d4ae8de52b7dc08d92041237fc' (2024-05-03) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/6fb82e44254d6a0ece014ec423cb62d92435336f' (2024-04-24) → 'github:cachix/pre-commit-hooks.nix/2849da033884f54822af194400f8dff435ada242' (2024-04-30) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 24898ecba..78f774342 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1714641030, + "narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e", "type": "github" }, "original": { @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1714213793, - "narHash": "sha256-Yg5D5LhyAZvd3DZrQQfJAVK8K3TkUYKooFtH1ulM0mw=", + "lastModified": 1714750952, + "narHash": "sha256-oOUdvPrO8CbupgDSaPou+Jv6GL+uQA2QlE33D7OLzkM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d6f6eb2a984f2ba9a366c31e4d36d65465683450", + "rev": "5fd8536a9a5932d4ae8de52b7dc08d92041237fc", "type": "github" }, "original": { @@ -122,11 +122,11 @@ ] }, "locked": { - "lastModified": 1713954846, - "narHash": "sha256-RWFafuSb5nkWGu8dDbW7gVb8FOQOPqmX/9MlxUUDguw=", + "lastModified": 1714478972, + "narHash": "sha256-q//cgb52vv81uOuwz1LaXElp3XAe1TqrABXODAEF6Sk=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "6fb82e44254d6a0ece014ec423cb62d92435336f", + "rev": "2849da033884f54822af194400f8dff435ada242", "type": "github" }, "original": { From 8e0af16de979ba92e9983b47c44ee0f321fcb116 Mon Sep 17 00:00:00 2001 From: Samuel Stidham Date: Sun, 5 May 2024 19:53:18 -0400 Subject: [PATCH 372/755] Add extra java locations for MacOs. Signed-off-by: Samuel Stidham --- launcher/java/JavaUtils.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 3627cec39..b996bf046 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -362,6 +362,12 @@ QList JavaUtils::FindJavaPaths() javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); } + + auto home = qEnvironmentVariable("HOME"); + + // javas downloaded by sdkman + scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); + javas.append(getMinecraftJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); From adf0cfdebf2d27148139da2965900a9b3c1ec389 Mon Sep 17 00:00:00 2001 From: Samuel Stidham Date: Sun, 5 May 2024 20:01:29 -0400 Subject: [PATCH 373/755] Fixed the code. Signed-off-by: Samuel Stidham --- launcher/java/JavaUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index b996bf046..e767eff89 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -366,7 +366,7 @@ QList JavaUtils::FindJavaPaths() auto home = qEnvironmentVariable("HOME"); // javas downloaded by sdkman - scanJavaDirs(FS::PathCombine(home, ".sdkman/candidates/java")); + javas.append(FS::PathCombine(home, ".sdkman/candidates/java")); javas.append(getMinecraftJavaBundle()); javas = addJavasFromEnv(javas); From 47871416982836e282e2da373a317d3df0cf4f38 Mon Sep 17 00:00:00 2001 From: Kenneth Chew <79120643+kthchew@users.noreply.github.com> Date: Tue, 7 May 2024 18:22:23 -0400 Subject: [PATCH 374/755] Improved blocked mods dialog permissions prompt Targeted to macOS (where `~/Downloads` is considered a sensitive location that the system protects behind a prompt even from non-sandboxed apps) Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com> --- cmake/MacOSXBundleInfo.plist.in | 2 ++ launcher/ui/dialogs/BlockedModsDialog.cpp | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in index d36ac3e8f..c439efe25 100644 --- a/cmake/MacOSXBundleInfo.plist.in +++ b/cmake/MacOSXBundleInfo.plist.in @@ -6,6 +6,8 @@ A Minecraft mod wants to access your camera. NSMicrophoneUsageDescription A Minecraft mod wants to access your microphone. + NSDownloadsFolderUsageDescription + Prism uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where Prism scans for downloaded mods in Settings or the prompt that appears. NSPrincipalClass NSApplication NSHighResolutionCapable diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index 7a5a16818..df351e845 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -40,6 +40,7 @@ #include #include #include +#include BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList& mods, QString hash_type) : QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods), m_hash_type(hash_type) @@ -60,8 +61,13 @@ BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, cons qDebug() << "[Blocked Mods Dialog] Mods List: " << mods; - setupWatch(); - scanPaths(); + // defer setup of file system watchers until after the dialog is shown + // this allows OS (namely macOS) permission prompts to show after the relevant dialog appears + QTimer::singleShot(0, this, [this] { + setupWatch(); + scanPaths(); + update(); + }); this->setWindowTitle(title); ui->labelDescription->setText(text); @@ -194,6 +200,10 @@ void BlockedModsDialog::setupWatch() void BlockedModsDialog::watchPath(QString path, bool watch_recursive) { auto to_watch = QFileInfo(path); + if (!to_watch.isReadable()) { + qWarning() << "[Blocked Mods Dialog] Failed to add Watch Path (unable to read):" << path; + return; + } auto to_watch_path = to_watch.canonicalFilePath(); if (m_watcher.directories().contains(to_watch_path)) return; // don't watch the same path twice (no loops!) From e7c95c9ccb92474cbbaf125814d3085a63b2b746 Mon Sep 17 00:00:00 2001 From: Kenneth Chew <79120643+kthchew@users.noreply.github.com> Date: Tue, 7 May 2024 18:57:04 -0400 Subject: [PATCH 375/755] Fix bug where watched directories are not clickable on macOS Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com> --- launcher/ui/dialogs/BlockedModsDialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index df351e845..2b415c2d9 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -164,7 +164,8 @@ void BlockedModsDialog::update() QString watching; for (auto& dir : m_watcher.directories()) { - watching += QString("%1
    ").arg(dir); + QUrl fileURL = QUrl::fromLocalFile(dir); + watching += QString("%2
    ").arg(fileURL.toString(), dir); } ui->textBrowserWatched->setText(watching); From a95c1768a778f3ade8d61fef4c636a21a55bb4fe Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 8 May 2024 14:16:12 +0300 Subject: [PATCH 376/755] Add neoforge support for technic packs and atlauncher packs Signed-off-by: Trial97 --- .../atlauncher/ATLPackInstallTask.cpp | 6 +++++ .../technic/TechnicPackProcessor.cpp | 23 +++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 8ae8145de..01de88b04 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -1031,6 +1031,12 @@ void PackInstallTask::install() return; components->setComponentVersion("net.minecraftforge", version); + } else if (m_version.loader.type == QString("neoforge")) { + auto version = getVersionForLoader("net.neoforged"); + if (version == Q_NULLPTR) + return; + + components->setComponentVersion("net.neoforged", version); } else if (m_version.loader.type == QString("fabric")) { auto version = getVersionForLoader("net.fabricmc.fabric-loader"); if (version == Q_NULLPTR) diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp index 90f59ce54..a47a4811f 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.cpp +++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -155,8 +155,26 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, auto libraryObject = Json::ensureObject(library, {}, ""); auto libraryName = Json::ensureString(libraryObject, "name", "", ""); - if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && - libraryName.contains('-')) { + if (libraryName.startsWith("net.neoforged.fancymodloader:")) { // it is neoforge + // no easy way to get the version from the libs so use the arguments + auto arguments = Json::ensureObject(root, "arguments", {}); + bool isVersionArg = false; + QString neoforgeVersion; + for (auto arg : Json::ensureArray(arguments, "game", {})) { + auto argument = Json::ensureString(arg, ""); + if (isVersionArg) { + neoforgeVersion = argument; + break; + } else { + isVersionArg = "--fml.neoForgeVersion" == argument || "--fml.forgeVersion" == argument; + } + } + if (!neoforgeVersion.isEmpty()) { + components->setComponentVersion("net.neoforged", neoforgeVersion); + } + break; + } else if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && + libraryName.contains('-')) { QString libraryVersion = libraryName.section(':', 2); if (!libraryVersion.startsWith("1.7.10-")) { components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1)); @@ -164,6 +182,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, // 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1)); } + break; } else { // -> static QMap loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" }, From d795ba25e745912c9772bd084eb98f5f039d041d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 May 2024 20:55:26 +0300 Subject: [PATCH 377/755] Delete instaces tmp diectory on startup Signed-off-by: Trial97 --- launcher/Application.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index bb8751ccc..f696d9022 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1209,6 +1209,12 @@ void Application::performMainStartupAction() qDebug() << "<> Updater started."; } + { // delete instances tmp dirctory + auto instDir = m_settings->get("InstanceDir").toString(); + const QString tempRoot = FS::PathCombine(instDir, ".tmp"); + FS::deletePath(tempRoot); + } + if (!m_urlsToImport.isEmpty()) { qDebug() << "<> Importing from url:" << m_urlsToImport; m_mainWindow->processURLs(m_urlsToImport); From d74926e60dd879d0f5b0dab307cf8b9259fcbaf7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 12 May 2024 00:20:20 +0000 Subject: [PATCH 378/755] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/5fd8536a9a5932d4ae8de52b7dc08d92041237fc' (2024-05-03) → 'github:nixos/nixpkgs/e4e7a43a9db7e22613accfeb1005cca1b2b1ee0d' (2024-05-11) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 78f774342..740d5c43e 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1714750952, - "narHash": "sha256-oOUdvPrO8CbupgDSaPou+Jv6GL+uQA2QlE33D7OLzkM=", + "lastModified": 1715413075, + "narHash": "sha256-FCi3R1MeS5bVp0M0xTheveP6hhcCYfW/aghSTPebYL4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "5fd8536a9a5932d4ae8de52b7dc08d92041237fc", + "rev": "e4e7a43a9db7e22613accfeb1005cca1b2b1ee0d", "type": "github" }, "original": { From 3336f8107c48e8d0c85e45fcc2e0ca46db25fc10 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 13 May 2024 23:25:08 +0300 Subject: [PATCH 379/755] Removed AuthRequest and NetAction Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 4 - launcher/InstanceCreationTask.cpp | 2 - launcher/InstanceCreationTask.h | 2 +- launcher/minecraft/AssetsUtils.cpp | 3 +- launcher/minecraft/AssetsUtils.h | 4 +- launcher/minecraft/Library.cpp | 11 +- launcher/minecraft/Library.h | 10 +- launcher/minecraft/auth/AccountList.cpp | 5 +- launcher/minecraft/auth/AuthRequest.cpp | 175 ------------------ launcher/minecraft/auth/AuthRequest.h | 67 ------- launcher/minecraft/auth/AuthStep.cpp | 2 - launcher/minecraft/auth/AuthStep.h | 3 +- launcher/minecraft/auth/MinecraftAccount.cpp | 2 +- launcher/minecraft/auth/flows/Offline.cpp | 5 - launcher/minecraft/auth/flows/Offline.h | 6 - .../minecraft/auth/steps/EntitlementsStep.cpp | 50 ++--- .../minecraft/auth/steps/EntitlementsStep.h | 12 +- launcher/minecraft/auth/steps/GetSkinStep.cpp | 34 ++-- launcher/minecraft/auth/steps/GetSkinStep.h | 12 +- .../auth/steps/LauncherLoginStep.cpp | 54 +++--- .../minecraft/auth/steps/LauncherLoginStep.h | 12 +- launcher/minecraft/auth/steps/MSAStep.cpp | 20 -- launcher/minecraft/auth/steps/MSAStep.h | 3 +- .../auth/steps/MinecraftProfileStep.cpp | 57 +++--- .../auth/steps/MinecraftProfileStep.h | 12 +- launcher/minecraft/auth/steps/OfflineStep.cpp | 8 - launcher/minecraft/auth/steps/OfflineStep.h | 6 +- .../auth/steps/XboxAuthorizationStep.cpp | 59 +++--- .../auth/steps/XboxAuthorizationStep.h | 13 +- .../minecraft/auth/steps/XboxProfileStep.cpp | 55 +++--- .../minecraft/auth/steps/XboxProfileStep.h | 12 +- .../minecraft/auth/steps/XboxUserStep.cpp | 51 +++-- launcher/minecraft/auth/steps/XboxUserStep.h | 12 +- .../helpers/NetworkResourceAPI.cpp | 12 +- .../modrinth/ModrinthInstanceCreationTask.cpp | 2 +- launcher/net/ApiDownload.cpp | 1 - launcher/net/ApiUpload.cpp | 3 - launcher/net/Download.cpp | 2 - launcher/net/NetAction.h | 100 ---------- launcher/net/NetJob.cpp | 11 +- launcher/net/NetJob.h | 6 +- launcher/net/NetRequest.cpp | 36 +++- launcher/net/NetRequest.h | 44 +++-- launcher/net/Sink.h | 3 +- launcher/net/Validator.h | 2 +- launcher/ui/dialogs/ProfileSetupDialog.cpp | 73 ++++---- launcher/ui/dialogs/ProfileSetupDialog.h | 12 +- .../ui/pages/modplatform/ResourceModel.cpp | 4 +- .../modplatform/modrinth/ModrinthModel.cpp | 4 +- tests/Library_test.cpp | 26 +-- 50 files changed, 381 insertions(+), 743 deletions(-) delete mode 100644 launcher/minecraft/auth/AuthRequest.cpp delete mode 100644 launcher/minecraft/auth/AuthRequest.h delete mode 100644 launcher/net/NetAction.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index e93219015..cf1ab8798 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -126,7 +126,6 @@ set(NET_SOURCES net/MetaCacheSink.h net/Logging.h net/Logging.cpp - net/NetAction.h net/NetJob.cpp net/NetJob.h net/NetUtils.h @@ -212,8 +211,6 @@ set(MINECRAFT_SOURCES minecraft/auth/AccountList.h minecraft/auth/AccountTask.cpp minecraft/auth/AccountTask.h - minecraft/auth/AuthRequest.cpp - minecraft/auth/AuthRequest.h minecraft/auth/AuthSession.cpp minecraft/auth/AuthSession.h minecraft/auth/AuthStep.cpp @@ -624,7 +621,6 @@ set(PRISMUPDATER_SOURCES net/HttpMetaCache.h net/Logging.h net/Logging.cpp - net/NetAction.h net/NetRequest.cpp net/NetRequest.h net/NetJob.cpp diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp index 73dc17891..1499199ae 100644 --- a/launcher/InstanceCreationTask.cpp +++ b/launcher/InstanceCreationTask.cpp @@ -3,8 +3,6 @@ #include #include -InstanceCreationTask::InstanceCreationTask() = default; - void InstanceCreationTask::executeTask() { setAbortable(true); diff --git a/launcher/InstanceCreationTask.h b/launcher/InstanceCreationTask.h index 380fdf8a4..84fb2a145 100644 --- a/launcher/InstanceCreationTask.h +++ b/launcher/InstanceCreationTask.h @@ -6,7 +6,7 @@ class InstanceCreationTask : public InstanceTask { Q_OBJECT public: - InstanceCreationTask(); + InstanceCreationTask() = default; virtual ~InstanceCreationTask() = default; protected: diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 48e150d16..6bbe0bb2c 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -51,6 +51,7 @@ #include "net/Download.h" #include "Application.h" +#include "net/NetRequest.h" namespace { QSet collectPathsFromDir(QString dirPath) @@ -276,7 +277,7 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder) } // namespace AssetsUtils -NetAction::Ptr AssetObject::getDownloadAction() +Net::NetRequest::Ptr AssetObject::getDownloadAction() { QFileInfo objectFile(getLocalPath()); if ((!objectFile.isFile()) || (objectFile.size() != size)) { diff --git a/launcher/minecraft/AssetsUtils.h b/launcher/minecraft/AssetsUtils.h index 87956e57a..ea3613bd0 100644 --- a/launcher/minecraft/AssetsUtils.h +++ b/launcher/minecraft/AssetsUtils.h @@ -17,14 +17,14 @@ #include #include -#include "net/NetAction.h" #include "net/NetJob.h" +#include "net/NetRequest.h" struct AssetObject { QString getRelPath(); QUrl getUrl(); QString getLocalPath(); - NetAction::Ptr getDownloadAction(); + Net::NetRequest::Ptr getDownloadAction(); QString hash; qint64 size; diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index 0e8ddf03d..2c3f2035f 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -35,6 +35,7 @@ #include "Library.h" #include "MinecraftInstance.h" +#include "net/NetRequest.h" #include #include @@ -74,12 +75,12 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext, } } -QList Library::getDownloads(const RuntimeContext& runtimeContext, - class HttpMetaCache* cache, - QStringList& failedLocalFiles, - const QString& overridePath) const +QList Library::getDownloads(const RuntimeContext& runtimeContext, + class HttpMetaCache* cache, + QStringList& failedLocalFiles, + const QString& overridePath) const { - QList out; + QList out; bool stale = isAlwaysStale(); bool local = isLocal(); diff --git a/launcher/minecraft/Library.h b/launcher/minecraft/Library.h index adb89c4c6..d3019e814 100644 --- a/launcher/minecraft/Library.h +++ b/launcher/minecraft/Library.h @@ -34,7 +34,6 @@ */ #pragma once -#include #include #include #include @@ -48,6 +47,7 @@ #include "MojangDownloadInfo.h" #include "Rule.h" #include "RuntimeContext.h" +#include "net/NetRequest.h" class Library; class MinecraftInstance; @@ -144,10 +144,10 @@ class Library { bool isForge() const; // Get a list of downloads for this library - QList getDownloads(const RuntimeContext& runtimeContext, - class HttpMetaCache* cache, - QStringList& failedLocalFiles, - const QString& overridePath) const; + QList getDownloads(const RuntimeContext& runtimeContext, + class HttpMetaCache* cache, + QStringList& failedLocalFiles, + const QString& overridePath) const; QString getCompatibleNative(const RuntimeContext& runtimeContext) const; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index 68ebe3626..af83502bc 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -36,6 +36,7 @@ #include "AccountList.h" #include "AccountData.h" #include "AccountTask.h" +#include "tasks/Task.h" #include #include @@ -639,8 +640,8 @@ void AccountList::tryNext() if (account->internalId() == accountId) { m_currentTask = account->refresh(); if (m_currentTask) { - connect(m_currentTask.get(), &AccountTask::succeeded, this, &AccountList::authSucceeded); - connect(m_currentTask.get(), &AccountTask::failed, this, &AccountList::authFailed); + connect(m_currentTask.get(), &Task::succeeded, this, &AccountList::authSucceeded); + connect(m_currentTask.get(), &Task::failed, this, &AccountList::authFailed); m_currentTask->start(); qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " << accountId; diff --git a/launcher/minecraft/auth/AuthRequest.cpp b/launcher/minecraft/auth/AuthRequest.cpp deleted file mode 100644 index 189978cc0..000000000 --- a/launcher/minecraft/auth/AuthRequest.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include - -#include "Application.h" -#include "AuthRequest.h" -#include "katabasis/Globals.h" - -AuthRequest::AuthRequest(QObject* parent) : QObject(parent) {} - -AuthRequest::~AuthRequest() {} - -void AuthRequest::get(const QNetworkRequest& req, int timeout /* = 60*1000*/) -{ - setup(req, QNetworkAccessManager::GetOperation); - reply_ = APPLICATION->network()->get(request_); - status_ = Requesting; - timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); -#else // &QNetworkReply::error SIGNAL depricated - connect(reply_, QOverload::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); -#endif - connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); - connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); -} - -void AuthRequest::post(const QNetworkRequest& req, const QByteArray& data, int timeout /* = 60*1000*/) -{ - setup(req, QNetworkAccessManager::PostOperation); - data_ = data; - status_ = Requesting; - reply_ = APPLICATION->network()->post(request_, data_); - timedReplies_.add(new Katabasis::Reply(reply_, timeout)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(reply_, &QNetworkReply::errorOccurred, this, &AuthRequest::onRequestError); -#else // &QNetworkReply::error SIGNAL depricated - connect(reply_, QOverload::of(&QNetworkReply::error), this, &AuthRequest::onRequestError); -#endif - connect(reply_, &QNetworkReply::finished, this, &AuthRequest::onRequestFinished); - connect(reply_, &QNetworkReply::sslErrors, this, &AuthRequest::onSslErrors); - connect(reply_, &QNetworkReply::uploadProgress, this, &AuthRequest::onUploadProgress); -} - -void AuthRequest::onRequestFinished() -{ - if (status_ == Idle) { - return; - } - if (reply_ != qobject_cast(sender())) { - return; - } - httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - finish(); -} - -void AuthRequest::onRequestError(QNetworkReply::NetworkError error) -{ - qWarning() << "AuthRequest::onRequestError: Error" << (int)error; - if (status_ == Idle) { - return; - } - if (reply_ != qobject_cast(sender())) { - return; - } - errorString_ = reply_->errorString(); - httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - error_ = error; - qWarning() << "AuthRequest::onRequestError: Error string: " << errorString_; - qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus_ - << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); - - // QTimer::singleShot(10, this, SLOT(finish())); -} - -void AuthRequest::onSslErrors(QList errors) -{ - int i = 1; - for (auto error : errors) { - qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void AuthRequest::onUploadProgress(qint64 uploaded, qint64 total) -{ - if (status_ == Idle) { - qWarning() << "AuthRequest::onUploadProgress: No pending request"; - return; - } - if (reply_ != qobject_cast(sender())) { - return; - } - // Restart timeout because request in progress - Katabasis::Reply* o2Reply = timedReplies_.find(reply_); - if (o2Reply) { - o2Reply->start(); - } - emit uploadProgress(uploaded, total); -} - -void AuthRequest::setup(const QNetworkRequest& req, QNetworkAccessManager::Operation operation, const QByteArray& verb) -{ - request_ = req; - operation_ = operation; - url_ = req.url(); - - QUrl url = url_; - request_.setUrl(url); - - if (!verb.isEmpty()) { - request_.setRawHeader(Katabasis::HTTP_HTTP_HEADER, verb); - } - - status_ = Requesting; - error_ = QNetworkReply::NoError; - errorString_.clear(); - httpStatus_ = 0; -} - -void AuthRequest::finish() -{ - QByteArray data; - if (status_ == Idle) { - qWarning() << "AuthRequest::finish: No pending request"; - return; - } - data = reply_->readAll(); - status_ = Idle; - timedReplies_.remove(reply_); - reply_->disconnect(this); - reply_->deleteLater(); - QList headers = reply_->rawHeaderPairs(); - emit finished(error_, data, headers); -} diff --git a/launcher/minecraft/auth/AuthRequest.h b/launcher/minecraft/auth/AuthRequest.h deleted file mode 100644 index 84d2a7d68..000000000 --- a/launcher/minecraft/auth/AuthRequest.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -#include "katabasis/Reply.h" - -/// Makes authentication requests. -class AuthRequest : public QObject { - Q_OBJECT - - public: - explicit AuthRequest(QObject* parent = 0); - ~AuthRequest(); - - public slots: - void get(const QNetworkRequest& req, int timeout = 60 * 1000); - void post(const QNetworkRequest& req, const QByteArray& data, int timeout = 60 * 1000); - - signals: - - /// Emitted when a request has been completed or failed. - void finished(QNetworkReply::NetworkError error, QByteArray data, QList headers); - - /// Emitted when an upload has progressed. - void uploadProgress(qint64 bytesSent, qint64 bytesTotal); - - protected slots: - - /// Handle request finished. - void onRequestFinished(); - - /// Handle request error. - void onRequestError(QNetworkReply::NetworkError error); - - /// Handle ssl errors. - void onSslErrors(QList errors); - - /// Finish the request, emit finished() signal. - void finish(); - - /// Handle upload progress. - void onUploadProgress(qint64 uploaded, qint64 total); - - public: - QNetworkReply::NetworkError error_; - int httpStatus_ = 0; - QString errorString_; - - protected: - void setup(const QNetworkRequest& request, QNetworkAccessManager::Operation operation, const QByteArray& verb = QByteArray()); - - enum Status { Idle, Requesting, ReRequesting }; - - QNetworkRequest request_; - QByteArray data_; - QNetworkReply* reply_; - Status status_; - QNetworkAccessManager::Operation operation_; - QUrl url_; - Katabasis::ReplyList timedReplies_; - - QTimer* timer_; -}; diff --git a/launcher/minecraft/auth/AuthStep.cpp b/launcher/minecraft/auth/AuthStep.cpp index 6240cc549..6b78c415a 100644 --- a/launcher/minecraft/auth/AuthStep.cpp +++ b/launcher/minecraft/auth/AuthStep.cpp @@ -1,5 +1,3 @@ #include "AuthStep.h" AuthStep::AuthStep(AccountData* data) : QObject(nullptr), m_data(data) {} - -AuthStep::~AuthStep() noexcept = default; diff --git a/launcher/minecraft/auth/AuthStep.h b/launcher/minecraft/auth/AuthStep.h index becd9b0c5..b837b5703 100644 --- a/launcher/minecraft/auth/AuthStep.h +++ b/launcher/minecraft/auth/AuthStep.h @@ -15,13 +15,12 @@ class AuthStep : public QObject { public: explicit AuthStep(AccountData* data); - virtual ~AuthStep() noexcept; + virtual ~AuthStep() noexcept = default; virtual QString describe() = 0; public slots: virtual void perform() = 0; - virtual void rehydrate() = 0; signals: void finished(AccountTaskState resultingState, QString message); diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index ecee93d98..db4d1c07b 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -153,7 +153,7 @@ shared_qobject_ptr MinecraftAccount::refresh() if (data.type == AccountType::MSA) { m_currentTask.reset(new MSASilent(&data)); } else { - m_currentTask.reset(new OfflineRefresh(&data)); + m_currentTask.reset(new OfflineLogin(&data)); } connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp index 3770b869a..1836533bd 100644 --- a/launcher/minecraft/auth/flows/Offline.cpp +++ b/launcher/minecraft/auth/flows/Offline.cpp @@ -2,11 +2,6 @@ #include "minecraft/auth/steps/OfflineStep.h" -OfflineRefresh::OfflineRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data)); -} - OfflineLogin::OfflineLogin(AccountData* data, QObject* parent) : AuthFlow(data, parent) { m_steps.append(makeShared(m_data)); diff --git a/launcher/minecraft/auth/flows/Offline.h b/launcher/minecraft/auth/flows/Offline.h index 2bc9c7612..a8d378e16 100644 --- a/launcher/minecraft/auth/flows/Offline.h +++ b/launcher/minecraft/auth/flows/Offline.h @@ -1,12 +1,6 @@ #pragma once #include "AuthFlow.h" -class OfflineRefresh : public AuthFlow { - Q_OBJECT - public: - explicit OfflineRefresh(AccountData* data, QObject* parent = 0); -}; - class OfflineLogin : public AuthFlow { Q_OBJECT public: diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.cpp b/launcher/minecraft/auth/steps/EntitlementsStep.cpp index 0573dcb6e..19cbe6898 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.cpp +++ b/launcher/minecraft/auth/steps/EntitlementsStep.cpp @@ -1,16 +1,20 @@ #include "EntitlementsStep.h" +#include #include +#include #include +#include +#include "Application.h" #include "Logging.h" -#include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" +#include "net/Download.h" +#include "net/StaticHeaderProxy.h" +#include "tasks/Task.h" EntitlementsStep::EntitlementsStep(AccountData* data) : AuthStep(data) {} -EntitlementsStep::~EntitlementsStep() noexcept = default; - QString EntitlementsStep::describe() { return tr("Determining game ownership."); @@ -19,35 +23,31 @@ QString EntitlementsStep::describe() void EntitlementsStep::perform() { auto uuid = QUuid::createUuid(); - m_entitlementsRequestId = uuid.toString().remove('{').remove('}'); - auto url = "https://api.minecraftservices.com/entitlements/license?requestId=" + m_entitlementsRequestId; - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &EntitlementsStep::onRequestDone); - requestor->get(request); + m_entitlements_request_id = uuid.toString().remove('{').remove('}'); + + QUrl url("https://api.minecraftservices.com/entitlements/license?requestId=" + m_entitlements_request_id); + auto headers = QList{ { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + { "Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8() } }; + + m_response.reset(new QByteArray()); + m_task = Net::Download::makeByteArray(url, m_response); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &EntitlementsStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); qDebug() << "Getting entitlements..."; } -void EntitlementsStep::rehydrate() +void EntitlementsStep::onRequestDone() { - // NOOP, for now. We only save bools and there's nothing to check. -} - -void EntitlementsStep::onRequestDone([[maybe_unused]] QNetworkReply::NetworkError error, - QByteArray data, - [[maybe_unused]] QList headers) -{ - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - qCDebug(authCredentials()) << data; + qCDebug(authCredentials()) << *m_response; // TODO: check presence of same entitlementsRequestId? // TODO: validate JWTs? - Parsers::parseMinecraftEntitlements(data, m_data->minecraftEntitlement); + Parsers::parseMinecraftEntitlements(*m_response, m_data->minecraftEntitlement); emit finished(AccountTaskState::STATE_WORKING, tr("Got entitlements")); } diff --git a/launcher/minecraft/auth/steps/EntitlementsStep.h b/launcher/minecraft/auth/steps/EntitlementsStep.h index be16bda13..dd8ec7aaa 100644 --- a/launcher/minecraft/auth/steps/EntitlementsStep.h +++ b/launcher/minecraft/auth/steps/EntitlementsStep.h @@ -1,24 +1,26 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Download.h" class EntitlementsStep : public AuthStep { Q_OBJECT public: explicit EntitlementsStep(AccountData* data); - virtual ~EntitlementsStep() noexcept; + virtual ~EntitlementsStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); private: - QString m_entitlementsRequestId; + QString m_entitlements_request_id; + std::shared_ptr m_response; + Net::Download::Ptr m_task; }; diff --git a/launcher/minecraft/auth/steps/GetSkinStep.cpp b/launcher/minecraft/auth/steps/GetSkinStep.cpp index 520877020..d9785b16a 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.cpp +++ b/launcher/minecraft/auth/steps/GetSkinStep.cpp @@ -3,13 +3,10 @@ #include -#include "minecraft/auth/AuthRequest.h" -#include "minecraft/auth/Parsers.h" +#include "Application.h" GetSkinStep::GetSkinStep(AccountData* data) : AuthStep(data) {} -GetSkinStep::~GetSkinStep() noexcept = default; - QString GetSkinStep::describe() { return tr("Getting skin."); @@ -17,25 +14,20 @@ QString GetSkinStep::describe() void GetSkinStep::perform() { - auto url = QUrl(m_data->minecraftProfile.skin.url); - QNetworkRequest request = QNetworkRequest(url); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &GetSkinStep::onRequestDone); - requestor->get(request); + QUrl url(m_data->minecraftProfile.skin.url); + + m_response.reset(new QByteArray()); + m_task = Net::Download::makeByteArray(url, m_response); + + connect(m_task.get(), &Task::finished, this, &GetSkinStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); } -void GetSkinStep::rehydrate() +void GetSkinStep::onRequestDone() { - // NOOP, for now. -} - -void GetSkinStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) -{ - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - if (error == QNetworkReply::NoError) { - m_data->minecraftProfile.skin.data = data; - } + if (m_task->error() == QNetworkReply::NoError) + m_data->minecraftProfile.skin.data = *m_response; emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Got skin")); } diff --git a/launcher/minecraft/auth/steps/GetSkinStep.h b/launcher/minecraft/auth/steps/GetSkinStep.h index 105e497d1..fffd8be03 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.h +++ b/launcher/minecraft/auth/steps/GetSkinStep.h @@ -1,21 +1,25 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Download.h" class GetSkinStep : public AuthStep { Q_OBJECT public: explicit GetSkinStep(AccountData* data); - virtual ~GetSkinStep() noexcept; + virtual ~GetSkinStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); + + private: + std::shared_ptr m_response; + Net::Download::Ptr m_task; }; diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp index c57f51113..8981c5752 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp @@ -1,17 +1,18 @@ #include "LauncherLoginStep.h" #include +#include +#include "Application.h" #include "Logging.h" #include "minecraft/auth/AccountTask.h" -#include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" +#include "net/StaticHeaderProxy.h" +#include "net/Upload.h" LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {} -LauncherLoginStep::~LauncherLoginStep() noexcept = default; - QString LauncherLoginStep::describe() { return tr("Accessing Mojang services."); @@ -19,7 +20,7 @@ QString LauncherLoginStep::describe() void LauncherLoginStep::perform() { - auto requestURL = "https://api.minecraftservices.com/launcher/login"; + QUrl url("https://api.minecraftservices.com/launcher/login"); auto uhs = m_data->mojangservicesToken.extra["uhs"].toString(); auto xToken = m_data->mojangservicesToken.token; @@ -31,40 +32,37 @@ void LauncherLoginStep::perform() )XXX"; auto requestBody = mc_auth_template.arg(uhs, xToken); - QNetworkRequest request = QNetworkRequest(QUrl(requestURL)); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &LauncherLoginStep::onRequestDone); - requestor->post(request, requestBody.toUtf8()); + auto headers = QList{ + { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + }; + + m_response.reset(new QByteArray()); + m_task = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8()); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &LauncherLoginStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); qDebug() << "Getting Minecraft access token..."; } -void LauncherLoginStep::rehydrate() +void LauncherLoginStep::onRequestDone() { - // TODO: check the token validity -} - -void LauncherLoginStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) -{ - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - qCDebug(authCredentials()) << data; - if (error != QNetworkReply::NoError) { - qWarning() << "Reply error:" << error; - qCDebug(authCredentials()) << data; - if (Net::isApplicationError(error)) { - emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)); + qCDebug(authCredentials()) << *m_response; + if (m_task->error() != QNetworkReply::NoError) { + qWarning() << "Reply error:" << m_task->error(); + if (Net::isApplicationError(m_task->error())) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to get Minecraft access token: %1").arg(m_task->errorString())); } else { - emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(requestor->errorString_)); + emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(m_task->errorString())); } return; } - if (!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { + if (!Parsers::parseMojangResponse(*m_response, m_data->yggdrasilToken)) { qWarning() << "Could not parse login_with_xbox response..."; - qCDebug(authCredentials()) << data; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response.")); return; } diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.h b/launcher/minecraft/auth/steps/LauncherLoginStep.h index 30c18e675..21a2a4920 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.h +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.h @@ -1,21 +1,25 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Upload.h" class LauncherLoginStep : public AuthStep { Q_OBJECT public: explicit LauncherLoginStep(AccountData* data); - virtual ~LauncherLoginStep() noexcept; + virtual ~LauncherLoginStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); + + private: + std::shared_ptr m_response; + Net::Upload::Ptr m_task; }; diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 1aa22765d..0a4b7c814 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -37,10 +37,6 @@ #include -#include "BuildConfig.h" -#include "minecraft/auth/AuthRequest.h" -#include "minecraft/auth/Parsers.h" - #include "Application.h" #include "Logging.h" @@ -63,27 +59,11 @@ MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(ac connect(m_oauth2, &OAuth2::showVerificationUriAndCode, this, &MSAStep::showVerificationUriAndCode); } -MSAStep::~MSAStep() noexcept = default; - QString MSAStep::describe() { return tr("Logging in with Microsoft account."); } -void MSAStep::rehydrate() -{ - switch (m_action) { - case Refresh: { - // TODO: check the tokens and see if they are old (older than a day) - return; - } - case Login: { - // NOOP - return; - } - } -} - void MSAStep::perform() { switch (m_action) { diff --git a/launcher/minecraft/auth/steps/MSAStep.h b/launcher/minecraft/auth/steps/MSAStep.h index b6635d4a5..ee441308f 100644 --- a/launcher/minecraft/auth/steps/MSAStep.h +++ b/launcher/minecraft/auth/steps/MSAStep.h @@ -48,10 +48,9 @@ class MSAStep : public AuthStep { public: explicit MSAStep(AccountData* data, Action action); - virtual ~MSAStep() noexcept; + virtual ~MSAStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp index a854342bc..305f44320 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp @@ -2,15 +2,13 @@ #include -#include "Logging.h" -#include "minecraft/auth/AuthRequest.h" +#include "Application.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" +#include "net/StaticHeaderProxy.h" MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {} -MinecraftProfileStep::~MinecraftProfileStep() noexcept = default; - QString MinecraftProfileStep::describe() { return tr("Fetching the Minecraft profile."); @@ -18,52 +16,47 @@ QString MinecraftProfileStep::describe() void MinecraftProfileStep::perform() { - auto url = QUrl("https://api.minecraftservices.com/minecraft/profile"); - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8()); + QUrl url("https://api.minecraftservices.com/minecraft/profile"); + auto headers = QList{ { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + { "Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8() } }; - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &MinecraftProfileStep::onRequestDone); - requestor->get(request); + m_response.reset(new QByteArray()); + m_task = Net::Download::makeByteArray(url, m_response); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &MinecraftProfileStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); } -void MinecraftProfileStep::rehydrate() +void MinecraftProfileStep::onRequestDone() { - // NOOP, for now. We only save bools and there's nothing to check. -} - -void MinecraftProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) -{ - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - qCDebug(authCredentials()) << data; - if (error == QNetworkReply::ContentNotFoundError) { + if (m_task->error() == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. m_data->minecraftProfile = MinecraftProfile(); emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile.")); return; } - if (error != QNetworkReply::NoError) { + if (m_task->error() != QNetworkReply::NoError) { qWarning() << "Error getting profile:"; - qWarning() << " HTTP Status: " << requestor->httpStatus_; - qWarning() << " Internal error no.: " << error; - qWarning() << " Error string: " << requestor->errorString_; + qWarning() << " HTTP Status: " << m_task->replyStatusCode(); + qWarning() << " Internal error no.: " << m_task->error(); + qWarning() << " Error string: " << m_task->errorString(); qWarning() << " Response:"; - qWarning() << QString::fromUtf8(data); + qWarning() << QString::fromUtf8(*m_response); - if (Net::isApplicationError(error)) { + if (Net::isApplicationError(m_task->error())) { emit finished(AccountTaskState::STATE_FAILED_SOFT, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); + tr("Minecraft Java profile acquisition failed: %1").arg(m_task->errorString())); } else { - emit finished(AccountTaskState::STATE_OFFLINE, - tr("Minecraft Java profile acquisition failed: %1").arg(requestor->errorString_)); + emit finished(AccountTaskState::STATE_OFFLINE, tr("Minecraft Java profile acquisition failed: %1").arg(m_task->errorString())); } return; } - if (!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) { + if (!Parsers::parseMinecraftProfile(*m_response, m_data->minecraftProfile)) { m_data->minecraftProfile = MinecraftProfile(); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); return; diff --git a/launcher/minecraft/auth/steps/MinecraftProfileStep.h b/launcher/minecraft/auth/steps/MinecraftProfileStep.h index cb30dab21..831cd52f7 100644 --- a/launcher/minecraft/auth/steps/MinecraftProfileStep.h +++ b/launcher/minecraft/auth/steps/MinecraftProfileStep.h @@ -1,21 +1,25 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Download.h" class MinecraftProfileStep : public AuthStep { Q_OBJECT public: explicit MinecraftProfileStep(AccountData* data); - virtual ~MinecraftProfileStep() noexcept; + virtual ~MinecraftProfileStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); + + private: + std::shared_ptr m_response; + Net::Download::Ptr m_task; }; diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp index bf111abe8..a96b08377 100644 --- a/launcher/minecraft/auth/steps/OfflineStep.cpp +++ b/launcher/minecraft/auth/steps/OfflineStep.cpp @@ -1,20 +1,12 @@ #include "OfflineStep.h" -#include "Application.h" - OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {} -OfflineStep::~OfflineStep() noexcept = default; QString OfflineStep::describe() { return tr("Creating offline account."); } -void OfflineStep::rehydrate() -{ - // NOOP -} - void OfflineStep::perform() { emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account.")); diff --git a/launcher/minecraft/auth/steps/OfflineStep.h b/launcher/minecraft/auth/steps/OfflineStep.h index 3bf123d6a..411879b10 100644 --- a/launcher/minecraft/auth/steps/OfflineStep.h +++ b/launcher/minecraft/auth/steps/OfflineStep.h @@ -1,19 +1,15 @@ #pragma once #include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" -#include - class OfflineStep : public AuthStep { Q_OBJECT public: explicit OfflineStep(AccountData* data); - virtual ~OfflineStep() noexcept; + virtual ~OfflineStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; }; diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index 84c52c386..2ae3af0dd 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -4,27 +4,22 @@ #include #include +#include "Application.h" #include "Logging.h" -#include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" +#include "net/StaticHeaderProxy.h" +#include "net/Upload.h" XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind) : AuthStep(data), m_token(token), m_relyingParty(relyingParty), m_authorizationKind(authorizationKind) {} -XboxAuthorizationStep::~XboxAuthorizationStep() noexcept = default; - QString XboxAuthorizationStep::describe() { return tr("Getting authorization to access %1 services.").arg(m_authorizationKind); } -void XboxAuthorizationStep::rehydrate() -{ - // FIXME: check if the tokens are good? -} - void XboxAuthorizationStep::perform() { QString xbox_auth_template = R"XXX( @@ -41,40 +36,44 @@ void XboxAuthorizationStep::perform() )XXX"; auto xbox_auth_data = xbox_auth_template.arg(m_data->userToken.token, m_relyingParty); // http://xboxlive.com - QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &XboxAuthorizationStep::onRequestDone); - requestor->post(request, xbox_auth_data.toUtf8()); + QUrl url("https://xsts.auth.xboxlive.com/xsts/authorize"); + auto headers = QList{ + { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + }; + m_response.reset(new QByteArray()); + m_task = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8()); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &XboxAuthorizationStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); qDebug() << "Getting authorization token for " << m_relyingParty; } -void XboxAuthorizationStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +void XboxAuthorizationStep::onRequestDone() { - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - qCDebug(authCredentials()) << data; - if (error != QNetworkReply::NoError) { - qWarning() << "Reply error:" << error; - if (Net::isApplicationError(error)) { - if (!processSTSError(error, data, headers)) { + qCDebug(authCredentials()) << *m_response; + if (m_task->error() != QNetworkReply::NoError) { + qWarning() << "Reply error:" << m_task->error(); + if (Net::isApplicationError(m_task->error())) { + if (!processSTSError()) { emit finished(AccountTaskState::STATE_FAILED_SOFT, - tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, error)); + tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, m_task->error())); } else { emit finished(AccountTaskState::STATE_FAILED_SOFT, - tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)); + tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, m_task->errorString())); } } else { emit finished(AccountTaskState::STATE_OFFLINE, - tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, requestor->errorString_)); + tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, m_task->errorString())); } return; } Katabasis::Token temp; - if (!Parsers::parseXTokenResponse(data, temp, m_authorizationKind)) { + if (!Parsers::parseXTokenResponse(*m_response, temp, m_authorizationKind)) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind)); return; @@ -91,11 +90,11 @@ void XboxAuthorizationStep::onRequestDone(QNetworkReply::NetworkError error, QBy emit finished(AccountTaskState::STATE_WORKING, tr("Got authorization to access %1").arg(m_relyingParty)); } -bool XboxAuthorizationStep::processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList headers) +bool XboxAuthorizationStep::processSTSError() { - if (error == QNetworkReply::AuthenticationRequiredError) { + if (m_task->error() == QNetworkReply::AuthenticationRequiredError) { QJsonParseError jsonError; - QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); + QJsonDocument doc = QJsonDocument::fromJson(*m_response, &jsonError); if (jsonError.error) { qWarning() << "Cannot parse error XSTS response as JSON: " << jsonError.errorString(); emit finished(AccountTaskState::STATE_FAILED_SOFT, diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h index dee24c954..eb7097f6f 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h @@ -1,29 +1,32 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Upload.h" class XboxAuthorizationStep : public AuthStep { Q_OBJECT public: explicit XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind); - virtual ~XboxAuthorizationStep() noexcept; + virtual ~XboxAuthorizationStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private: - bool processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList headers); + bool processSTSError(); private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); private: Katabasis::Token* m_token; QString m_relyingParty; QString m_authorizationKind; + + std::shared_ptr m_response; + Net::Upload::Ptr m_task; }; diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.cpp b/launcher/minecraft/auth/steps/XboxProfileStep.cpp index fd2b32cce..440a4657c 100644 --- a/launcher/minecraft/auth/steps/XboxProfileStep.cpp +++ b/launcher/minecraft/auth/steps/XboxProfileStep.cpp @@ -3,28 +3,21 @@ #include #include +#include "Application.h" #include "Logging.h" -#include "minecraft/auth/AuthRequest.h" -#include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" +#include "net/StaticHeaderProxy.h" XboxProfileStep::XboxProfileStep(AccountData* data) : AuthStep(data) {} -XboxProfileStep::~XboxProfileStep() noexcept = default; - QString XboxProfileStep::describe() { return tr("Fetching Xbox profile."); } -void XboxProfileStep::rehydrate() -{ - // NOOP, for now. We only save bools and there's nothing to check. -} - void XboxProfileStep::perform() { - auto url = QUrl("https://profile.xboxlive.com/users/me/profile/settings"); + QUrl url("https://profile.xboxlive.com/users/me/profile/settings"); QUrlQuery q; q.addQueryItem("settings", "GameDisplayName,AppDisplayName,AppDisplayPicRaw,GameDisplayPicRaw," @@ -33,36 +26,38 @@ void XboxProfileStep::perform() "PreferredColor,Location,Bio,Watermarks," "RealName,RealNameOverride,IsQuarantined"); url.setQuery(q); + auto headers = QList{ + { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + { "x-xbl-contract-version", "3" }, + { "Authorization", QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8() } + }; - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - request.setRawHeader("x-xbl-contract-version", "3"); - request.setRawHeader("Authorization", - QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8()); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &XboxProfileStep::onRequestDone); - requestor->get(request); + m_response.reset(new QByteArray()); + m_task = Net::Download::makeByteArray(url, m_response); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &XboxProfileStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); qDebug() << "Getting Xbox profile..."; } -void XboxProfileStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +void XboxProfileStep::onRequestDone() { - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - if (error != QNetworkReply::NoError) { - qWarning() << "Reply error:" << error; - qCDebug(authCredentials()) << data; - if (Net::isApplicationError(error)) { - emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)); + if (m_task->error() != QNetworkReply::NoError) { + qWarning() << "Reply error:" << m_task->error(); + qCDebug(authCredentials()) << *m_response; + if (Net::isApplicationError(m_task->error())) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to retrieve the Xbox profile: %1").arg(m_task->errorString())); } else { - emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to retrieve the Xbox profile: %1").arg(requestor->errorString_)); + emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to retrieve the Xbox profile: %1").arg(m_task->errorString())); } return; } - qCDebug(authCredentials()) << "XBox profile: " << data; + qCDebug(authCredentials()) << "XBox profile: " << *m_response; emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile")); } diff --git a/launcher/minecraft/auth/steps/XboxProfileStep.h b/launcher/minecraft/auth/steps/XboxProfileStep.h index b8494b6e5..dfa273d9c 100644 --- a/launcher/minecraft/auth/steps/XboxProfileStep.h +++ b/launcher/minecraft/auth/steps/XboxProfileStep.h @@ -1,21 +1,25 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Download.h" class XboxProfileStep : public AuthStep { Q_OBJECT public: explicit XboxProfileStep(AccountData* data); - virtual ~XboxProfileStep() noexcept; + virtual ~XboxProfileStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); + + private: + std::shared_ptr m_response; + Net::Download::Ptr m_task; }; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp index 856036d23..46c3f0365 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.cpp +++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp @@ -2,24 +2,18 @@ #include -#include "minecraft/auth/AuthRequest.h" +#include "Application.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" +#include "net/StaticHeaderProxy.h" XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) {} -XboxUserStep::~XboxUserStep() noexcept = default; - QString XboxUserStep::describe() { return tr("Logging in as an Xbox user."); } -void XboxUserStep::rehydrate() -{ - // NOOP, for now. We only save bools and there's nothing to check. -} - void XboxUserStep::perform() { QString xbox_auth_template = R"XXX( @@ -35,36 +29,39 @@ void XboxUserStep::perform() )XXX"; auto xbox_auth_data = xbox_auth_template.arg(m_data->msaToken.token); - QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate")); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - // 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"); + QUrl url("https://user.auth.xboxlive.com/user/authenticate"); + auto headers = QList{ + { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + // set contract-version header (prevent err 400 bad-request?) + // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders + { "x-xbl-contract-version", "1" } + }; + m_response.reset(new QByteArray()); + m_task = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8()); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); - auto* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &XboxUserStep::onRequestDone); - requestor->post(request, xbox_auth_data.toUtf8()); + connect(m_task.get(), &Task::finished, this, &XboxUserStep::onRequestDone); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); qDebug() << "First layer of XBox auth ... commencing."; } -void XboxUserStep::onRequestDone(QNetworkReply::NetworkError error, QByteArray data, QList headers) +void XboxUserStep::onRequestDone() { - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - if (error != QNetworkReply::NoError) { - qWarning() << "Reply error:" << error; - if (Net::isApplicationError(error)) { - emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(requestor->errorString_)); + if (m_task->error() != QNetworkReply::NoError) { + qWarning() << "Reply error:" << m_task->error(); + if (Net::isApplicationError(m_task->error())) { + emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(m_task->errorString())); } else { - emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(requestor->errorString_)); + emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(m_task->errorString())); } return; } Katabasis::Token temp; - if (!Parsers::parseXTokenResponse(data, temp, "UToken")) { + if (!Parsers::parseXTokenResponse(*m_response, temp, "UToken")) { qWarning() << "Could not parse user authentication response..."; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication response could not be understood.")); return; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.h b/launcher/minecraft/auth/steps/XboxUserStep.h index e92727a4d..934a00c52 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.h +++ b/launcher/minecraft/auth/steps/XboxUserStep.h @@ -1,21 +1,25 @@ #pragma once #include +#include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" +#include "net/Upload.h" class XboxUserStep : public AuthStep { Q_OBJECT public: explicit XboxUserStep(AccountData* data); - virtual ~XboxUserStep() noexcept; + virtual ~XboxUserStep() noexcept = default; void perform() override; - void rehydrate() override; QString describe() override; private slots: - void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList); + void onRequestDone(); + + private: + std::shared_ptr m_response; + Net::Upload::Ptr m_task; }; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 225583764..974e732a7 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -45,8 +45,8 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) { int network_error_code = -1; - if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) - network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) + network_error_code = failed_action->replyStatusCode(); callbacks.on_fail(reason, network_error_code); }); @@ -104,8 +104,8 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi }); QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) { int network_error_code = -1; - if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) - network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) + network_error_code = failed_action->replyStatusCode(); callbacks.on_fail(reason, network_error_code); }); @@ -155,8 +155,8 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, }); QObject::connect(netJob.get(), &NetJob::failed, [netJob, callbacks](const QString& reason) { int network_error_code = -1; - if (auto* failed_action = netJob->getFailedActions().at(0); failed_action && failed_action->m_reply) - network_error_code = failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) + network_error_code = failed_action->replyStatusCode(); callbacks.on_fail(reason, network_error_code); }); diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 3b875103b..e92489999 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -261,7 +261,7 @@ bool ModrinthCreationTask::createInstance() // FIXME: This really needs to be put into a ConcurrentTask of // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); - connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] { + connect(dl.get(), &Task::failed, [this, &file, file_path, param] { auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); m_files_job->addNetAction(ndl); diff --git a/launcher/net/ApiDownload.cpp b/launcher/net/ApiDownload.cpp index aaa8ff650..8768b63f8 100644 --- a/launcher/net/ApiDownload.cpp +++ b/launcher/net/ApiDownload.cpp @@ -21,7 +21,6 @@ #include "ByteArraySink.h" #include "ChecksumValidator.h" #include "MetaCacheSink.h" -#include "net/NetAction.h" namespace Net { diff --git a/launcher/net/ApiUpload.cpp b/launcher/net/ApiUpload.cpp index c1221b764..505cbd9f9 100644 --- a/launcher/net/ApiUpload.cpp +++ b/launcher/net/ApiUpload.cpp @@ -19,9 +19,6 @@ #include "net/ApiUpload.h" #include "ByteArraySink.h" -#include "ChecksumValidator.h" -#include "MetaCacheSink.h" -#include "net/NetAction.h" namespace Net { diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index bae364f12..49686db98 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -47,8 +47,6 @@ #include "ChecksumValidator.h" #include "MetaCacheSink.h" -#include "net/NetAction.h" - namespace Net { #if defined(LAUNCHER_APPLICATION) diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h deleted file mode 100644 index b66b91941..000000000 --- a/launcher/net/NetAction.h +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (c) 2022 flowln - * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include "QObjectPtr.h" -#include "tasks/Task.h" - -#include "HeaderProxy.h" - -class NetAction : public Task { - Q_OBJECT - protected: - explicit NetAction() : Task() {} - - public: - using Ptr = shared_qobject_ptr; - - virtual ~NetAction() = default; - - QUrl url() { return m_url; } - - void setNetwork(shared_qobject_ptr network) { m_network = network; } - - void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr(proxy)); } - virtual void init() = 0; - - protected slots: - virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; - virtual void downloadError(QNetworkReply::NetworkError error) = 0; - virtual void downloadFinished() = 0; - virtual void downloadReadyRead() = 0; - - virtual void sslErrors(const QList& errors) - { - int i = 1; - for (auto error : errors) { - qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } - } - - public slots: - void startAction(shared_qobject_ptr network) - { - m_network = network; - executeTask(); - } - - protected: - void executeTask() override {} - - public: - shared_qobject_ptr m_network; - - /// the network reply - unique_qobject_ptr m_reply; - - /// source URL - QUrl m_url; - std::vector> m_headerProxies; -}; diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index d027e31c9..1ceb0a860 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -36,6 +36,7 @@ */ #include "NetJob.h" +#include "net/NetRequest.h" #include "tasks/ConcurrentTask.h" #if defined(LAUNCHER_APPLICATION) #include "Application.h" @@ -48,7 +49,7 @@ NetJob::NetJob(QString job_name, shared_qobject_ptr netwo #endif } -auto NetJob::addNetAction(NetAction::Ptr action) -> bool +auto NetJob::addNetAction(Net::NetRequest::Ptr action) -> bool { action->setNetwork(m_network); @@ -111,11 +112,11 @@ auto NetJob::abort() -> bool return fullyAborted; } -auto NetJob::getFailedActions() -> QList +auto NetJob::getFailedActions() -> QList { - QList failed; + QList failed; for (auto index : m_failed) { - failed.push_back(dynamic_cast(index.get())); + failed.push_back(dynamic_cast(index.get())); } return failed; } @@ -124,7 +125,7 @@ auto NetJob::getFailedFiles() -> QList { QList failed; for (auto index : m_failed) { - failed.append(static_cast(index.get())->url().toString()); + failed.append(static_cast(index.get())->url().toString()); } return failed; } diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index f6c005809..1661842f0 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -39,7 +39,7 @@ #include #include -#include "NetAction.h" +#include "net/NetRequest.h" #include "tasks/ConcurrentTask.h" // Those are included so that they are also included by anyone using NetJob @@ -58,9 +58,9 @@ class NetJob : public ConcurrentTask { auto size() const -> int; auto canAbort() const -> bool override; - auto addNetAction(NetAction::Ptr action) -> bool; + auto addNetAction(Net::NetRequest::Ptr action) -> bool; - auto getFailedActions() -> QList; + auto getFailedActions() -> QList; auto getFailedFiles() -> QList; public slots: diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 526fe77a5..009213332 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -37,6 +37,7 @@ */ #include "NetRequest.h" +#include #include #include @@ -48,8 +49,6 @@ #endif #include "BuildConfig.h" -#include "net/NetAction.h" - #include "MMCTime.h" #include "StringUtils.h" @@ -105,20 +104,18 @@ void NetRequest::executeTask() for (auto& header_proxy : m_headerProxies) { header_proxy->writeHeaders(request); } - // TODO remove duplication -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - request.setTransferTimeout(); -#endif + request.setTransferTimeout(QNetworkRequest::DefaultTransferTimeoutConstant); m_last_progress_time = m_clock.now(); m_last_progress_bytes = 0; - QNetworkReply* rep = getReply(request); + auto rep = getReply(request); if (rep == nullptr) // it failed return; m_reply.reset(rep); - connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); + connect(rep, &QNetworkReply::uploadProgress, this, &NetRequest::onProgress); + connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::onProgress); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 connect(rep, &QNetworkReply::errorOccurred, this, &NetRequest::downloadError); @@ -129,7 +126,7 @@ void NetRequest::executeTask() connect(rep, &QNetworkReply::readyRead, this, &NetRequest::downloadReadyRead); } -void NetRequest::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +void NetRequest::onProgress(qint64 bytesReceived, qint64 bytesTotal) { auto now = m_clock.now(); auto elapsed = now - m_last_progress_time; @@ -237,7 +234,7 @@ auto NetRequest::handleRedirect() -> bool m_url = QUrl(redirect.toString()); qCDebug(logCat) << getUid().toString() << "Following redirect to " << m_url.toString(); - startAction(m_network); + executeTask(); return true; } @@ -334,4 +331,23 @@ auto NetRequest::abort() -> bool return true; } +int NetRequest::replyStatusCode() const +{ + return m_reply ? m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() : -1; +} + +QNetworkReply::NetworkError NetRequest::error() const +{ + return m_reply ? m_reply->error() : QNetworkReply::NoError; +} + +QUrl NetRequest::url() const +{ + return m_url; +} + +QString NetRequest::errorString() const +{ + return m_reply ? m_reply->errorString() : ""; +} } // namespace Net diff --git a/launcher/net/NetRequest.h b/launcher/net/NetRequest.h index 0b307b4f6..6c32215b0 100644 --- a/launcher/net/NetRequest.h +++ b/launcher/net/NetRequest.h @@ -39,20 +39,23 @@ #pragma once #include +#include +#include #include -#include "NetAction.h" +#include "HeaderProxy.h" #include "Sink.h" #include "Validator.h" #include "QObjectPtr.h" #include "net/Logging.h" +#include "tasks/Task.h" namespace Net { -class NetRequest : public NetAction { +class NetRequest : public Task { Q_OBJECT protected: - explicit NetRequest() : NetAction() {} + explicit NetRequest() : Task() {} public: using Ptr = shared_qobject_ptr; @@ -61,26 +64,30 @@ class NetRequest : public NetAction { public: ~NetRequest() override = default; - - void init() override {} - - public: void addValidator(Validator* v); auto abort() -> bool override; auto canAbort() const -> bool override { return true; } + void setNetwork(shared_qobject_ptr network) { m_network = network; } + void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr(proxy)); } + + virtual void init() {} + + QUrl url() const; + int replyStatusCode() const; + QNetworkReply::NetworkError error() const; + QString errorString() const; + private: auto handleRedirect() -> bool; virtual QNetworkReply* getReply(QNetworkRequest&) = 0; protected slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; - void downloadError(QNetworkReply::NetworkError error) override; - void sslErrors(const QList& errors) override; - void downloadFinished() override; - void downloadReadyRead() override; - - public slots: + void onProgress(qint64 bytesReceived, qint64 bytesTotal); + void downloadError(QNetworkReply::NetworkError error); + void sslErrors(const QList& errors); + void downloadFinished(); + void downloadReadyRead(); void executeTask() override; protected: @@ -93,6 +100,15 @@ class NetRequest : public NetAction { std::chrono::steady_clock m_clock; std::chrono::time_point m_last_progress_time; qint64 m_last_progress_bytes; + + shared_qobject_ptr m_network; + + /// the network reply + unique_qobject_ptr m_reply; + + /// source URL + QUrl m_url; + std::vector> m_headerProxies; }; } // namespace Net diff --git a/launcher/net/Sink.h b/launcher/net/Sink.h index fcdabf372..d1fd9de10 100644 --- a/launcher/net/Sink.h +++ b/launcher/net/Sink.h @@ -35,9 +35,8 @@ #pragma once -#include "net/NetAction.h" - #include "Validator.h" +#include "tasks/Task.h" namespace Net { class Sink { diff --git a/launcher/net/Validator.h b/launcher/net/Validator.h index 92ac6ea15..6d1945ee6 100644 --- a/launcher/net/Validator.h +++ b/launcher/net/Validator.h @@ -34,7 +34,7 @@ #pragma once -#include "net/NetAction.h" +#include namespace Net { class Validator { diff --git a/launcher/ui/dialogs/ProfileSetupDialog.cpp b/launcher/ui/dialogs/ProfileSetupDialog.cpp index 4b0c5b768..c5d4c2621 100644 --- a/launcher/ui/dialogs/ProfileSetupDialog.cpp +++ b/launcher/ui/dialogs/ProfileSetupDialog.cpp @@ -45,8 +45,9 @@ #include "ui/dialogs/ProgressDialog.h" #include -#include "minecraft/auth/AuthRequest.h" #include "minecraft/auth/Parsers.h" +#include "net/StaticHeaderProxy.h" +#include "net/Upload.h" ProfileSetupDialog::ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget* parent) : QDialog(parent), m_accountToSetup(accountToSetup), ui(new Ui::ProfileSetupDialog) @@ -150,28 +151,27 @@ void ProfileSetupDialog::checkName(const QString& name) currentCheck = name; isChecking = true; - auto token = m_accountToSetup->accessToken(); + QUrl url(QString("https://api.minecraftservices.com/minecraft/profile/name/%1/available").arg(name)); + auto headers = QList{ { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + { "Authorization", QString("Bearer %1").arg(m_accountToSetup->accessToken()).toUtf8() } }; - auto url = QString("https://api.minecraftservices.com/minecraft/profile/name/%1/available").arg(name); - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8()); + m_check_response.reset(new QByteArray()); + if (m_check_task) + disconnect(m_check_task.get(), nullptr, this, nullptr); + m_check_task = Net::Download::makeByteArray(url, m_check_response); + m_check_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::checkFinished); - requestor->get(request); + connect(m_check_task.get(), &Task::finished, this, &ProfileSetupDialog::checkFinished); + + m_check_task->setNetwork(APPLICATION->network()); + m_check_task->start(); } -void ProfileSetupDialog::checkFinished(QNetworkReply::NetworkError error, - QByteArray profileData, - [[maybe_unused]] QList headers) +void ProfileSetupDialog::checkFinished() { - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - - if (error == QNetworkReply::NoError) { - auto doc = QJsonDocument::fromJson(profileData); + if (m_check_task->error() == QNetworkReply::NoError) { + auto doc = QJsonDocument::fromJson(*m_check_response); auto root = doc.object(); auto statusValue = root.value("status").toString("INVALID"); if (statusValue == "AVAILABLE") { @@ -195,20 +195,22 @@ void ProfileSetupDialog::setupProfile(const QString& profileName) return; } - auto token = m_accountToSetup->accessToken(); - - auto url = QString("https://api.minecraftservices.com/minecraft/profile"); - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - request.setRawHeader("Accept", "application/json"); - request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8()); - QString payloadTemplate("{\"profileName\":\"%1\"}"); - auto profileData = payloadTemplate.arg(profileName).toUtf8(); - AuthRequest* requestor = new AuthRequest(this); - connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::setupProfileFinished); - requestor->post(request, profileData); + QUrl url("https://api.minecraftservices.com/minecraft/profile"); + auto headers = QList{ { "Content-Type", "application/json" }, + { "Accept", "application/json" }, + { "Authorization", QString("Bearer %1").arg(m_accountToSetup->accessToken()).toUtf8() } }; + + m_profile_response.reset(new QByteArray()); + m_profile_task = Net::Upload::makeByteArray(url, m_profile_response, payloadTemplate.arg(profileName).toUtf8()); + m_profile_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_profile_task.get(), &Task::finished, this, &ProfileSetupDialog::setupProfileFinished); + + m_profile_task->setNetwork(APPLICATION->network()); + m_profile_task->start(); + isWorking = true; auto button = ui->buttonBox->button(QDialogButtonBox::Cancel); @@ -244,22 +246,17 @@ struct MojangError { } // namespace -void ProfileSetupDialog::setupProfileFinished(QNetworkReply::NetworkError error, - QByteArray errorData, - [[maybe_unused]] QList headers) +void ProfileSetupDialog::setupProfileFinished() { - auto requestor = qobject_cast(QObject::sender()); - requestor->deleteLater(); - isWorking = false; - if (error == QNetworkReply::NoError) { + if (m_profile_task->error() == QNetworkReply::NoError) { /* * data contains the profile in the response * ... we could parse it and update the account, but let's just return back to the normal login flow instead... */ accept(); } else { - auto parsedError = MojangError::fromJSON(errorData); + auto parsedError = MojangError::fromJSON(*m_profile_response); ui->errorLabel->setVisible(true); ui->errorLabel->setText(tr("The server returned the following error:") + "\n\n" + parsedError.errorMessage); qDebug() << parsedError.rawError; diff --git a/launcher/ui/dialogs/ProfileSetupDialog.h b/launcher/ui/dialogs/ProfileSetupDialog.h index 09f8124e2..c005a4138 100644 --- a/launcher/ui/dialogs/ProfileSetupDialog.h +++ b/launcher/ui/dialogs/ProfileSetupDialog.h @@ -22,6 +22,8 @@ #include #include +#include "net/Download.h" +#include "net/Upload.h" namespace Ui { class ProfileSetupDialog; @@ -40,10 +42,10 @@ class ProfileSetupDialog : public QDialog { void on_buttonBox_rejected(); void nameEdited(const QString& name); - void checkFinished(QNetworkReply::NetworkError error, QByteArray data, QList headers); void startCheck(); - void setupProfileFinished(QNetworkReply::NetworkError error, QByteArray data, QList headers); + void checkFinished(); + void setupProfileFinished(); protected: void scheduleCheck(const QString& name); @@ -67,4 +69,10 @@ class ProfileSetupDialog : public QDialog { QString currentCheck; QTimer checkStartTimer; + + std::shared_ptr m_check_response; + Net::Download::Ptr m_check_task; + + std::shared_ptr m_profile_response; + Net::Upload::Ptr m_profile_task; }; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index f3c7ff60b..8a69e910d 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -331,7 +331,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) auto icon_fetch_action = Net::ApiDownload::makeCached(url, cache_entry); auto full_file_path = cache_entry->getFullPath(); - connect(icon_fetch_action.get(), &NetAction::succeeded, this, [=] { + connect(icon_fetch_action.get(), &Task::succeeded, this, [=] { auto icon = QIcon(full_file_path); QPixmapCache::insert(url.toString(), icon.pixmap(icon.actualSize({ 64, 64 }))); @@ -339,7 +339,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) emit dataChanged(index, index, { Qt::DecorationRole }); }); - connect(icon_fetch_action.get(), &NetAction::failed, this, [=] { + connect(icon_fetch_action.get(), &Task::failed, this, [=] { m_currently_running_icon_actions.remove(url); m_failed_icon_actions.insert(url); }); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index bac294b60..ccfe7eccb 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -352,10 +352,10 @@ void ModpackListModel::searchRequestForOneSucceeded(QJsonDocument& doc) void ModpackListModel::searchRequestFailed(QString reason) { auto failed_action = dynamic_cast(jobPtr.get())->getFailedActions().at(0); - if (!failed_action->m_reply) { + if (failed_action->replyStatusCode() == -1) { // Network error QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks.")); - } else if (failed_action->m_reply && failed_action->m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 409) { + } else if (failed_action->replyStatusCode() == 409) { // 409 Gone, notify user to update QMessageBox::critical(nullptr, tr("Error"), //: %1 refers to the launcher itself diff --git a/tests/Library_test.cpp b/tests/Library_test.cpp index 8b8d4c55c..9826abbdf 100644 --- a/tests/Library_test.cpp +++ b/tests/Library_test.cpp @@ -95,8 +95,8 @@ class LibraryTest : public QObject { auto downloads = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(downloads.size(), 1); QCOMPARE(failedFiles, {}); - NetAction::Ptr dl = downloads[0]; - QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar")); + Net::NetRequest::Ptr dl = downloads[0]; + QCOMPARE(dl->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar")); } void test_legacy_url_local_broken() { @@ -147,7 +147,7 @@ class LibraryTest : public QObject { QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); auto dl = dls[0]; - QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar")); + QCOMPARE(dl->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar")); } } void test_legacy_native_arch() @@ -170,8 +170,8 @@ class LibraryTest : public QObject { auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar")); - QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar")); + QCOMPARE(dls[0]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar")); + QCOMPARE(dls[1]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar")); } r.system = "windows"; { @@ -185,8 +185,8 @@ class LibraryTest : public QObject { auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar")); - QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar")); + QCOMPARE(dls[0]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar")); + QCOMPARE(dls[1]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar")); } r.system = "osx"; { @@ -200,8 +200,8 @@ class LibraryTest : public QObject { auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar")); - QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar")); + QCOMPARE(dls[0]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar")); + QCOMPARE(dls[1]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar")); } } void test_legacy_native_arch_local_override() @@ -244,7 +244,7 @@ class LibraryTest : public QObject { auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar")); + QCOMPARE(dls[0]->url(), QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar")); } r.system = "osx"; test->setHint("local"); @@ -300,7 +300,7 @@ class LibraryTest : public QObject { auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/" + QCOMPARE(dls[0]->url(), QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/" "lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); } void test_onenine_native_arch() @@ -317,9 +317,9 @@ class LibraryTest : public QObject { auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); - QCOMPARE(dls[0]->m_url, + QCOMPARE(dls[0]->url(), QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); - QCOMPARE(dls[1]->m_url, + QCOMPARE(dls[1]->url(), QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); } From e285a85fe88e78ddced5c0d839056f2132e4c3b6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 14 May 2024 00:20:32 +0300 Subject: [PATCH 380/755] Removed AccountTask Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 3 - launcher/LaunchController.cpp | 1 - launcher/minecraft/auth/AccountList.cpp | 1 - launcher/minecraft/auth/AccountList.h | 3 +- launcher/minecraft/auth/AccountTask.cpp | 134 ------------------ launcher/minecraft/auth/AccountTask.h | 92 ------------ launcher/minecraft/auth/AuthStep.cpp | 3 - launcher/minecraft/auth/AuthStep.h | 19 ++- launcher/minecraft/auth/MinecraftAccount.cpp | 10 +- launcher/minecraft/auth/MinecraftAccount.h | 14 +- launcher/minecraft/auth/flows/AuthFlow.cpp | 93 ++++++++++-- launcher/minecraft/auth/flows/AuthFlow.h | 32 +++-- .../auth/steps/LauncherLoginStep.cpp | 1 - launcher/net/NetRequest.cpp | 2 + launcher/ui/dialogs/MSALoginDialog.cpp | 6 +- launcher/ui/dialogs/MSALoginDialog.h | 3 +- launcher/ui/dialogs/OfflineLoginDialog.cpp | 2 - 17 files changed, 143 insertions(+), 276 deletions(-) delete mode 100644 launcher/minecraft/auth/AccountTask.cpp delete mode 100644 launcher/minecraft/auth/AccountTask.h delete mode 100644 launcher/minecraft/auth/AuthStep.cpp diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index cf1ab8798..e4143ee28 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -209,11 +209,8 @@ set(MINECRAFT_SOURCES minecraft/auth/AccountData.h minecraft/auth/AccountList.cpp minecraft/auth/AccountList.h - minecraft/auth/AccountTask.cpp - minecraft/auth/AccountTask.h minecraft/auth/AuthSession.cpp minecraft/auth/AuthSession.h - minecraft/auth/AuthStep.cpp minecraft/auth/AuthStep.h minecraft/auth/MinecraftAccount.cpp minecraft/auth/MinecraftAccount.h diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index a30f99439..cf8d0a794 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -57,7 +57,6 @@ #include "BuildConfig.h" #include "JavaCommon.h" #include "launch/steps/TextPrint.h" -#include "minecraft/auth/AccountTask.h" #include "tasks/Task.h" LaunchController::LaunchController(QObject* parent) : Task(parent) {} diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index af83502bc..d276d4c41 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -35,7 +35,6 @@ #include "AccountList.h" #include "AccountData.h" -#include "AccountTask.h" #include "tasks/Task.h" #include diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index 039730739..b6038edb7 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -36,6 +36,7 @@ #pragma once #include "MinecraftAccount.h" +#include "minecraft/auth/flows/AuthFlow.h" #include #include @@ -144,7 +145,7 @@ class AccountList : public QAbstractListModel { QList m_refreshQueue; QTimer* m_refreshTimer; QTimer* m_nextTimer; - shared_qobject_ptr m_currentTask; + shared_qobject_ptr m_currentTask; /*! * Called whenever the list changes. diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp deleted file mode 100644 index 4c3d6ee19..000000000 --- a/launcher/minecraft/auth/AccountTask.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AccountTask.h" -#include "MinecraftAccount.h" - -#include -#include -#include -#include -#include -#include - -#include - -AccountTask::AccountTask(AccountData* data, QObject* parent) : Task(parent), m_data(data) -{ - changeState(AccountTaskState::STATE_CREATED); -} - -QString AccountTask::getStateMessage() const -{ - switch (m_taskState) { - case AccountTaskState::STATE_CREATED: - return "Waiting..."; - case AccountTaskState::STATE_WORKING: - return tr("Sending request to auth servers..."); - case AccountTaskState::STATE_SUCCEEDED: - return tr("Authentication task succeeded."); - case AccountTaskState::STATE_OFFLINE: - return tr("Failed to contact the authentication server."); - case AccountTaskState::STATE_DISABLED: - return tr("Client ID has changed. New session needs to be created."); - case AccountTaskState::STATE_FAILED_SOFT: - return tr("Encountered an error during authentication."); - case AccountTaskState::STATE_FAILED_HARD: - return tr("Failed to authenticate. The session has expired."); - case AccountTaskState::STATE_FAILED_GONE: - return tr("Failed to authenticate. The account no longer exists."); - default: - return tr("..."); - } -} - -bool AccountTask::changeState(AccountTaskState newState, QString reason) -{ - m_taskState = newState; - // FIXME: virtual method invoked in constructor. - // We want that behavior, but maybe make it less weird? - setStatus(getStateMessage()); - switch (newState) { - case AccountTaskState::STATE_CREATED: { - m_data->errorString.clear(); - return true; - } - case AccountTaskState::STATE_WORKING: { - m_data->accountState = AccountState::Working; - return true; - } - case AccountTaskState::STATE_SUCCEEDED: { - m_data->accountState = AccountState::Online; - emitSucceeded(); - return false; - } - case AccountTaskState::STATE_OFFLINE: { - m_data->errorString = reason; - m_data->accountState = AccountState::Offline; - emitFailed(reason); - return false; - } - case AccountTaskState::STATE_DISABLED: { - m_data->errorString = reason; - m_data->accountState = AccountState::Disabled; - emitFailed(reason); - return false; - } - case AccountTaskState::STATE_FAILED_SOFT: { - m_data->errorString = reason; - m_data->accountState = AccountState::Errored; - emitFailed(reason); - return false; - } - case AccountTaskState::STATE_FAILED_HARD: { - m_data->errorString = reason; - m_data->accountState = AccountState::Expired; - emitFailed(reason); - return false; - } - case AccountTaskState::STATE_FAILED_GONE: { - m_data->errorString = reason; - m_data->accountState = AccountState::Gone; - emitFailed(reason); - return false; - } - default: { - QString error = tr("Unknown account task state: %1").arg(int(newState)); - m_data->accountState = AccountState::Errored; - emitFailed(error); - return false; - } - } -} diff --git a/launcher/minecraft/auth/AccountTask.h b/launcher/minecraft/auth/AccountTask.h deleted file mode 100644 index 82332c0b9..000000000 --- a/launcher/minecraft/auth/AccountTask.h +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include -#include - -#include "MinecraftAccount.h" - -class QNetworkReply; - -/** - * Enum for describing the state of the current task. - * Used by the getStateMessage function to determine what the status message should be. - */ -enum class AccountTaskState { - STATE_CREATED, - STATE_WORKING, - STATE_SUCCEEDED, - STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn - STATE_FAILED_SOFT, //!< soft failure. authentication went through partially - STATE_FAILED_HARD, //!< hard failure. main tokens are invalid - STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists - STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way -}; - -class AccountTask : public Task { - Q_OBJECT - public: - explicit AccountTask(AccountData* data, QObject* parent = 0); - virtual ~AccountTask(){}; - - AccountTaskState m_taskState = AccountTaskState::STATE_CREATED; - - AccountTaskState taskState() { return m_taskState; } - - signals: - void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); - void hideVerificationUriAndCode(); - - protected: - /** - * Returns the state message for the given state. - * Used to set the status message for the task. - * Should be overridden by subclasses that want to change messages for a given state. - */ - virtual QString getStateMessage() const; - - protected slots: - // NOTE: true -> non-terminal state, false -> terminal state - bool changeState(AccountTaskState newState, QString reason = QString()); - - protected: - AccountData* m_data = nullptr; -}; diff --git a/launcher/minecraft/auth/AuthStep.cpp b/launcher/minecraft/auth/AuthStep.cpp deleted file mode 100644 index 6b78c415a..000000000 --- a/launcher/minecraft/auth/AuthStep.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "AuthStep.h" - -AuthStep::AuthStep(AccountData* data) : QObject(nullptr), m_data(data) {} diff --git a/launcher/minecraft/auth/AuthStep.h b/launcher/minecraft/auth/AuthStep.h index b837b5703..dccbec11e 100644 --- a/launcher/minecraft/auth/AuthStep.h +++ b/launcher/minecraft/auth/AuthStep.h @@ -3,18 +3,31 @@ #include #include -#include "AccountTask.h" #include "QObjectPtr.h" #include "minecraft/auth/AccountData.h" +/** + * Enum for describing the state of the current task. + * Used by the getStateMessage function to determine what the status message should be. + */ +enum class AccountTaskState { + STATE_CREATED, + STATE_WORKING, + STATE_SUCCEEDED, + STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn + STATE_FAILED_SOFT, //!< soft failure. authentication went through partially + STATE_FAILED_HARD, //!< hard failure. main tokens are invalid + STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists + STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way +}; + class AuthStep : public QObject { Q_OBJECT public: using Ptr = shared_qobject_ptr; - public: - explicit AuthStep(AccountData* data); + explicit AuthStep(AccountData* data) : QObject(nullptr), m_data(data){}; virtual ~AuthStep() noexcept = default; virtual QString describe() = 0; diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index db4d1c07b..d927cd6b4 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -53,6 +53,8 @@ #include "flows/MSA.h" #include "flows/Offline.h" #include "minecraft/auth/AccountData.h" +#include "minecraft/auth/flows/AuthFlow.h" +#include "tasks/Task.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { @@ -120,7 +122,7 @@ QPixmap MinecraftAccount::getFace() const return skin.scaled(64, 64, Qt::KeepAspectRatio); } -shared_qobject_ptr MinecraftAccount::loginMSA() +shared_qobject_ptr MinecraftAccount::loginMSA() { Q_ASSERT(m_currentTask.get() == nullptr); @@ -132,7 +134,7 @@ shared_qobject_ptr MinecraftAccount::loginMSA() return m_currentTask; } -shared_qobject_ptr MinecraftAccount::loginOffline() +shared_qobject_ptr MinecraftAccount::loginOffline() { Q_ASSERT(m_currentTask.get() == nullptr); @@ -144,7 +146,7 @@ shared_qobject_ptr MinecraftAccount::loginOffline() return m_currentTask; } -shared_qobject_ptr MinecraftAccount::refresh() +shared_qobject_ptr MinecraftAccount::refresh() { if (m_currentTask) { return m_currentTask; @@ -163,7 +165,7 @@ shared_qobject_ptr MinecraftAccount::refresh() return m_currentTask; } -shared_qobject_ptr MinecraftAccount::currentTask() +shared_qobject_ptr MinecraftAccount::currentTask() { return m_currentTask; } diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index f773b3bc9..24600cb73 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -43,15 +43,13 @@ #include #include -#include - #include "AccountData.h" #include "AuthSession.h" #include "QObjectPtr.h" #include "Usable.h" +#include "minecraft/auth/flows/AuthFlow.h" class Task; -class AccountTask; class MinecraftAccount; using MinecraftAccountPtr = shared_qobject_ptr; @@ -97,13 +95,13 @@ class MinecraftAccount : public QObject, public Usable { QJsonObject saveToJson() const; public: /* manipulation */ - shared_qobject_ptr loginMSA(); + shared_qobject_ptr loginMSA(); - shared_qobject_ptr loginOffline(); + shared_qobject_ptr loginOffline(); - shared_qobject_ptr refresh(); + shared_qobject_ptr refresh(); - shared_qobject_ptr currentTask(); + shared_qobject_ptr currentTask(); public: /* queries */ QString internalId() const { return data.internalId; } @@ -166,7 +164,7 @@ class MinecraftAccount : public QObject, public Usable { AccountData data; // current task we are executing here - shared_qobject_ptr m_currentTask; + shared_qobject_ptr m_currentTask; protected: /* methods */ void incrementUses() override; diff --git a/launcher/minecraft/auth/flows/AuthFlow.cpp b/launcher/minecraft/auth/flows/AuthFlow.cpp index c51839a8c..20d90f504 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.cpp +++ b/launcher/minecraft/auth/flows/AuthFlow.cpp @@ -4,11 +4,13 @@ #include #include "AuthFlow.h" -#include "katabasis/Globals.h" #include -AuthFlow::AuthFlow(AccountData* data, QObject* parent) : AccountTask(data, parent) {} +AuthFlow::AuthFlow(AccountData* data, QObject* parent) : Task(parent), m_data(data) +{ + changeState(AccountTaskState::STATE_CREATED); +} void AuthFlow::succeed() { @@ -46,16 +48,26 @@ void AuthFlow::nextStep() QString AuthFlow::getStateMessage() const { switch (m_taskState) { - case AccountTaskState::STATE_WORKING: { - if (m_currentStep) { + case AccountTaskState::STATE_CREATED: + return "Waiting..."; + case AccountTaskState::STATE_WORKING: + if (m_currentStep) return m_currentStep->describe(); - } else { - return tr("Working..."); - } - } - default: { - return AccountTask::getStateMessage(); - } + return tr("Working..."); + case AccountTaskState::STATE_SUCCEEDED: + return tr("Authentication task succeeded."); + case AccountTaskState::STATE_OFFLINE: + return tr("Failed to contact the authentication server."); + case AccountTaskState::STATE_DISABLED: + return tr("Client ID has changed. New session needs to be created."); + case AccountTaskState::STATE_FAILED_SOFT: + return tr("Encountered an error during authentication."); + case AccountTaskState::STATE_FAILED_HARD: + return tr("Failed to authenticate. The session has expired."); + case AccountTaskState::STATE_FAILED_GONE: + return tr("Failed to authenticate. The account no longer exists."); + default: + return tr("..."); } } @@ -65,3 +77,62 @@ void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) nextStep(); } } + +bool AuthFlow::changeState(AccountTaskState newState, QString reason) +{ + m_taskState = newState; + // FIXME: virtual method invoked in constructor. + // We want that behavior, but maybe make it less weird? + setStatus(getStateMessage()); + switch (newState) { + case AccountTaskState::STATE_CREATED: { + m_data->errorString.clear(); + return true; + } + case AccountTaskState::STATE_WORKING: { + m_data->accountState = AccountState::Working; + return true; + } + case AccountTaskState::STATE_SUCCEEDED: { + m_data->accountState = AccountState::Online; + emitSucceeded(); + return false; + } + case AccountTaskState::STATE_OFFLINE: { + m_data->errorString = reason; + m_data->accountState = AccountState::Offline; + emitFailed(reason); + return false; + } + case AccountTaskState::STATE_DISABLED: { + m_data->errorString = reason; + m_data->accountState = AccountState::Disabled; + emitFailed(reason); + return false; + } + case AccountTaskState::STATE_FAILED_SOFT: { + m_data->errorString = reason; + m_data->accountState = AccountState::Errored; + emitFailed(reason); + return false; + } + case AccountTaskState::STATE_FAILED_HARD: { + m_data->errorString = reason; + m_data->accountState = AccountState::Expired; + emitFailed(reason); + return false; + } + case AccountTaskState::STATE_FAILED_GONE: { + m_data->errorString = reason; + m_data->accountState = AccountState::Gone; + emitFailed(reason); + return false; + } + default: { + QString error = tr("Unknown account task state: %1").arg(int(newState)); + m_data->accountState = AccountState::Errored; + emitFailed(error); + return false; + } + } +} \ No newline at end of file diff --git a/launcher/minecraft/auth/flows/AuthFlow.h b/launcher/minecraft/auth/flows/AuthFlow.h index e39e926dd..10fdd22fa 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.h +++ b/launcher/minecraft/auth/flows/AuthFlow.h @@ -10,32 +10,48 @@ #include #include "minecraft/auth/AccountData.h" -#include "minecraft/auth/AccountTask.h" #include "minecraft/auth/AuthStep.h" +#include "tasks/Task.h" -class AuthFlow : public AccountTask { +class AuthFlow : public Task { Q_OBJECT public: explicit AuthFlow(AccountData* data, QObject* parent = 0); + virtual ~AuthFlow() = default; Katabasis::Validity validity() { return m_data->validity_; }; - QString getStateMessage() const override; - void executeTask() override; + AccountTaskState taskState() { return m_taskState; } + signals: + void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); + void hideVerificationUriAndCode(); + void activityChanged(Katabasis::Activity activity); + protected: + /** + * Returns the state message for the given state. + * Used to set the status message for the task. + * Should be overridden by subclasses that want to change messages for a given state. + */ + virtual QString getStateMessage() const; + void succeed(); + void nextStep(); + + protected slots: + // NOTE: true -> non-terminal state, false -> terminal state + bool changeState(AccountTaskState newState, QString reason = QString()); + private slots: void stepFinished(AccountTaskState resultingState, QString message); protected: - void succeed(); - void nextStep(); - - protected: + AccountTaskState m_taskState = AccountTaskState::STATE_CREATED; QList m_steps; AuthStep::Ptr m_currentStep; + AccountData* m_data = nullptr; }; diff --git a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp index 8981c5752..d72346c74 100644 --- a/launcher/minecraft/auth/steps/LauncherLoginStep.cpp +++ b/launcher/minecraft/auth/steps/LauncherLoginStep.cpp @@ -5,7 +5,6 @@ #include "Application.h" #include "Logging.h" -#include "minecraft/auth/AccountTask.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" #include "net/StaticHeaderProxy.h" diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 009213332..29de2febf 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -170,6 +170,8 @@ void NetRequest::downloadError(QNetworkReply::NetworkError error) } // error happened during download. qCCritical(logCat) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; + if (m_reply) + qCCritical(logCat) << getUid().toString() << "HTTP Status " << replyStatusCode() << ";error " << errorString(); m_state = State::Failed; } } diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index 7df423412..e396c01f6 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -37,7 +37,7 @@ #include "ui_MSALoginDialog.h" #include "DesktopServices.h" -#include "minecraft/auth/AccountTask.h" +#include "minecraft/auth/flows/AuthFlow.h" #include #include @@ -67,8 +67,8 @@ int MSALoginDialog::exec() connect(m_loginTask.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &MSALoginDialog::onTaskStatus); connect(m_loginTask.get(), &Task::progress, this, &MSALoginDialog::onTaskProgress); - connect(m_loginTask.get(), &AccountTask::showVerificationUriAndCode, this, &MSALoginDialog::showVerificationUriAndCode); - connect(m_loginTask.get(), &AccountTask::hideVerificationUriAndCode, this, &MSALoginDialog::hideVerificationUriAndCode); + connect(m_loginTask.get(), &AuthFlow::showVerificationUriAndCode, this, &MSALoginDialog::showVerificationUriAndCode); + connect(m_loginTask.get(), &AuthFlow::hideVerificationUriAndCode, this, &MSALoginDialog::hideVerificationUriAndCode); connect(&m_externalLoginTimer, &QTimer::timeout, this, &MSALoginDialog::externalLoginTick); m_loginTask->start(); diff --git a/launcher/ui/dialogs/MSALoginDialog.h b/launcher/ui/dialogs/MSALoginDialog.h index 03e276bc0..531ae3cc1 100644 --- a/launcher/ui/dialogs/MSALoginDialog.h +++ b/launcher/ui/dialogs/MSALoginDialog.h @@ -20,6 +20,7 @@ #include #include "minecraft/auth/MinecraftAccount.h" +#include "minecraft/auth/flows/AuthFlow.h" namespace Ui { class MSALoginDialog; @@ -52,7 +53,7 @@ class MSALoginDialog : public QDialog { private: Ui::MSALoginDialog* ui; MinecraftAccountPtr m_account; - shared_qobject_ptr m_loginTask; + shared_qobject_ptr m_loginTask; QTimer m_externalLoginTimer; int m_externalLoginElapsed = 0; int m_externalLoginTimeout = 0; diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp index 137620be4..cd3102135 100644 --- a/launcher/ui/dialogs/OfflineLoginDialog.cpp +++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp @@ -1,8 +1,6 @@ #include "OfflineLoginDialog.h" #include "ui_OfflineLoginDialog.h" -#include "minecraft/auth/AccountTask.h" - #include OfflineLoginDialog::OfflineLoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog) From c2ed50627dfb09d3ef99f38a70955d8ef2803de3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 14 May 2024 00:36:35 +0300 Subject: [PATCH 381/755] Added back qt version check Signed-off-by: Trial97 --- launcher/net/NetRequest.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 29de2febf..90ce15451 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -105,7 +105,9 @@ void NetRequest::executeTask() header_proxy->writeHeaders(request); } - request.setTransferTimeout(QNetworkRequest::DefaultTransferTimeoutConstant); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + request.setTransferTimeout(); +#endif m_last_progress_time = m_clock.now(); m_last_progress_bytes = 0; From 80d8e3ee06e8f71503523d4f388b1f729ae7a75e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 May 2024 12:53:06 +0300 Subject: [PATCH 382/755] Remove katabasis Signed-off-by: Trial97 --- .github/workflows/build.yml | 25 +- CMakeLists.txt | 5 +- COPYING.md | 26 - launcher/CMakeLists.txt | 3 +- launcher/minecraft/auth/AccountData.h | 33 +- launcher/minecraft/auth/AuthStep.h | 2 - launcher/minecraft/auth/flows/AuthFlow.cpp | 42 +- launcher/minecraft/auth/flows/AuthFlow.h | 11 - launcher/minecraft/auth/steps/MSAStep.cpp | 102 ++-- launcher/minecraft/auth/steps/MSAStep.h | 9 +- launcher/net/NetRequest.cpp | 4 +- launcher/qtlogging.ini | 1 - launcher/ui/dialogs/MSALoginDialog.cpp | 52 -- launcher/ui/dialogs/MSALoginDialog.h | 8 - libraries/README.md | 8 - libraries/katabasis/.gitignore | 2 - libraries/katabasis/CMakeLists.txt | 58 --- libraries/katabasis/LICENSE | 23 - libraries/katabasis/README.md | 36 -- libraries/katabasis/acknowledgements.md | 108 ---- libraries/katabasis/include/katabasis/Bits.h | 33 -- .../katabasis/include/katabasis/DeviceFlow.h | 149 ------ .../katabasis/include/katabasis/Globals.h | 59 --- .../katabasis/include/katabasis/PollServer.h | 51 -- libraries/katabasis/include/katabasis/Reply.h | 63 --- .../include/katabasis/RequestParameter.h | 13 - libraries/katabasis/src/DeviceFlow.cpp | 467 ------------------ libraries/katabasis/src/JsonResponse.cpp | 27 - libraries/katabasis/src/JsonResponse.h | 12 - libraries/katabasis/src/PollServer.cpp | 118 ----- libraries/katabasis/src/Reply.cpp | 74 --- 31 files changed, 99 insertions(+), 1525 deletions(-) delete mode 100644 libraries/katabasis/.gitignore delete mode 100644 libraries/katabasis/CMakeLists.txt delete mode 100644 libraries/katabasis/LICENSE delete mode 100644 libraries/katabasis/README.md delete mode 100644 libraries/katabasis/acknowledgements.md delete mode 100644 libraries/katabasis/include/katabasis/Bits.h delete mode 100644 libraries/katabasis/include/katabasis/DeviceFlow.h delete mode 100644 libraries/katabasis/include/katabasis/Globals.h delete mode 100644 libraries/katabasis/include/katabasis/PollServer.h delete mode 100644 libraries/katabasis/include/katabasis/Reply.h delete mode 100644 libraries/katabasis/include/katabasis/RequestParameter.h delete mode 100644 libraries/katabasis/src/DeviceFlow.cpp delete mode 100644 libraries/katabasis/src/JsonResponse.cpp delete mode 100644 libraries/katabasis/src/JsonResponse.h delete mode 100644 libraries/katabasis/src/PollServer.cpp delete mode 100644 libraries/katabasis/src/Reply.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e502318a3..999029bd2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,14 +57,14 @@ jobs: qt_host: linux qt_arch: "" qt_version: "5.12.8" - qt_modules: "" + qt_modules: "qtnetworkauth" - os: ubuntu-20.04 qt_ver: 6 qt_host: linux qt_arch: "" qt_version: "6.2.4" - qt_modules: "qt5compat qtimageformats" + qt_modules: "qt5compat qtimageformats qtnetworkauth" - os: windows-2022 name: "Windows-MinGW-w64" @@ -78,9 +78,9 @@ jobs: vcvars_arch: "amd64" qt_ver: 6 qt_host: windows - qt_arch: '' - qt_version: '6.7.0' - qt_modules: 'qt5compat qtimageformats' + qt_arch: "" + qt_version: "6.7.0" + qt_modules: "qt5compat qtimageformats qtnetworkauth" - os: windows-2022 name: "Windows-MSVC-arm64" @@ -89,18 +89,18 @@ jobs: vcvars_arch: "amd64_arm64" qt_ver: 6 qt_host: windows - qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.7.0' - qt_modules: 'qt5compat qtimageformats' + qt_arch: "win64_msvc2019_arm64" + qt_version: "6.7.0" + qt_modules: "qt5compat qtimageformats qtnetworkauth" - os: macos-12 name: macOS macosx_deployment_target: 11.0 qt_ver: 6 qt_host: mac - qt_arch: '' - qt_version: '6.7.0' - qt_modules: 'qt5compat qtimageformats' + qt_arch: "" + qt_version: "6.7.0" + qt_modules: "qt5compat qtimageformats qtnetworkauth" - os: macos-12 name: macOS-Legacy @@ -108,7 +108,7 @@ jobs: qt_ver: 5 qt_host: mac qt_version: "5.15.2" - qt_modules: "" + qt_modules: "qtnetworkauth" runs-on: ${{ matrix.os }} @@ -150,6 +150,7 @@ jobs: quazip-qt6:p ccache:p qt6-5compat:p + qt6-networkauth:p cmark:p - name: Force newer ccache diff --git a/CMakeLists.txt b/CMakeLists.txt index 63408ec21..5c3987406 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -282,7 +282,7 @@ endif() include(QtVersionlessBackport) if(Launcher_QT_VERSION_MAJOR EQUAL 5) set(QT_VERSION_MAJOR 5) - find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml) + find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml NetworkAuth) if(NOT Launcher_FORCE_BUNDLED_LIBS) find_package(QuaZip-Qt5 1.3 QUIET) @@ -296,7 +296,7 @@ if(Launcher_QT_VERSION_MAJOR EQUAL 5) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE") elseif(Launcher_QT_VERSION_MAJOR EQUAL 6) set(QT_VERSION_MAJOR 6) - find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat) + find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat NetworkAuth) list(APPEND Launcher_QT_LIBS Qt6::Core5Compat) if(NOT Launcher_FORCE_BUNDLED_LIBS) @@ -523,7 +523,6 @@ if(NOT cmark_FOUND) else() message(STATUS "Using system cmark") endif() -add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much add_subdirectory(libraries/gamemode) add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API if (NOT ghc_filesystem_FOUND) diff --git a/COPYING.md b/COPYING.md index f14e2958e..111587060 100644 --- a/COPYING.md +++ b/COPYING.md @@ -333,32 +333,6 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -## O2 (Katabasis fork) - - Copyright (c) 2012, Akos Polster - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ## Gamemode Copyright (c) 2017-2022, Feral Interactive diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index e4143ee28..e5eb4b733 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1234,7 +1234,6 @@ target_link_libraries(Launcher_logic tomlplusplus::tomlplusplus qdcss BuildConfig - Katabasis Qt${QT_VERSION_MAJOR}::Widgets ghcFilesystem::ghc_filesystem ) @@ -1252,6 +1251,7 @@ target_link_libraries(Launcher_logic Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::NetworkAuth ${Launcher_QT_LIBS} ) target_link_libraries(Launcher_logic @@ -1322,7 +1322,6 @@ if(Launcher_BUILD_UPDATER) Qt${QT_VERSION_MAJOR}::Network ${Launcher_QT_LIBS} cmark::cmark - Katabasis ) add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index bac77e17f..71f1d00b2 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -34,12 +34,43 @@ */ #pragma once -#include #include #include #include #include +#include +#include +#include +#include + +namespace Katabasis { +enum class Activity { + Idle, + LoggingIn, + LoggingOut, + Refreshing, + FailedSoft, //!< soft failure. this generally means the user auth details haven't been invalidated + FailedHard, //!< hard failure. auth is invalid + FailedGone, //!< hard failure. auth is invalid, and the account no longer exists + Succeeded +}; + +enum class Validity { None, Assumed, Certain }; + +struct Token { + QDateTime issueInstant; + QDateTime notAfter; + QString token; + QString refresh_token; + QVariantMap extra; + + Validity validity = Validity::None; + bool persistent = true; +}; + +} // namespace Katabasis + struct Skin { QString id; QString url; diff --git a/launcher/minecraft/auth/AuthStep.h b/launcher/minecraft/auth/AuthStep.h index dccbec11e..4d2cf69c1 100644 --- a/launcher/minecraft/auth/AuthStep.h +++ b/launcher/minecraft/auth/AuthStep.h @@ -37,8 +37,6 @@ class AuthStep : public QObject { signals: void finished(AccountTaskState resultingState, QString message); - void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); - void hideVerificationUriAndCode(); protected: AccountData* m_data; diff --git a/launcher/minecraft/auth/flows/AuthFlow.cpp b/launcher/minecraft/auth/flows/AuthFlow.cpp index 20d90f504..1b5e01569 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.cpp +++ b/launcher/minecraft/auth/flows/AuthFlow.cpp @@ -21,6 +21,7 @@ void AuthFlow::succeed() void AuthFlow::executeTask() { if (m_currentStep) { + emitFailed("No task"); return; } changeState(AccountTaskState::STATE_WORKING, tr("Initializing")); @@ -39,38 +40,10 @@ void AuthFlow::nextStep() qDebug() << "AuthFlow:" << m_currentStep->describe(); m_steps.pop_front(); connect(m_currentStep.get(), &AuthStep::finished, this, &AuthFlow::stepFinished); - connect(m_currentStep.get(), &AuthStep::showVerificationUriAndCode, this, &AuthFlow::showVerificationUriAndCode); - connect(m_currentStep.get(), &AuthStep::hideVerificationUriAndCode, this, &AuthFlow::hideVerificationUriAndCode); m_currentStep->perform(); } -QString AuthFlow::getStateMessage() const -{ - switch (m_taskState) { - case AccountTaskState::STATE_CREATED: - return "Waiting..."; - case AccountTaskState::STATE_WORKING: - if (m_currentStep) - return m_currentStep->describe(); - return tr("Working..."); - case AccountTaskState::STATE_SUCCEEDED: - return tr("Authentication task succeeded."); - case AccountTaskState::STATE_OFFLINE: - return tr("Failed to contact the authentication server."); - case AccountTaskState::STATE_DISABLED: - return tr("Client ID has changed. New session needs to be created."); - case AccountTaskState::STATE_FAILED_SOFT: - return tr("Encountered an error during authentication."); - case AccountTaskState::STATE_FAILED_HARD: - return tr("Failed to authenticate. The session has expired."); - case AccountTaskState::STATE_FAILED_GONE: - return tr("Failed to authenticate. The account no longer exists."); - default: - return tr("..."); - } -} - void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) { if (changeState(resultingState, message)) { @@ -81,54 +54,61 @@ void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) bool AuthFlow::changeState(AccountTaskState newState, QString reason) { m_taskState = newState; - // FIXME: virtual method invoked in constructor. - // We want that behavior, but maybe make it less weird? - setStatus(getStateMessage()); + setDetails(reason); switch (newState) { case AccountTaskState::STATE_CREATED: { + setStatus(tr("Waiting...")); m_data->errorString.clear(); return true; } case AccountTaskState::STATE_WORKING: { + setStatus(m_currentStep ? m_currentStep->describe() : tr("Working...")); m_data->accountState = AccountState::Working; return true; } case AccountTaskState::STATE_SUCCEEDED: { + setStatus(tr("Authentication task succeeded.")); m_data->accountState = AccountState::Online; emitSucceeded(); return false; } case AccountTaskState::STATE_OFFLINE: { + setStatus(tr("Failed to contact the authentication server.")); m_data->errorString = reason; m_data->accountState = AccountState::Offline; emitFailed(reason); return false; } case AccountTaskState::STATE_DISABLED: { + setStatus(tr("Client ID has changed. New session needs to be created.")); m_data->errorString = reason; m_data->accountState = AccountState::Disabled; emitFailed(reason); return false; } case AccountTaskState::STATE_FAILED_SOFT: { + setStatus(tr("Encountered an error during authentication.")); m_data->errorString = reason; m_data->accountState = AccountState::Errored; emitFailed(reason); return false; } case AccountTaskState::STATE_FAILED_HARD: { + setStatus(tr("Failed to authenticate. The session has expired.")); m_data->errorString = reason; m_data->accountState = AccountState::Expired; emitFailed(reason); return false; } case AccountTaskState::STATE_FAILED_GONE: { + setStatus(tr("Failed to authenticate. The account no longer exists.")); m_data->errorString = reason; m_data->accountState = AccountState::Gone; emitFailed(reason); return false; } default: { + setStatus(tr("...")); QString error = tr("Unknown account task state: %1").arg(int(newState)); m_data->accountState = AccountState::Errored; emitFailed(error); diff --git a/launcher/minecraft/auth/flows/AuthFlow.h b/launcher/minecraft/auth/flows/AuthFlow.h index 10fdd22fa..de563c3c5 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.h +++ b/launcher/minecraft/auth/flows/AuthFlow.h @@ -7,8 +7,6 @@ #include #include -#include - #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AuthStep.h" #include "tasks/Task.h" @@ -27,18 +25,9 @@ class AuthFlow : public Task { AccountTaskState taskState() { return m_taskState; } signals: - void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); - void hideVerificationUriAndCode(); - void activityChanged(Katabasis::Activity activity); protected: - /** - * Returns the state message for the given state. - * Used to set the status message for the task. - * Should be overridden by subclasses that want to change messages for a given state. - */ - virtual QString getStateMessage() const; void succeed(); void nextStep(); diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 0a4b7c814..71bd25096 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -35,28 +35,44 @@ #include "MSAStep.h" +#include +#include +#include #include #include "Application.h" -#include "Logging.h" - -using OAuth2 = Katabasis::DeviceFlow; -using Activity = Katabasis::Activity; MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) { m_clientId = APPLICATION->getMSAClientID(); - OAuth2::Options opts; - opts.scope = "XboxLive.signin offline_access"; - opts.clientIdentifier = m_clientId; - opts.authorizationUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"; - opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; - // FIXME: OAuth2 is not aware of our fancy shared pointers - m_oauth2 = new OAuth2(opts, m_data->msaToken, this, APPLICATION->network().get()); + auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this); + oauth2.setReplyHandler(replyHandler); + oauth2.setAuthorizationUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize")); + oauth2.setAccessTokenUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/token")); + oauth2.setScope("XboxLive.SignIn XboxLive.offline_access"); + oauth2.setClientIdentifier(m_clientId); + oauth2.setNetworkAccessManager(APPLICATION->network().get()); - connect(m_oauth2, &OAuth2::activityChanged, this, &MSAStep::onOAuthActivityChanged); - connect(m_oauth2, &OAuth2::showVerificationUriAndCode, this, &MSAStep::showVerificationUriAndCode); + connect(&oauth2, &QOAuth2AuthorizationCodeFlow::granted, this, [this] { + m_data->msaClientID = oauth2.clientIdentifier(); + m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc(); + m_data->msaToken.notAfter = oauth2.expirationAt(); + m_data->msaToken.extra = oauth2.extraTokens(); + m_data->msaToken.refresh_token = oauth2.refreshToken(); + m_data->msaToken.token = oauth2.token(); + emit finished(AccountTaskState::STATE_WORKING, tr("Got ")); + }); + connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &QDesktopServices::openUrl); + connect(&oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this](const QAbstractOAuth2::Error err) { + emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Microsoft user authentication failed.")); + }); + + connect(&oauth2, &QOAuth2AuthorizationCodeFlow::extraTokensChanged, this, + [this](const QVariantMap& tokens) { m_data->msaToken.extra = tokens; }); + + connect(&oauth2, &QOAuth2AuthorizationCodeFlow::clientIdentifierChanged, this, + [this](const QString& clientIdentifier) { m_data->msaClientID = clientIdentifier; }); } QString MSAStep::describe() @@ -69,68 +85,22 @@ void MSAStep::perform() switch (m_action) { case Refresh: { if (m_data->msaClientID != m_clientId) { - emit hideVerificationUriAndCode(); emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - client identification has changed.")); } - m_oauth2->refresh(); + oauth2.setRefreshToken(m_data->msaToken.refresh_token); + oauth2.refreshAccessToken(); return; } case Login: { - QVariantMap extraOpts; - extraOpts["prompt"] = "select_account"; - m_oauth2->setExtraRequestParams(extraOpts); + oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMultiMap* map) { + map->insert("prompt", "select_account"); + map->insert("cobrandid", "8058f65d-ce06-4c30-9559-473c9275a65d"); + }); *m_data = AccountData(); m_data->msaClientID = m_clientId; - m_oauth2->login(); - return; - } - } -} - -void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) -{ - switch (activity) { - case Katabasis::Activity::Idle: - case Katabasis::Activity::LoggingIn: - case Katabasis::Activity::Refreshing: - case Katabasis::Activity::LoggingOut: { - // We asked it to do something, it's doing it. Nothing to act upon. - return; - } - case Katabasis::Activity::Succeeded: { - // Succeeded or did not invalidate tokens - emit hideVerificationUriAndCode(); - QVariantMap extraTokens = m_oauth2->extraTokens(); - if (!extraTokens.isEmpty()) { - qCDebug(authCredentials()) << "Extra tokens in response:"; - foreach (QString key, extraTokens.keys()) { - qCDebug(authCredentials()) << "\t" << key << ":" << extraTokens.value(key); - } - } - emit finished(AccountTaskState::STATE_WORKING, tr("Got ")); - return; - } - case Katabasis::Activity::FailedSoft: { - // NOTE: soft error in the first step means 'offline' - emit hideVerificationUriAndCode(); - emit finished(AccountTaskState::STATE_OFFLINE, tr("Microsoft user authentication ended with a network error.")); - return; - } - case Katabasis::Activity::FailedGone: { - emit hideVerificationUriAndCode(); - emit finished(AccountTaskState::STATE_FAILED_GONE, tr("Microsoft user authentication failed - user no longer exists.")); - return; - } - case Katabasis::Activity::FailedHard: { - emit hideVerificationUriAndCode(); - emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Microsoft user authentication failed.")); - return; - } - default: { - emit hideVerificationUriAndCode(); - emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Microsoft user authentication completed with an unrecognized result.")); + oauth2.grant(); return; } } diff --git a/launcher/minecraft/auth/steps/MSAStep.h b/launcher/minecraft/auth/steps/MSAStep.h index ee441308f..f38f5c70e 100644 --- a/launcher/minecraft/auth/steps/MSAStep.h +++ b/launcher/minecraft/auth/steps/MSAStep.h @@ -36,11 +36,9 @@ #pragma once #include -#include "QObjectPtr.h" #include "minecraft/auth/AuthStep.h" -#include - +#include class MSAStep : public AuthStep { Q_OBJECT public: @@ -54,11 +52,8 @@ class MSAStep : public AuthStep { QString describe() override; - private slots: - void onOAuthActivityChanged(Katabasis::Activity activity); - private: - Katabasis::DeviceFlow* m_oauth2 = nullptr; Action m_action; QString m_clientId; + QOAuth2AuthorizationCodeFlow oauth2; }; diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 90ce15451..55a4f185c 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -171,9 +171,9 @@ void NetRequest::downloadError(QNetworkReply::NetworkError error) } } // error happened during download. - qCCritical(logCat) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error; + qCCritical(logCat) << getUid().toString() << "Failed" << m_url.toString() << "with reason" << error; if (m_reply) - qCCritical(logCat) << getUid().toString() << "HTTP Status " << replyStatusCode() << ";error " << errorString(); + qCCritical(logCat) << getUid().toString() << "HTTP Status" << replyStatusCode() << ";error" << errorString(); m_state = State::Failed; } } diff --git a/launcher/qtlogging.ini b/launcher/qtlogging.ini index 5266de59b..c12d1e109 100644 --- a/launcher/qtlogging.ini +++ b/launcher/qtlogging.ini @@ -5,7 +5,6 @@ qt.*.debug=false # don't log credentials by default launcher.auth.credentials.debug=false -katabasis.*.debug=false # remove the debug lines, other log levels still get through launcher.task.net.download.debug=false # enable or disable whole catageries diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index e396c01f6..b249346a4 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -67,9 +67,6 @@ int MSALoginDialog::exec() connect(m_loginTask.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &MSALoginDialog::onTaskStatus); connect(m_loginTask.get(), &Task::progress, this, &MSALoginDialog::onTaskProgress); - connect(m_loginTask.get(), &AuthFlow::showVerificationUriAndCode, this, &MSALoginDialog::showVerificationUriAndCode); - connect(m_loginTask.get(), &AuthFlow::hideVerificationUriAndCode, this, &MSALoginDialog::hideVerificationUriAndCode); - connect(&m_externalLoginTimer, &QTimer::timeout, this, &MSALoginDialog::externalLoginTick); m_loginTask->start(); return QDialog::exec(); @@ -80,55 +77,6 @@ MSALoginDialog::~MSALoginDialog() delete ui; } -void MSALoginDialog::externalLoginTick() -{ - m_externalLoginElapsed++; - ui->progressBar->setValue(m_externalLoginElapsed); - ui->progressBar->repaint(); - - if (m_externalLoginElapsed >= m_externalLoginTimeout) { - m_externalLoginTimer.stop(); - } -} - -void MSALoginDialog::showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn) -{ - m_externalLoginElapsed = 0; - m_externalLoginTimeout = expiresIn; - - m_externalLoginTimer.setInterval(1000); - m_externalLoginTimer.setSingleShot(false); - m_externalLoginTimer.start(); - - ui->progressBar->setMaximum(expiresIn); - ui->progressBar->setValue(m_externalLoginElapsed); - - QString urlString = uri.toString(); - QString linkString = QString("%2").arg(urlString, urlString); - if (urlString == "https://www.microsoft.com/link" && !code.isEmpty()) { - urlString += QString("?otc=%1").arg(code); - DesktopServices::openUrl(urlString); - ui->label->setText(tr("

    Please login in the opened browser. If no browser was opened, please open up %1 in " - "a browser and put in the code %2 to proceed with login.

    ") - .arg(linkString, code)); - } else { - ui->label->setText( - tr("

    Please open up %1 in a browser and put in the code %2 to proceed with login.

    ").arg(linkString, code)); - } - ui->actionButton->setVisible(true); - connect(ui->actionButton, &QPushButton::clicked, [=]() { - DesktopServices::openUrl(uri); - QClipboard* cb = QApplication::clipboard(); - cb->setText(code); - }); -} - -void MSALoginDialog::hideVerificationUriAndCode() -{ - m_externalLoginTimer.stop(); - ui->actionButton->setVisible(false); -} - void MSALoginDialog::setUserInputsEnabled(bool enable) { ui->buttonBox->setEnabled(enable); diff --git a/launcher/ui/dialogs/MSALoginDialog.h b/launcher/ui/dialogs/MSALoginDialog.h index 531ae3cc1..f14e04776 100644 --- a/launcher/ui/dialogs/MSALoginDialog.h +++ b/launcher/ui/dialogs/MSALoginDialog.h @@ -15,7 +15,6 @@ #pragma once -#include #include #include @@ -45,16 +44,9 @@ class MSALoginDialog : public QDialog { void onTaskSucceeded(); void onTaskStatus(const QString& status); void onTaskProgress(qint64 current, qint64 total); - void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); - void hideVerificationUriAndCode(); - - void externalLoginTick(); private: Ui::MSALoginDialog* ui; MinecraftAccountPtr m_account; shared_qobject_ptr m_loginTask; - QTimer m_externalLoginTimer; - int m_externalLoginElapsed = 0; - int m_externalLoginTimeout = 0; }; diff --git a/libraries/README.md b/libraries/README.md index e75a381ee..67d78dade 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -32,14 +32,6 @@ Simple Java tool that prints the JVM details - version and platform bitness. Do what you want with it. It is so trivial that noone cares. -## Katabasis - -Oauth2 library customized for Microsoft authentication. - -This is a fork of the [O2 library](https://github.com/pipacs/o2). - -MIT licensed. - ## launcher Java launcher part for Minecraft. diff --git a/libraries/katabasis/.gitignore b/libraries/katabasis/.gitignore deleted file mode 100644 index 35e189c5e..000000000 --- a/libraries/katabasis/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build/ -*.kdev4 diff --git a/libraries/katabasis/CMakeLists.txt b/libraries/katabasis/CMakeLists.txt deleted file mode 100644 index 643244ede..000000000 --- a/libraries/katabasis/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -cmake_minimum_required(VERSION 3.9.4) - -string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) -if(IS_IN_SOURCE_BUILD) - message(FATAL_ERROR "You are building Katabasis in-source. Please separate the build tree from the source tree.") -endif() - -project(Katabasis) -enable_testing() - -set(CMAKE_AUTOMOC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_CXX_STANDARD_REQUIRED true) -set(CMAKE_C_STANDARD_REQUIRED true) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_C_STANDARD 11) - -if(QT_VERSION_MAJOR EQUAL 5) - find_package(Qt5 COMPONENTS Core Network REQUIRED) -elseif(Launcher_QT_VERSION_MAJOR EQUAL 6) - find_package(Qt6 COMPONENTS Core Network REQUIRED) -endif() - -set( katabasis_PRIVATE - src/DeviceFlow.cpp - src/JsonResponse.cpp - src/JsonResponse.h - src/PollServer.cpp - src/Reply.cpp -) - -set( katabasis_PUBLIC - include/katabasis/DeviceFlow.h - include/katabasis/Globals.h - include/katabasis/PollServer.h - include/katabasis/Reply.h - include/katabasis/RequestParameter.h -) - -ecm_qt_declare_logging_category(katabasis_PRIVATE - HEADER KatabasisLogging.h # NOTE: this won't be in src/, but CMAKE_BINARY_DIR/src isn't included by default so this should be fine - IDENTIFIER katabasisCredentials - CATEGORY_NAME "katabasis.credentials" - DEFAULT_SEVERITY Warning - DESCRIPTION "Secrets and credentials from Katabasis" - EXPORT "Katabasis" -) - -add_library( Katabasis STATIC ${katabasis_PRIVATE} ${katabasis_PUBLIC} ) -target_link_libraries(Katabasis Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network) - -# needed for statically linked Katabasis in shared libs on x86_64 -set_target_properties(Katabasis - PROPERTIES POSITION_INDEPENDENT_CODE TRUE -) - -target_include_directories(Katabasis PUBLIC include PRIVATE src include/katabasis) diff --git a/libraries/katabasis/LICENSE b/libraries/katabasis/LICENSE deleted file mode 100644 index 9ac8d42fb..000000000 --- a/libraries/katabasis/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2012, Akos Polster -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libraries/katabasis/README.md b/libraries/katabasis/README.md deleted file mode 100644 index fe6dd4aca..000000000 --- a/libraries/katabasis/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Katabasis - MS-flavored OAuth for Qt, derived from the O2 library - -This library's sole purpose is to make interacting with MSA and various MSA and XBox authenticated services less painful. - -It may be possible to backport some of the changes to O2 in the future, but for the sake of going fast, all compatibility concerns have been ignored. - -[You can find the original library's git repository here.](https://github.com/pipacs/o2) - -Notes to contributors: - -* Please follow the coding style of the existing source, where reasonable -* Code contributions are released under Simplified BSD License, as specified in LICENSE. Do not contribute if this license does not suit your code -* If you are interested in working on this, come to the Prism Launcher Discord server and talk first - -## Installation - -Clone the Github repository, integrate the it into your CMake build system. - -The library is static only, dynamic linking and system-wide installation are out of scope and undesirable. - -## Usage - -At this stage, don't, unless you want to help with the library itself. - -This is an experimental fork of the O2 library and is undergoing a big design/architecture shift in order to support different features: - -* Multiple accounts -* Multi-stage authentication/authorization schemes -* Tighter control over token chains and their storage -* Talking to complex APIs and individually authorized microservices -* Token lifetime management, 'offline mode' and resilience in face of network failures -* Token and claims/entitlements validation -* Caching of some API results -* XBox magic -* Mojang magic -* Generally, magic that you would spend weeks on researching while getting confused by contradictory/incomplete documentation (if any is available) diff --git a/libraries/katabasis/acknowledgements.md b/libraries/katabasis/acknowledgements.md deleted file mode 100644 index a6989d15a..000000000 --- a/libraries/katabasis/acknowledgements.md +++ /dev/null @@ -1,108 +0,0 @@ -## O2 library by Akos Polster and contributors - -[The origin of this fork.](https://github.com/pipacs/o2) - -> Copyright (c) 2012, Akos Polster -> All rights reserved. -> -> Redistribution and use in source and binary forms, with or without -> modification, are permitted provided that the following conditions are met: -> -> * Redistributions of source code must retain the above copyright notice, this -> list of conditions and the following disclaimer. -> -> * Redistributions in binary form must reproduce the above copyright notice, -> this list of conditions and the following disclaimer in the documentation -> and/or other materials provided with the distribution. -> -> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -## SimpleCrypt by Andre Somers - -Cryptographic methods for Qt. - -> Copyright (c) 2011, Andre Somers -> All rights reserved. -> -> Redistribution and use in source and binary forms, with or without -> modification, are permitted provided that the following conditions are met: -> -> * Redistributions of source code must retain the above copyright -> notice, this list of conditions and the following disclaimer. -> * Redistributions in binary form must reproduce the above copyright -> notice, this list of conditions and the following disclaimer in the -> documentation and/or other materials provided with the distribution. -> * Neither the name of the Rathenau Instituut, Andre Somers nor the -> names of its contributors may be used to endorse or promote products -> derived from this software without specific prior written permission. -> -> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -> ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -> WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -> DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY -> DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -> (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -> ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -> (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -> SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -## Mandeep Sandhu - -Configurable settings storage, Twitter XAuth specialization, new demos, cleanups. - -> "Hi Akos, -> -> I'm writing this mail to confirm that my contributions to the O2 library, available here , can be freely distributed according to the project's license (as shown in the LICENSE file). -> -> Regards, -> -mandeep" - -## Sergey Gavrushkin - -FreshBooks specialization - -## Theofilos Intzoglou - -Hubic specialization - -## Dimitar - -SurveyMonkey specialization - -## David Brooks - -CMake related fixes and improvements. - -## Lukas Vogel - -Spotify support - -## Alan Garny - -Windows DLL build support - -## MartinMikita - -Bug fixes - -## Larry Shaffer - -Versioning, shared lib, install target and header support - -## Gilmanov Ildar - -Bug fixes, support for ```qml``` module - -## Fabian Vogt - -Bug fixes, support for building without Qt keywords enabled diff --git a/libraries/katabasis/include/katabasis/Bits.h b/libraries/katabasis/include/katabasis/Bits.h deleted file mode 100644 index 15da2a5a8..000000000 --- a/libraries/katabasis/include/katabasis/Bits.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace Katabasis { -enum class Activity { - Idle, - LoggingIn, - LoggingOut, - Refreshing, - FailedSoft, //!< soft failure. this generally means the user auth details haven't been invalidated - FailedHard, //!< hard failure. auth is invalid - FailedGone, //!< hard failure. auth is invalid, and the account no longer exists - Succeeded -}; - -enum class Validity { None, Assumed, Certain }; - -struct Token { - QDateTime issueInstant; - QDateTime notAfter; - QString token; - QString refresh_token; - QVariantMap extra; - - Validity validity = Validity::None; - bool persistent = true; -}; - -} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/DeviceFlow.h b/libraries/katabasis/include/katabasis/DeviceFlow.h deleted file mode 100644 index 98724d81b..000000000 --- a/libraries/katabasis/include/katabasis/DeviceFlow.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "Bits.h" -#include "Reply.h" -#include "RequestParameter.h" - -namespace Katabasis { - -class ReplyServer; -class PollServer; - -/// Simple OAuth2 Device Flow authenticator. -class DeviceFlow : public QObject { - Q_OBJECT - public: - Q_ENUMS(GrantFlow) - - public: - struct Options { - QString userAgent = QStringLiteral("Katabasis/1.0"); - QString responseType = QStringLiteral("code"); - QString scope; - QString clientIdentifier; - QString clientSecret; - QUrl authorizationUrl; - QUrl accessTokenUrl; - }; - - public: - /// Are we authenticated? - bool linked(); - - /// Authentication token. - QString token(); - - /// Provider-specific extra tokens, available after a successful authentication - QVariantMap extraTokens(); - - public: - // TODO: put in `Options` - /// User-defined extra parameters to append to request URL - QVariantMap extraRequestParams(); - void setExtraRequestParams(const QVariantMap& value); - - // TODO: split up the class into multiple, each implementing one OAuth2 flow - /// Grant type (if non-standard) - QString grantType(); - void setGrantType(const QString& value); - - public: - /// Constructor. - /// @param parent Parent object. - explicit DeviceFlow(Options& opts, Token& token, QObject* parent = 0, QNetworkAccessManager* manager = 0); - - /// Get refresh token. - QString refreshToken(); - - /// Get token expiration time - QDateTime expires(); - - public slots: - /// Authenticate. - void login(); - - /// De-authenticate. - void logout(); - - /// Refresh token. - bool refresh(); - - /// Handle situation where reply server has opted to close its connection - void serverHasClosed(bool paramsfound = false); - - signals: - /// Emitted when client needs to open a web browser window, with the given URL. - void openBrowser(const QUrl& url); - - /// Emitted when client can close the browser window. - void closeBrowser(); - - /// Emitted when client needs to show a verification uri and user code - void showVerificationUriAndCode(const QUrl& uri, const QString& code, int expiresIn); - - /// Emitted when the internal state changes - void activityChanged(Activity activity); - - public slots: - /// Handle verification response. - void onVerificationReceived(QMap); - - protected slots: - /// Handle completion of a Device Authorization Request - void onDeviceAuthReplyFinished(); - - /// Handle completion of a refresh request. - void onRefreshFinished(); - - /// Handle failure of a refresh request. - void onRefreshError(QNetworkReply::NetworkError error, QNetworkReply* reply); - - protected: - /// Set refresh token. - void setRefreshToken(const QString& v); - - /// Set token expiration time. - void setExpires(QDateTime v); - - /// Start polling authorization server - void startPollServer(const QVariantMap& params, int expiresIn); - - /// Set authentication token. - void setToken(const QString& v); - - /// Set the linked state - void setLinked(bool v); - - /// Set extra tokens found in OAuth response - void setExtraTokens(QVariantMap extraTokens); - - /// Set local poll server - void setPollServer(PollServer* server); - - PollServer* pollServer() const; - - void updateActivity(Activity activity); - - protected: - Options options_; - - QVariantMap extraReqParams_; - QNetworkAccessManager* manager_ = nullptr; - ReplyList timedReplies_; - QString grantType_; - - protected: - Token& token_; - - private: - PollServer* pollServer_ = nullptr; - Activity activity_ = Activity::Idle; -}; - -} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/Globals.h b/libraries/katabasis/include/katabasis/Globals.h deleted file mode 100644 index 02fe1cf45..000000000 --- a/libraries/katabasis/include/katabasis/Globals.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -namespace Katabasis { - -// Common constants -const char ENCRYPTION_KEY[] = "12345678"; -const char MIME_TYPE_XFORM[] = "application/x-www-form-urlencoded"; -const char MIME_TYPE_JSON[] = "application/json"; - -// OAuth 1/1.1 Request Parameters -const char OAUTH_CALLBACK[] = "oauth_callback"; -const char OAUTH_CONSUMER_KEY[] = "oauth_consumer_key"; -const char OAUTH_NONCE[] = "oauth_nonce"; -const char OAUTH_SIGNATURE[] = "oauth_signature"; -const char OAUTH_SIGNATURE_METHOD[] = "oauth_signature_method"; -const char OAUTH_TIMESTAMP[] = "oauth_timestamp"; -const char OAUTH_VERSION[] = "oauth_version"; -// OAuth 1/1.1 Response Parameters -const char OAUTH_TOKEN[] = "oauth_token"; -const char OAUTH_TOKEN_SECRET[] = "oauth_token_secret"; -const char OAUTH_CALLBACK_CONFIRMED[] = "oauth_callback_confirmed"; -const char OAUTH_VERFIER[] = "oauth_verifier"; - -// OAuth 2 Request Parameters -const char OAUTH2_RESPONSE_TYPE[] = "response_type"; -const char OAUTH2_CLIENT_ID[] = "client_id"; -const char OAUTH2_CLIENT_SECRET[] = "client_secret"; -const char OAUTH2_USERNAME[] = "username"; -const char OAUTH2_PASSWORD[] = "password"; -const char OAUTH2_REDIRECT_URI[] = "redirect_uri"; -const char OAUTH2_SCOPE[] = "scope"; -const char OAUTH2_GRANT_TYPE_CODE[] = "code"; -const char OAUTH2_GRANT_TYPE_TOKEN[] = "token"; -const char OAUTH2_GRANT_TYPE_PASSWORD[] = "password"; -const char OAUTH2_GRANT_TYPE_DEVICE[] = "urn:ietf:params:oauth:grant-type:device_code"; -const char OAUTH2_GRANT_TYPE[] = "grant_type"; -const char OAUTH2_API_KEY[] = "api_key"; -const char OAUTH2_STATE[] = "state"; -const char OAUTH2_CODE[] = "code"; - -// OAuth 2 Response Parameters -const char OAUTH2_ACCESS_TOKEN[] = "access_token"; -const char OAUTH2_REFRESH_TOKEN[] = "refresh_token"; -const char OAUTH2_EXPIRES_IN[] = "expires_in"; -const char OAUTH2_DEVICE_CODE[] = "device_code"; -const char OAUTH2_USER_CODE[] = "user_code"; -const char OAUTH2_VERIFICATION_URI[] = "verification_uri"; -const char OAUTH2_VERIFICATION_URL[] = "verification_url"; // Google sign-in -const char OAUTH2_VERIFICATION_URI_COMPLETE[] = "verification_uri_complete"; -const char OAUTH2_INTERVAL[] = "interval"; - -// Parameter values -const char AUTHORIZATION_CODE[] = "authorization_code"; - -// Standard HTTP headers -const char HTTP_HTTP_HEADER[] = "HTTP"; -const char HTTP_AUTHORIZATION_HEADER[] = "Authorization"; - -} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/PollServer.h b/libraries/katabasis/include/katabasis/PollServer.h deleted file mode 100644 index fd6a5351c..000000000 --- a/libraries/katabasis/include/katabasis/PollServer.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -class QNetworkAccessManager; - -namespace Katabasis { - -/// Poll an authorization server for token -class PollServer : public QObject { - Q_OBJECT - - public: - explicit PollServer(QNetworkAccessManager* manager, - const QNetworkRequest& request, - const QByteArray& payload, - int expiresIn, - QObject* parent = 0); - - /// Seconds to wait between polling requests - Q_PROPERTY(int interval READ interval WRITE setInterval) - int interval() const; - void setInterval(int interval); - - signals: - void verificationReceived(QMap); - void serverClosed(bool); // whether it has found parameters - - public slots: - void startPolling(); - - protected slots: - void onPollTimeout(); - void onExpiration(); - void onReplyFinished(); - - protected: - QNetworkAccessManager* manager_; - const QNetworkRequest request_; - const QByteArray payload_; - const int expiresIn_; - QTimer expirationTimer; - QTimer pollTimer; -}; - -} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/Reply.h b/libraries/katabasis/include/katabasis/Reply.h deleted file mode 100644 index 89ee90e98..000000000 --- a/libraries/katabasis/include/katabasis/Reply.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Katabasis { - -constexpr int defaultTimeout = 30 * 1000; - -/// A network request/reply pair that can time out. -class Reply : public QTimer { - Q_OBJECT - - public: - Reply(QNetworkReply* reply, int timeOut = defaultTimeout, QObject* parent = 0); - - signals: - void error(QNetworkReply::NetworkError); - - public slots: - /// When time out occurs, the QNetworkReply's error() signal is triggered. - void onTimeOut(); - - public: - QNetworkReply* reply; - bool timedOut = false; -}; - -/// List of O2Replies. -class ReplyList { - public: - ReplyList() { ignoreSslErrors_ = false; } - - /// Destructor. - /// Deletes all O2Reply instances in the list. - virtual ~ReplyList(); - - /// Create a new O2Reply from a QNetworkReply, and add it to this list. - void add(QNetworkReply* reply, int timeOut = defaultTimeout); - - /// Add an O2Reply to the list, while taking ownership of it. - void add(Reply* reply); - - /// Remove item from the list that corresponds to a QNetworkReply. - void remove(QNetworkReply* reply); - - /// Find an O2Reply in the list, corresponding to a QNetworkReply. - /// @return Matching O2Reply or NULL. - Reply* find(QNetworkReply* reply); - - bool ignoreSslErrors(); - void setIgnoreSslErrors(bool ignoreSslErrors); - - protected: - QList replies_; - bool ignoreSslErrors_; -}; - -} // namespace Katabasis diff --git a/libraries/katabasis/include/katabasis/RequestParameter.h b/libraries/katabasis/include/katabasis/RequestParameter.h deleted file mode 100644 index 1d23cf0e1..000000000 --- a/libraries/katabasis/include/katabasis/RequestParameter.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace Katabasis { - -/// Request parameter (name-value pair) participating in authentication. -struct RequestParameter { - RequestParameter(const QByteArray& n, const QByteArray& v) : name(n), value(v) {} - bool operator<(const RequestParameter& other) const { return (name == other.name) ? (value < other.value) : (name < other.name); } - QByteArray name; - QByteArray value; -}; - -} // namespace Katabasis diff --git a/libraries/katabasis/src/DeviceFlow.cpp b/libraries/katabasis/src/DeviceFlow.cpp deleted file mode 100644 index 3b9d9c53f..000000000 --- a/libraries/katabasis/src/DeviceFlow.cpp +++ /dev/null @@ -1,467 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "katabasis/DeviceFlow.h" -#include "katabasis/Globals.h" -#include "katabasis/PollServer.h" - -#include "JsonResponse.h" -#include "KatabasisLogging.h" - -namespace { - -// ref: https://tools.ietf.org/html/rfc8628#section-3.2 -// Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both. -bool hasMandatoryDeviceAuthParams(const QVariantMap& params) -{ - if (!params.contains(Katabasis::OAUTH2_DEVICE_CODE)) - return false; - - if (!params.contains(Katabasis::OAUTH2_USER_CODE)) - return false; - - if (!(params.contains(Katabasis::OAUTH2_VERIFICATION_URI) || params.contains(Katabasis::OAUTH2_VERIFICATION_URL))) - return false; - - if (!params.contains(Katabasis::OAUTH2_EXPIRES_IN)) - return false; - - return true; -} - -QByteArray createQueryParameters(const QList& parameters) -{ - QByteArray ret; - bool first = true; - for (auto& h : parameters) { - if (first) { - first = false; - } else { - ret.append("&"); - } - ret.append(QUrl::toPercentEncoding(h.name) + "=" + QUrl::toPercentEncoding(h.value)); - } - return ret; -} -} // namespace - -namespace Katabasis { - -DeviceFlow::DeviceFlow(Options& opts, Token& token, QObject* parent, QNetworkAccessManager* manager) : QObject(parent), token_(token) -{ - manager_ = manager ? manager : new QNetworkAccessManager(this); - qRegisterMetaType("QNetworkReply::NetworkError"); - options_ = opts; -} - -bool DeviceFlow::linked() -{ - return token_.validity != Validity::None; -} -void DeviceFlow::setLinked(bool v) -{ - qDebug() << "DeviceFlow::setLinked:" << (v ? "true" : "false"); - token_.validity = v ? Validity::Certain : Validity::None; -} - -void DeviceFlow::updateActivity(Activity activity) -{ - if (activity_ == activity) { - return; - } - - activity_ = activity; - switch (activity) { - case Katabasis::Activity::Idle: - case Katabasis::Activity::LoggingIn: - case Katabasis::Activity::LoggingOut: - case Katabasis::Activity::Refreshing: - // non-terminal states... - break; - case Katabasis::Activity::FailedSoft: - // terminal state, tokens did not change - break; - case Katabasis::Activity::FailedHard: - case Katabasis::Activity::FailedGone: - // terminal state, tokens are invalid - token_ = Token(); - break; - case Katabasis::Activity::Succeeded: - setLinked(true); - break; - } - emit activityChanged(activity_); -} - -QString DeviceFlow::token() -{ - return token_.token; -} -void DeviceFlow::setToken(const QString& v) -{ - token_.token = v; -} - -QVariantMap DeviceFlow::extraTokens() -{ - return token_.extra; -} - -void DeviceFlow::setExtraTokens(QVariantMap extraTokens) -{ - token_.extra = extraTokens; -} - -void DeviceFlow::setPollServer(PollServer* server) -{ - if (pollServer_) - pollServer_->deleteLater(); - - pollServer_ = server; -} - -PollServer* DeviceFlow::pollServer() const -{ - return pollServer_; -} - -QVariantMap DeviceFlow::extraRequestParams() -{ - return extraReqParams_; -} - -void DeviceFlow::setExtraRequestParams(const QVariantMap& value) -{ - extraReqParams_ = value; -} - -QString DeviceFlow::grantType() -{ - if (!grantType_.isEmpty()) - return grantType_; - - return OAUTH2_GRANT_TYPE_DEVICE; -} - -void DeviceFlow::setGrantType(const QString& value) -{ - grantType_ = value; -} - -// First get the URL and token to display to the user -void DeviceFlow::login() -{ - qDebug() << "DeviceFlow::link"; - - updateActivity(Activity::LoggingIn); - setLinked(false); - setToken(""); - setExtraTokens(QVariantMap()); - setRefreshToken(QString()); - setExpires(QDateTime()); - - QList parameters; - parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - parameters.append(RequestParameter(OAUTH2_SCOPE, options_.scope.toUtf8())); - QByteArray payload = createQueryParameters(parameters); - - QUrl url(options_.authorizationUrl); - QNetworkRequest deviceRequest(url); - deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply* tokenReply = manager_->post(deviceRequest, payload); - - connect(tokenReply, &QNetworkReply::finished, this, &DeviceFlow::onDeviceAuthReplyFinished, Qt::QueuedConnection); -} - -// Then, once we get them, present them to the user -void DeviceFlow::onDeviceAuthReplyFinished() -{ - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished"; - QNetworkReply* tokenReply = qobject_cast(sender()); - if (!tokenReply) { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: reply is null"; - return; - } - if (tokenReply->error() == QNetworkReply::NoError) { - QByteArray replyData = tokenReply->readAll(); - - // Dump replyData - // SENSITIVE DATA in RelWithDebInfo or Debug builds - // qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: replyData\n"; - // qDebug() << QString( replyData ); - - QVariantMap params = parseJsonResponse(replyData); - - // Dump tokens - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Tokens returned:\n"; - foreach (QString key, params.keys()) { - // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first - qDebug() << key << ": " << params.value(key).toString(); - } - - // Check for mandatory parameters - if (hasMandatoryDeviceAuthParams(params)) { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Device auth request response"; - - const QString userCode = params.take(OAUTH2_USER_CODE).toString(); - QUrl uri = params.take(OAUTH2_VERIFICATION_URI).toUrl(); - if (uri.isEmpty()) - uri = params.take(OAUTH2_VERIFICATION_URL).toUrl(); - - if (params.contains(OAUTH2_VERIFICATION_URI_COMPLETE)) - emit openBrowser(params.take(OAUTH2_VERIFICATION_URI_COMPLETE).toUrl()); - - bool ok = false; - int expiresIn = params[OAUTH2_EXPIRES_IN].toInt(&ok); - if (!ok) { - qWarning() << "DeviceFlow::startPollServer: No expired_in parameter"; - updateActivity(Activity::FailedHard); - return; - } - - emit showVerificationUriAndCode(uri, userCode, expiresIn); - - startPollServer(params, expiresIn); - } else { - qWarning() << "DeviceFlow::onDeviceAuthReplyFinished: Mandatory parameters missing from response"; - updateActivity(Activity::FailedHard); - } - } - tokenReply->deleteLater(); -} - -// Spin up polling for the user completing the login flow out of band -void DeviceFlow::startPollServer(const QVariantMap& params, int expiresIn) -{ - qDebug() << "DeviceFlow::startPollServer: device_ and user_code expires in" << expiresIn << "seconds"; - - QUrl url(options_.accessTokenUrl); - QNetworkRequest authRequest(url); - authRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - - const QString deviceCode = params[OAUTH2_DEVICE_CODE].toString(); - const QString grantType = grantType_.isEmpty() ? OAUTH2_GRANT_TYPE_DEVICE : grantType_; - - QList parameters; - parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - if (!options_.clientSecret.isEmpty()) { - parameters.append(RequestParameter(OAUTH2_CLIENT_SECRET, options_.clientSecret.toUtf8())); - } - parameters.append(RequestParameter(OAUTH2_CODE, deviceCode.toUtf8())); - parameters.append(RequestParameter(OAUTH2_GRANT_TYPE, grantType.toUtf8())); - QByteArray payload = createQueryParameters(parameters); - - PollServer* pollServer = new PollServer(manager_, authRequest, payload, expiresIn, this); - if (params.contains(OAUTH2_INTERVAL)) { - bool ok = false; - int interval = params[OAUTH2_INTERVAL].toInt(&ok); - if (ok) { - pollServer->setInterval(interval); - } - } - connect(pollServer, &PollServer::verificationReceived, this, &DeviceFlow::onVerificationReceived); - connect(pollServer, &PollServer::serverClosed, this, &DeviceFlow::serverHasClosed); - setPollServer(pollServer); - pollServer->startPolling(); -} - -// Once the user completes the flow, update the internal state and report it to observers -void DeviceFlow::onVerificationReceived(const QMap response) -{ - qDebug() << "DeviceFlow::onVerificationReceived: Emitting closeBrowser()"; - emit closeBrowser(); - - if (response.contains("error")) { - qWarning() << "DeviceFlow::onVerificationReceived: Verification failed:" << response; - updateActivity(Activity::FailedHard); - return; - } - - // Check for mandatory tokens - if (response.contains(OAUTH2_ACCESS_TOKEN)) { - qDebug() << "DeviceFlow::onVerificationReceived: Access token returned for implicit or device flow"; - setToken(response.value(OAUTH2_ACCESS_TOKEN)); - if (response.contains(OAUTH2_EXPIRES_IN)) { - bool ok = false; - int expiresIn = response.value(OAUTH2_EXPIRES_IN).toInt(&ok); - if (ok) { - qDebug() << "DeviceFlow::onVerificationReceived: Token expires in" << expiresIn << "seconds"; - setExpires(QDateTime::currentDateTimeUtc().addSecs(expiresIn)); - } - } - if (response.contains(OAUTH2_REFRESH_TOKEN)) { - setRefreshToken(response.value(OAUTH2_REFRESH_TOKEN)); - } - updateActivity(Activity::Succeeded); - } else { - qWarning() << "DeviceFlow::onVerificationReceived: Access token missing from response for implicit or device flow"; - updateActivity(Activity::FailedHard); - } -} - -// Or if the flow fails or the polling times out, update the internal state with error and report it to observers -void DeviceFlow::serverHasClosed(bool paramsfound) -{ - if (!paramsfound) { - // server has probably timed out after receiving first response - updateActivity(Activity::FailedHard); - } - // poll server is not re-used for later auth requests - setPollServer(NULL); -} - -void DeviceFlow::logout() -{ - qDebug() << "DeviceFlow::unlink"; - updateActivity(Activity::LoggingOut); - // FIXME: implement logout flows... if they exist - token_ = Token(); - updateActivity(Activity::FailedHard); -} - -QDateTime DeviceFlow::expires() -{ - return token_.notAfter; -} -void DeviceFlow::setExpires(QDateTime v) -{ - token_.notAfter = v; -} - -QString DeviceFlow::refreshToken() -{ - return token_.refresh_token; -} - -void DeviceFlow::setRefreshToken(const QString& v) -{ - qCDebug(katabasisCredentials) << "new refresh token:" << v; - token_.refresh_token = v; -} - -namespace { -QByteArray buildRequestBody(const QMap& parameters) -{ - QByteArray body; - bool first = true; - foreach (QString key, parameters.keys()) { - if (first) { - first = false; - } else { - body.append("&"); - } - QString value = parameters.value(key); - body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value)); - } - return body; -} -} // namespace - -bool DeviceFlow::refresh() -{ - qDebug() << "DeviceFlow::refresh: Token: ..." << refreshToken().right(7); - - updateActivity(Activity::Refreshing); - - if (refreshToken().isEmpty()) { - qWarning() << "DeviceFlow::refresh: No refresh token"; - onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); - return false; - } - if (options_.accessTokenUrl.isEmpty()) { - qWarning() << "DeviceFlow::refresh: Refresh token URL not set"; - onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); - return false; - } - - QNetworkRequest refreshRequest(options_.accessTokenUrl); - refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, MIME_TYPE_XFORM); - QMap parameters; - parameters.insert(OAUTH2_CLIENT_ID, options_.clientIdentifier); - if (!options_.clientSecret.isEmpty()) { - parameters.insert(OAUTH2_CLIENT_SECRET, options_.clientSecret); - } - parameters.insert(OAUTH2_REFRESH_TOKEN, refreshToken()); - parameters.insert(OAUTH2_GRANT_TYPE, OAUTH2_REFRESH_TOKEN); - - QByteArray data = buildRequestBody(parameters); - QNetworkReply* refreshReply = manager_->post(refreshRequest, data); - timedReplies_.add(refreshReply); - connect(refreshReply, &QNetworkReply::finished, this, &DeviceFlow::onRefreshFinished, Qt::QueuedConnection); - return true; -} - -void DeviceFlow::onRefreshFinished() -{ - QNetworkReply* refreshReply = qobject_cast(sender()); - - auto networkError = refreshReply->error(); - if (networkError == QNetworkReply::NoError) { - QByteArray reply = refreshReply->readAll(); - QVariantMap tokens = parseJsonResponse(reply); - setToken(tokens.value(OAUTH2_ACCESS_TOKEN).toString()); - setExpires(QDateTime::currentDateTimeUtc().addSecs(tokens.value(OAUTH2_EXPIRES_IN).toInt())); - QString refreshToken = tokens.value(OAUTH2_REFRESH_TOKEN).toString(); - if (!refreshToken.isEmpty()) { - setRefreshToken(refreshToken); - } else { - qDebug() << "No new refresh token. Keep the old one."; - } - timedReplies_.remove(refreshReply); - refreshReply->deleteLater(); - updateActivity(Activity::Succeeded); - qDebug() << "New token expires in" << expires() << "seconds"; - } else { - // FIXME: differentiate the error more here - onRefreshError(networkError, refreshReply); - } -} - -void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, QNetworkReply* refreshReply) -{ - QString errorString = "No Reply"; - if (refreshReply) { - timedReplies_.remove(refreshReply); - errorString = refreshReply->errorString(); - } - - switch (error) { - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::AuthenticationRequiredError: - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::ProtocolInvalidOperationError: - updateActivity(Activity::FailedHard); - break; - case QNetworkReply::ContentGoneError: { - updateActivity(Activity::FailedGone); - break; - } - case QNetworkReply::TimeoutError: - case QNetworkReply::OperationCanceledError: - case QNetworkReply::SslHandshakeFailedError: - default: - updateActivity(Activity::FailedSoft); - return; - } - if (refreshReply) { - refreshReply->deleteLater(); - } - qDebug() << "DeviceFlow::onRefreshFinished: Error" << static_cast(error) << " - " << errorString; -} - -} // namespace Katabasis diff --git a/libraries/katabasis/src/JsonResponse.cpp b/libraries/katabasis/src/JsonResponse.cpp deleted file mode 100644 index 6840627ac..000000000 --- a/libraries/katabasis/src/JsonResponse.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "JsonResponse.h" - -#include -#include -#include -#include - -namespace Katabasis { - -QVariantMap parseJsonResponse(const QByteArray& data) -{ - QJsonParseError err; - QJsonDocument doc = QJsonDocument::fromJson(data, &err); - if (err.error != QJsonParseError::NoError) { - qWarning() << "parseTokenResponse: Failed to parse token response due to err:" << err.errorString(); - return QVariantMap(); - } - - if (!doc.isObject()) { - qWarning() << "parseTokenResponse: Token response is not an object"; - return QVariantMap(); - } - - return doc.object().toVariantMap(); -} - -} // namespace Katabasis diff --git a/libraries/katabasis/src/JsonResponse.h b/libraries/katabasis/src/JsonResponse.h deleted file mode 100644 index ff3471752..000000000 --- a/libraries/katabasis/src/JsonResponse.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -class QByteArray; - -namespace Katabasis { - -/// Parse JSON data into a QVariantMap -QVariantMap parseJsonResponse(const QByteArray& data); - -} // namespace Katabasis diff --git a/libraries/katabasis/src/PollServer.cpp b/libraries/katabasis/src/PollServer.cpp deleted file mode 100644 index c1c316df9..000000000 --- a/libraries/katabasis/src/PollServer.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include - -#include "JsonResponse.h" -#include "katabasis/PollServer.h" - -namespace { -QMap toVerificationParams(const QVariantMap& map) -{ - QMap params; - for (QVariantMap::const_iterator i = map.constBegin(); i != map.constEnd(); ++i) { - params[i.key()] = i.value().toString(); - } - return params; -} -} // namespace - -namespace Katabasis { - -PollServer::PollServer(QNetworkAccessManager* manager, - const QNetworkRequest& request, - const QByteArray& payload, - int expiresIn, - QObject* parent) - : QObject(parent), manager_(manager), request_(request), payload_(payload), expiresIn_(expiresIn) -{ - expirationTimer.setTimerType(Qt::VeryCoarseTimer); - expirationTimer.setInterval(expiresIn * 1000); - expirationTimer.setSingleShot(true); - connect(&expirationTimer, SIGNAL(timeout()), this, SLOT(onExpiration())); - expirationTimer.start(); - - pollTimer.setTimerType(Qt::VeryCoarseTimer); - pollTimer.setInterval(5 * 1000); - pollTimer.setSingleShot(true); - connect(&pollTimer, SIGNAL(timeout()), this, SLOT(onPollTimeout())); -} - -int PollServer::interval() const -{ - return pollTimer.interval() / 1000; -} - -void PollServer::setInterval(int interval) -{ - pollTimer.setInterval(interval * 1000); -} - -void PollServer::startPolling() -{ - if (expirationTimer.isActive()) { - pollTimer.start(); - } -} - -void PollServer::onPollTimeout() -{ - qDebug() << "PollServer::onPollTimeout: retrying"; - QNetworkReply* reply = manager_->post(request_, payload_); - connect(reply, SIGNAL(finished()), this, SLOT(onReplyFinished())); -} - -void PollServer::onExpiration() -{ - pollTimer.stop(); - emit serverClosed(false); -} - -void PollServer::onReplyFinished() -{ - QNetworkReply* reply = qobject_cast(sender()); - - if (!reply) { - qDebug() << "PollServer::onReplyFinished: reply is null"; - return; - } - - QByteArray replyData = reply->readAll(); - QMap params = toVerificationParams(parseJsonResponse(replyData)); - - // Dump replyData - // SENSITIVE DATA in RelWithDebInfo or Debug builds - // qDebug() << "PollServer::onReplyFinished: replyData\n"; - // qDebug() << QString( replyData ); - - if (reply->error() == QNetworkReply::TimeoutError) { - // rfc8628#section-3.2 - // "On encountering a connection timeout, clients MUST unilaterally - // reduce their polling frequency before retrying. The use of an - // exponential backoff algorithm to achieve this, such as doubling the - // polling interval on each such connection timeout, is RECOMMENDED." - setInterval(interval() * 2); - pollTimer.start(); - } else { - QString error = params.value("error"); - if (error == "slow_down") { - // rfc8628#section-3.2 - // "A variant of 'authorization_pending', the authorization request is - // still pending and polling should continue, but the interval MUST - // be increased by 5 seconds for this and all subsequent requests." - setInterval(interval() + 5); - pollTimer.start(); - } else if (error == "authorization_pending") { - // keep trying - rfc8628#section-3.2 - // "The authorization request is still pending as the end user hasn't - // yet completed the user-interaction steps (Section 3.3)." - pollTimer.start(); - } else { - expirationTimer.stop(); - emit serverClosed(true); - // let O2 handle the other cases - emit verificationReceived(params); - } - } - reply->deleteLater(); -} - -} // namespace Katabasis diff --git a/libraries/katabasis/src/Reply.cpp b/libraries/katabasis/src/Reply.cpp deleted file mode 100644 index 4a5017e22..000000000 --- a/libraries/katabasis/src/Reply.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include - -#include "katabasis/Reply.h" - -namespace Katabasis { - -Reply::Reply(QNetworkReply* r, int timeOut, QObject* parent) : QTimer(parent), reply(r) -{ - setSingleShot(true); - connect(this, &Reply::timeout, this, &Reply::onTimeOut, Qt::QueuedConnection); - start(timeOut); -} - -void Reply::onTimeOut() -{ - timedOut = true; - reply->abort(); -} - -// ---------------------------- - -ReplyList::~ReplyList() -{ - foreach (Reply* timedReply, replies_) { - delete timedReply; - } -} - -void ReplyList::add(QNetworkReply* reply, int timeOut) -{ - if (reply && ignoreSslErrors()) { - reply->ignoreSslErrors(); - } - add(new Reply(reply, timeOut)); -} - -void ReplyList::add(Reply* reply) -{ - replies_.append(reply); -} - -void ReplyList::remove(QNetworkReply* reply) -{ - Reply* o2Reply = find(reply); - if (o2Reply) { - o2Reply->stop(); - (void)replies_.removeOne(o2Reply); - // we took ownership, we must free - delete o2Reply; - } -} - -Reply* ReplyList::find(QNetworkReply* reply) -{ - foreach (Reply* timedReply, replies_) { - if (timedReply->reply == reply) { - return timedReply; - } - } - return 0; -} - -bool ReplyList::ignoreSslErrors() -{ - return ignoreSslErrors_; -} - -void ReplyList::setIgnoreSslErrors(bool ignoreSslErrors) -{ - ignoreSslErrors_ = ignoreSslErrors; -} - -} // namespace Katabasis From 849c3faeb4dfeb9761fba4bc9b1d35e0b8129c41 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 May 2024 13:06:55 +0300 Subject: [PATCH 383/755] Fix CI Signed-off-by: Trial97 --- .github/workflows/codeql.yml | 2 +- launcher/minecraft/auth/steps/MSAStep.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d40d7eb68..261f67819 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,7 +23,7 @@ jobs: run: sudo apt-get -y update - sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 + sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 - name: Configure and Build run: | diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 71bd25096..79cb062b6 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -93,7 +93,11 @@ void MSAStep::perform() return; } case Login: { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // QMultiMap param changed in 6.0 oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMultiMap* map) { +#else + oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMap* map) { +#endif map->insert("prompt", "select_account"); map->insert("cobrandid", "8058f65d-ce06-4c30-9559-473c9275a65d"); }); From abedc6a23ce85c27f2816f9510d1acdcd59480f4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 May 2024 17:25:15 +0300 Subject: [PATCH 384/755] Improve login UI Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 10 +-- launcher/minecraft/auth/AccountData.cpp | 18 +++--- launcher/minecraft/auth/AccountData.h | 30 +++------ launcher/minecraft/auth/AccountList.h | 2 +- .../minecraft/auth/{flows => }/AuthFlow.cpp | 35 +++++++--- .../minecraft/auth/{flows => }/AuthFlow.h | 12 ++-- launcher/minecraft/auth/MinecraftAccount.cpp | 47 ++++---------- launcher/minecraft/auth/MinecraftAccount.h | 6 +- launcher/minecraft/auth/Parsers.cpp | 14 ++-- launcher/minecraft/auth/Parsers.h | 4 +- launcher/minecraft/auth/flows/MSA.cpp | 36 ----------- launcher/minecraft/auth/flows/MSA.h | 14 ---- launcher/minecraft/auth/flows/Offline.cpp | 8 --- launcher/minecraft/auth/flows/Offline.h | 8 --- launcher/minecraft/auth/steps/MSAStep.cpp | 40 +++++------- launcher/minecraft/auth/steps/MSAStep.h | 10 +-- launcher/minecraft/auth/steps/OfflineStep.cpp | 13 ---- launcher/minecraft/auth/steps/OfflineStep.h | 15 ----- .../auth/steps/XboxAuthorizationStep.cpp | 4 +- .../auth/steps/XboxAuthorizationStep.h | 4 +- .../minecraft/auth/steps/XboxUserStep.cpp | 2 +- launcher/ui/dialogs/MSALoginDialog.cpp | 64 ++++++++++--------- launcher/ui/dialogs/MSALoginDialog.h | 7 +- launcher/ui/dialogs/MSALoginDialog.ui | 41 ++++++------ launcher/ui/dialogs/OfflineLoginDialog.cpp | 2 +- 25 files changed, 159 insertions(+), 287 deletions(-) rename launcher/minecraft/auth/{flows => }/AuthFlow.cpp (70%) rename launcher/minecraft/auth/{flows => }/AuthFlow.h (80%) delete mode 100644 launcher/minecraft/auth/flows/MSA.cpp delete mode 100644 launcher/minecraft/auth/flows/MSA.h delete mode 100644 launcher/minecraft/auth/flows/Offline.cpp delete mode 100644 launcher/minecraft/auth/flows/Offline.h delete mode 100644 launcher/minecraft/auth/steps/OfflineStep.cpp delete mode 100644 launcher/minecraft/auth/steps/OfflineStep.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index e5eb4b733..e63328e6e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -217,15 +217,9 @@ set(MINECRAFT_SOURCES minecraft/auth/Parsers.cpp minecraft/auth/Parsers.h - minecraft/auth/flows/AuthFlow.cpp - minecraft/auth/flows/AuthFlow.h - minecraft/auth/flows/MSA.cpp - minecraft/auth/flows/MSA.h - minecraft/auth/flows/Offline.cpp - minecraft/auth/flows/Offline.h + minecraft/auth/AuthFlow.cpp + minecraft/auth/AuthFlow.h - minecraft/auth/steps/OfflineStep.cpp - minecraft/auth/steps/OfflineStep.h minecraft/auth/steps/EntitlementsStep.cpp minecraft/auth/steps/EntitlementsStep.h minecraft/auth/steps/GetSkinStep.cpp diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index e1f1e9b1e..fd2082035 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -42,7 +42,7 @@ #include namespace { -void tokenToJSONV3(QJsonObject& parent, Katabasis::Token t, const char* tokenName) +void tokenToJSONV3(QJsonObject& parent, Token t, const char* tokenName) { if (!t.persistent) { return; @@ -74,9 +74,9 @@ void tokenToJSONV3(QJsonObject& parent, Katabasis::Token t, const char* tokenNam } } -Katabasis::Token tokenFromJSONV3(const QJsonObject& parent, const char* tokenName) +Token tokenFromJSONV3(const QJsonObject& parent, const char* tokenName) { - Katabasis::Token out; + Token out; auto tokenObject = parent.value(tokenName).toObject(); if (tokenObject.isEmpty()) { return out; @@ -94,7 +94,7 @@ Katabasis::Token tokenFromJSONV3(const QJsonObject& parent, const char* tokenNam auto token = tokenObject.value("token"); if (token.isString()) { out.token = token.toString(); - out.validity = Katabasis::Validity::Assumed; + out.validity = Validity::Assumed; } auto refresh_token = tokenObject.value("refresh_token"); @@ -241,13 +241,13 @@ MinecraftProfile profileFromJSONV3(const QJsonObject& parent, const char* tokenN } } } - out.validity = Katabasis::Validity::Assumed; + out.validity = Validity::Assumed; return out; } void entitlementToJSONV3(QJsonObject& parent, MinecraftEntitlement p) { - if (p.validity == Katabasis::Validity::None) { + if (p.validity == Validity::None) { return; } QJsonObject out; @@ -271,7 +271,7 @@ bool entitlementFromJSONV3(const QJsonObject& parent, MinecraftEntitlement& out) } out.canPlayMinecraft = canPlayMinecraftV.toBool(false); out.ownsMinecraft = ownsMinecraftV.toBool(false); - out.validity = Katabasis::Validity::Assumed; + out.validity = Validity::Assumed; } return true; } @@ -313,10 +313,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) minecraftProfile = profileFromJSONV3(data, "profile"); if (!entitlementFromJSONV3(data, minecraftEntitlement)) { - if (minecraftProfile.validity != Katabasis::Validity::None) { + if (minecraftProfile.validity != Validity::None) { minecraftEntitlement.canPlayMinecraft = true; minecraftEntitlement.ownsMinecraft = true; - minecraftEntitlement.validity = Katabasis::Validity::Assumed; + minecraftEntitlement.validity = Validity::Assumed; } } diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 71f1d00b2..1ada4e38a 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -44,18 +44,6 @@ #include #include -namespace Katabasis { -enum class Activity { - Idle, - LoggingIn, - LoggingOut, - Refreshing, - FailedSoft, //!< soft failure. this generally means the user auth details haven't been invalidated - FailedHard, //!< hard failure. auth is invalid - FailedGone, //!< hard failure. auth is invalid, and the account no longer exists - Succeeded -}; - enum class Validity { None, Assumed, Certain }; struct Token { @@ -69,8 +57,6 @@ struct Token { bool persistent = true; }; -} // namespace Katabasis - struct Skin { QString id; QString url; @@ -90,7 +76,7 @@ struct Cape { struct MinecraftEntitlement { bool ownsMinecraft = false; bool canPlayMinecraft = false; - Katabasis::Validity validity = Katabasis::Validity::None; + Validity validity = Validity::None; }; struct MinecraftProfile { @@ -99,7 +85,7 @@ struct MinecraftProfile { Skin skin; QString currentCape; QMap capes; - Katabasis::Validity validity = Katabasis::Validity::None; + Validity validity = Validity::None; }; enum class AccountType { MSA, Offline }; @@ -124,15 +110,15 @@ struct AccountData { AccountType type = AccountType::MSA; QString msaClientID; - Katabasis::Token msaToken; - Katabasis::Token userToken; - Katabasis::Token xboxApiToken; - Katabasis::Token mojangservicesToken; + Token msaToken; + Token userToken; + Token xboxApiToken; + Token mojangservicesToken; - Katabasis::Token yggdrasilToken; + Token yggdrasilToken; MinecraftProfile minecraftProfile; MinecraftEntitlement minecraftEntitlement; - Katabasis::Validity validity_ = Katabasis::Validity::None; + Validity validity_ = Validity::None; // runtime only information (not saved with the account) QString internalId; diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h index b6038edb7..d3be6740e 100644 --- a/launcher/minecraft/auth/AccountList.h +++ b/launcher/minecraft/auth/AccountList.h @@ -36,7 +36,7 @@ #pragma once #include "MinecraftAccount.h" -#include "minecraft/auth/flows/AuthFlow.h" +#include "minecraft/auth/AuthFlow.h" #include #include diff --git a/launcher/minecraft/auth/flows/AuthFlow.cpp b/launcher/minecraft/auth/AuthFlow.cpp similarity index 70% rename from launcher/minecraft/auth/flows/AuthFlow.cpp rename to launcher/minecraft/auth/AuthFlow.cpp index 1b5e01569..c98d0b2c9 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.cpp +++ b/launcher/minecraft/auth/AuthFlow.cpp @@ -3,27 +3,47 @@ #include #include +#include "minecraft/auth/AccountData.h" +#include "minecraft/auth/steps/EntitlementsStep.h" +#include "minecraft/auth/steps/GetSkinStep.h" +#include "minecraft/auth/steps/LauncherLoginStep.h" +#include "minecraft/auth/steps/MSAStep.h" +#include "minecraft/auth/steps/MinecraftProfileStep.h" +#include "minecraft/auth/steps/XboxAuthorizationStep.h" +#include "minecraft/auth/steps/XboxProfileStep.h" +#include "minecraft/auth/steps/XboxUserStep.h" + #include "AuthFlow.h" #include -AuthFlow::AuthFlow(AccountData* data, QObject* parent) : Task(parent), m_data(data) +AuthFlow::AuthFlow(AccountData* data, bool silent, QObject* parent) : Task(parent), m_data(data) { + if (data->type == AccountType::MSA) { + auto oauthStep = makeShared(m_data, silent); + connect(oauthStep.get(), &MSAStep::authorizeWithBrowser, this, &AuthFlow::authorizeWithBrowser); + m_steps.append(oauthStep); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); + m_steps.append( + makeShared(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + m_steps.append(makeShared(m_data)); + } changeState(AccountTaskState::STATE_CREATED); } void AuthFlow::succeed() { - m_data->validity_ = Katabasis::Validity::Certain; + m_data->validity_ = Validity::Certain; changeState(AccountTaskState::STATE_SUCCEEDED, tr("Finished all authentication steps")); } void AuthFlow::executeTask() { - if (m_currentStep) { - emitFailed("No task"); - return; - } changeState(AccountTaskState::STATE_WORKING, tr("Initializing")); nextStep(); } @@ -46,9 +66,8 @@ void AuthFlow::nextStep() void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) { - if (changeState(resultingState, message)) { + if (changeState(resultingState, message)) nextStep(); - } } bool AuthFlow::changeState(AccountTaskState newState, QString reason) diff --git a/launcher/minecraft/auth/flows/AuthFlow.h b/launcher/minecraft/auth/AuthFlow.h similarity index 80% rename from launcher/minecraft/auth/flows/AuthFlow.h rename to launcher/minecraft/auth/AuthFlow.h index de563c3c5..611c25058 100644 --- a/launcher/minecraft/auth/flows/AuthFlow.h +++ b/launcher/minecraft/auth/AuthFlow.h @@ -15,30 +15,26 @@ class AuthFlow : public Task { Q_OBJECT public: - explicit AuthFlow(AccountData* data, QObject* parent = 0); + explicit AuthFlow(AccountData* data, bool silent = false, QObject* parent = 0); virtual ~AuthFlow() = default; - Katabasis::Validity validity() { return m_data->validity_; }; - void executeTask() override; AccountTaskState taskState() { return m_taskState; } signals: - void activityChanged(Katabasis::Activity activity); + void authorizeWithBrowser(const QUrl& url); protected: void succeed(); void nextStep(); - protected slots: + private slots: // NOTE: true -> non-terminal state, false -> terminal state bool changeState(AccountTaskState newState, QString reason = QString()); - - private slots: void stepFinished(AccountTaskState resultingState, QString message); - protected: + private: AccountTaskState m_taskState = AccountTaskState::STATE_CREATED; QList m_steps; AuthStep::Ptr m_currentStep; diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index d927cd6b4..f8a43900b 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -50,11 +50,8 @@ #include -#include "flows/MSA.h" -#include "flows/Offline.h" #include "minecraft/auth/AccountData.h" -#include "minecraft/auth/flows/AuthFlow.h" -#include "tasks/Task.h" +#include "minecraft/auth/AuthFlow.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { @@ -82,7 +79,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username) auto account = makeShared(); account->data.type = AccountType::Offline; account->data.yggdrasilToken.token = "0"; - account->data.yggdrasilToken.validity = Katabasis::Validity::Certain; + account->data.yggdrasilToken.validity = Validity::Certain; account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); @@ -90,7 +87,7 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username) account->data.minecraftEntitlement.canPlayMinecraft = true; account->data.minecraftProfile.id = uuidFromUsername(username).toString().remove(QRegularExpression("[{}-]")); account->data.minecraftProfile.name = username; - account->data.minecraftProfile.validity = Katabasis::Validity::Certain; + account->data.minecraftProfile.validity = Validity::Certain; return account; } @@ -122,23 +119,11 @@ QPixmap MinecraftAccount::getFace() const return skin.scaled(64, 64, Qt::KeepAspectRatio); } -shared_qobject_ptr MinecraftAccount::loginMSA() +shared_qobject_ptr MinecraftAccount::login() { Q_ASSERT(m_currentTask.get() == nullptr); - m_currentTask.reset(new MSAInteractive(&data)); - connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); - connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); - connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); - emit activityChanged(true); - return m_currentTask; -} - -shared_qobject_ptr MinecraftAccount::loginOffline() -{ - Q_ASSERT(m_currentTask.get() == nullptr); - - m_currentTask.reset(new OfflineLogin(&data)); + m_currentTask.reset(new AuthFlow(&data, false, this)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); @@ -152,11 +137,7 @@ shared_qobject_ptr MinecraftAccount::refresh() return m_currentTask; } - if (data.type == AccountType::MSA) { - m_currentTask.reset(new MSASilent(&data)); - } else { - m_currentTask.reset(new OfflineLogin(&data)); - } + m_currentTask.reset(new AuthFlow(&data, true, this)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); @@ -191,17 +172,17 @@ void MinecraftAccount::authFailed(QString reason) if (accountType() == AccountType::MSA) { data.msaToken.token = QString(); data.msaToken.refresh_token = QString(); - data.msaToken.validity = Katabasis::Validity::None; - data.validity_ = Katabasis::Validity::None; + data.msaToken.validity = Validity::None; + data.validity_ = Validity::None; } else { data.yggdrasilToken.token = QString(); - data.yggdrasilToken.validity = Katabasis::Validity::None; - data.validity_ = Katabasis::Validity::None; + data.yggdrasilToken.validity = Validity::None; + data.validity_ = Validity::None; } emit changed(); } break; case AccountTaskState::STATE_FAILED_GONE: { - data.validity_ = Katabasis::Validity::None; + data.validity_ = Validity::None; emit changed(); } break; case AccountTaskState::STATE_CREATED: @@ -231,13 +212,13 @@ bool MinecraftAccount::shouldRefresh() const return false; } switch (data.validity_) { - case Katabasis::Validity::Certain: { + case Validity::Certain: { break; } - case Katabasis::Validity::None: { + case Validity::None: { return false; } - case Katabasis::Validity::Assumed: { + case Validity::Assumed: { return true; } } diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 24600cb73..6f540c572 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -47,7 +47,7 @@ #include "AuthSession.h" #include "QObjectPtr.h" #include "Usable.h" -#include "minecraft/auth/flows/AuthFlow.h" +#include "minecraft/auth/AuthFlow.h" class Task; class MinecraftAccount; @@ -95,9 +95,7 @@ class MinecraftAccount : public QObject, public Usable { QJsonObject saveToJson() const; public: /* manipulation */ - shared_qobject_ptr loginMSA(); - - shared_qobject_ptr loginOffline(); + shared_qobject_ptr login(); shared_qobject_ptr refresh(); diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index f6179a93e..a2b97e278 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -79,7 +79,7 @@ bool getBool(QJsonValue value, bool& out) // 2148916238 = child account not linked to a family */ -bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString name) +bool parseXTokenResponse(QByteArray& data, Token& output, QString name) { qDebug() << "Parsing" << name << ":"; qCDebug(authCredentials()) << data; @@ -135,7 +135,7 @@ bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString nam qWarning() << "Missing uhs"; return false; } - output.validity = Katabasis::Validity::Certain; + output.validity = Validity::Certain; qDebug() << name << "is valid."; return true; } @@ -213,7 +213,7 @@ bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output) output.capes[capeOut.id] = capeOut; } output.currentCape = currentCape; - output.validity = Katabasis::Validity::Certain; + output.validity = Validity::Certain; return true; } @@ -388,7 +388,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) output.currentCape = capeOut.alias; } - output.validity = Katabasis::Validity::Certain; + output.validity = Validity::Certain; return true; } @@ -422,7 +422,7 @@ bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output) output.ownsMinecraft = true; } } - output.validity = Katabasis::Validity::Certain; + output.validity = Validity::Certain; return true; } @@ -456,7 +456,7 @@ bool parseRolloutResponse(QByteArray& data, bool& result) return true; } -bool parseMojangResponse(QByteArray& data, Katabasis::Token& output) +bool parseMojangResponse(QByteArray& data, Token& output) { QJsonParseError jsonError; qDebug() << "Parsing Mojang response..."; @@ -488,7 +488,7 @@ bool parseMojangResponse(QByteArray& data, Katabasis::Token& output) qWarning() << "access_token is not valid"; return false; } - output.validity = Katabasis::Validity::Certain; + output.validity = Validity::Certain; qDebug() << "Mojang response is valid."; return true; } diff --git a/launcher/minecraft/auth/Parsers.h b/launcher/minecraft/auth/Parsers.h index d073f9994..4a235e4c2 100644 --- a/launcher/minecraft/auth/Parsers.h +++ b/launcher/minecraft/auth/Parsers.h @@ -9,8 +9,8 @@ bool getNumber(QJsonValue value, double& out); bool getNumber(QJsonValue value, int64_t& out); bool getBool(QJsonValue value, bool& out); -bool parseXTokenResponse(QByteArray& data, Katabasis::Token& output, QString name); -bool parseMojangResponse(QByteArray& data, Katabasis::Token& output); +bool parseXTokenResponse(QByteArray& data, Token& output, QString name); +bool parseMojangResponse(QByteArray& data, Token& output); bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output); bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output); diff --git a/launcher/minecraft/auth/flows/MSA.cpp b/launcher/minecraft/auth/flows/MSA.cpp deleted file mode 100644 index f0399342e..000000000 --- a/launcher/minecraft/auth/flows/MSA.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "MSA.h" - -#include "minecraft/auth/steps/EntitlementsStep.h" -#include "minecraft/auth/steps/GetSkinStep.h" -#include "minecraft/auth/steps/LauncherLoginStep.h" -#include "minecraft/auth/steps/MSAStep.h" -#include "minecraft/auth/steps/MinecraftProfileStep.h" -#include "minecraft/auth/steps/XboxAuthorizationStep.h" -#include "minecraft/auth/steps/XboxProfileStep.h" -#include "minecraft/auth/steps/XboxUserStep.h" - -MSASilent::MSASilent(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data, MSAStep::Action::Refresh)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); - m_steps.append(makeShared(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} - -MSAInteractive::MSAInteractive(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data, MSAStep::Action::Login)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); - m_steps.append(makeShared(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} diff --git a/launcher/minecraft/auth/flows/MSA.h b/launcher/minecraft/auth/flows/MSA.h deleted file mode 100644 index e403d530f..000000000 --- a/launcher/minecraft/auth/flows/MSA.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "AuthFlow.h" - -class MSAInteractive : public AuthFlow { - Q_OBJECT - public: - explicit MSAInteractive(AccountData* data, QObject* parent = 0); -}; - -class MSASilent : public AuthFlow { - Q_OBJECT - public: - explicit MSASilent(AccountData* data, QObject* parent = 0); -}; diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp deleted file mode 100644 index 1836533bd..000000000 --- a/launcher/minecraft/auth/flows/Offline.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "Offline.h" - -#include "minecraft/auth/steps/OfflineStep.h" - -OfflineLogin::OfflineLogin(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data)); -} diff --git a/launcher/minecraft/auth/flows/Offline.h b/launcher/minecraft/auth/flows/Offline.h deleted file mode 100644 index a8d378e16..000000000 --- a/launcher/minecraft/auth/flows/Offline.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "AuthFlow.h" - -class OfflineLogin : public AuthFlow { - Q_OBJECT - public: - explicit OfflineLogin(AccountData* data, QObject* parent = 0); -}; diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 79cb062b6..4d3c2d202 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -37,12 +37,11 @@ #include #include -#include #include #include "Application.h" -MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(action) +MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent) { m_clientId = APPLICATION->getMSAClientID(); @@ -63,7 +62,7 @@ MSAStep::MSAStep(AccountData* data, Action action) : AuthStep(data), m_action(ac m_data->msaToken.token = oauth2.token(); emit finished(AccountTaskState::STATE_WORKING, tr("Got ")); }); - connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &QDesktopServices::openUrl); + connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser); connect(&oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this](const QAbstractOAuth2::Error err) { emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Microsoft user authentication failed.")); }); @@ -82,30 +81,25 @@ QString MSAStep::describe() void MSAStep::perform() { - switch (m_action) { - case Refresh: { - if (m_data->msaClientID != m_clientId) { - emit finished(AccountTaskState::STATE_DISABLED, - tr("Microsoft user authentication failed - client identification has changed.")); - } - oauth2.setRefreshToken(m_data->msaToken.refresh_token); - oauth2.refreshAccessToken(); - return; + if (m_silent) { + if (m_data->msaClientID != m_clientId) { + emit finished(AccountTaskState::STATE_DISABLED, + tr("Microsoft user authentication failed - client identification has changed.")); } - case Login: { + oauth2.setRefreshToken(m_data->msaToken.refresh_token); + oauth2.refreshAccessToken(); + } else { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // QMultiMap param changed in 6.0 - oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMultiMap* map) { + oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMultiMap* map) { #else - oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMap* map) { + oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMap* map) { #endif - map->insert("prompt", "select_account"); - map->insert("cobrandid", "8058f65d-ce06-4c30-9559-473c9275a65d"); - }); + map->insert("prompt", "select_account"); + map->insert("cobrandid", "8058f65d-ce06-4c30-9559-473c9275a65d"); + }); - *m_data = AccountData(); - m_data->msaClientID = m_clientId; - oauth2.grant(); - return; - } + *m_data = AccountData(); + m_data->msaClientID = m_clientId; + oauth2.grant(); } } diff --git a/launcher/minecraft/auth/steps/MSAStep.h b/launcher/minecraft/auth/steps/MSAStep.h index f38f5c70e..675cfb2ca 100644 --- a/launcher/minecraft/auth/steps/MSAStep.h +++ b/launcher/minecraft/auth/steps/MSAStep.h @@ -42,18 +42,18 @@ class MSAStep : public AuthStep { Q_OBJECT public: - enum Action { Refresh, Login }; - - public: - explicit MSAStep(AccountData* data, Action action); + explicit MSAStep(AccountData* data, bool silent = false); virtual ~MSAStep() noexcept = default; void perform() override; QString describe() override; + signals: + void authorizeWithBrowser(const QUrl& url); + private: - Action m_action; + bool m_silent; QString m_clientId; QOAuth2AuthorizationCodeFlow oauth2; }; diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp deleted file mode 100644 index a96b08377..000000000 --- a/launcher/minecraft/auth/steps/OfflineStep.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "OfflineStep.h" - -OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {} - -QString OfflineStep::describe() -{ - return tr("Creating offline account."); -} - -void OfflineStep::perform() -{ - emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account.")); -} diff --git a/launcher/minecraft/auth/steps/OfflineStep.h b/launcher/minecraft/auth/steps/OfflineStep.h deleted file mode 100644 index 411879b10..000000000 --- a/launcher/minecraft/auth/steps/OfflineStep.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -#include "minecraft/auth/AuthStep.h" - -class OfflineStep : public AuthStep { - Q_OBJECT - public: - explicit OfflineStep(AccountData* data); - virtual ~OfflineStep() noexcept = default; - - void perform() override; - - QString describe() override; -}; diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp index 2ae3af0dd..f07220986 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp @@ -11,7 +11,7 @@ #include "net/StaticHeaderProxy.h" #include "net/Upload.h" -XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind) +XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Token* token, QString relyingParty, QString authorizationKind) : AuthStep(data), m_token(token), m_relyingParty(relyingParty), m_authorizationKind(authorizationKind) {} @@ -72,7 +72,7 @@ void XboxAuthorizationStep::onRequestDone() return; } - Katabasis::Token temp; + Token temp; if (!Parsers::parseXTokenResponse(*m_response, temp, m_authorizationKind)) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind)); diff --git a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h index eb7097f6f..f6329b7f0 100644 --- a/launcher/minecraft/auth/steps/XboxAuthorizationStep.h +++ b/launcher/minecraft/auth/steps/XboxAuthorizationStep.h @@ -9,7 +9,7 @@ class XboxAuthorizationStep : public AuthStep { Q_OBJECT public: - explicit XboxAuthorizationStep(AccountData* data, Katabasis::Token* token, QString relyingParty, QString authorizationKind); + explicit XboxAuthorizationStep(AccountData* data, Token* token, QString relyingParty, QString authorizationKind); virtual ~XboxAuthorizationStep() noexcept = default; void perform() override; @@ -23,7 +23,7 @@ class XboxAuthorizationStep : public AuthStep { void onRequestDone(); private: - Katabasis::Token* m_token; + Token* m_token; QString m_relyingParty; QString m_authorizationKind; diff --git a/launcher/minecraft/auth/steps/XboxUserStep.cpp b/launcher/minecraft/auth/steps/XboxUserStep.cpp index 46c3f0365..c9453dba1 100644 --- a/launcher/minecraft/auth/steps/XboxUserStep.cpp +++ b/launcher/minecraft/auth/steps/XboxUserStep.cpp @@ -60,7 +60,7 @@ void XboxUserStep::onRequestDone() return; } - Katabasis::Token temp; + Token temp; if (!Parsers::parseXTokenResponse(*m_response, temp, "UToken")) { qWarning() << "Could not parse user authentication response..."; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication response could not be understood.")); diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index b249346a4..33df1876e 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -37,7 +37,7 @@ #include "ui_MSALoginDialog.h" #include "DesktopServices.h" -#include "minecraft/auth/flows/AuthFlow.h" +#include "minecraft/auth/AuthFlow.h" #include #include @@ -47,26 +47,24 @@ MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MSALoginDialog) { ui->setupUi(this); - ui->progressBar->setVisible(false); - ui->actionButton->setVisible(false); - // ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); - connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + ui->cancel->setEnabled(false); + ui->link->setVisible(false); + ui->copy->setVisible(false); + + connect(ui->cancel, &QPushButton::pressed, this, &QDialog::reject); + connect(ui->copy, &QPushButton::pressed, this, &MSALoginDialog::copyUrl); } int MSALoginDialog::exec() { - setUserInputsEnabled(false); - ui->progressBar->setVisible(true); - // Setup the login task and start it m_account = MinecraftAccount::createBlankMSA(); - m_loginTask = m_account->loginMSA(); + m_loginTask = m_account->login(); connect(m_loginTask.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed); connect(m_loginTask.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &MSALoginDialog::onTaskStatus); - connect(m_loginTask.get(), &Task::progress, this, &MSALoginDialog::onTaskProgress); + connect(m_loginTask.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser); m_loginTask->start(); return QDialog::exec(); @@ -77,11 +75,6 @@ MSALoginDialog::~MSALoginDialog() delete ui; } -void MSALoginDialog::setUserInputsEnabled(bool enable) -{ - ui->buttonBox->setEnabled(enable); -} - void MSALoginDialog::onTaskFailed(const QString& reason) { // Set message @@ -94,12 +87,7 @@ void MSALoginDialog::onTaskFailed(const QString& reason) processed += "
    "; } } - ui->label->setText(processed); - - // Re-enable user-interaction - setUserInputsEnabled(true); - ui->progressBar->setVisible(false); - ui->actionButton->setVisible(false); + ui->message->setText(processed); } void MSALoginDialog::onTaskSucceeded() @@ -109,22 +97,38 @@ void MSALoginDialog::onTaskSucceeded() void MSALoginDialog::onTaskStatus(const QString& status) { - ui->label->setText(status); -} - -void MSALoginDialog::onTaskProgress(qint64 current, qint64 total) -{ - ui->progressBar->setMaximum(total); - ui->progressBar->setValue(current); + ui->message->setText(status); + ui->cancel->setEnabled(false); + ui->link->setVisible(false); + ui->copy->setVisible(false); } // Public interface MinecraftAccountPtr MSALoginDialog::newAccount(QWidget* parent, QString msg) { MSALoginDialog dlg(parent); - dlg.ui->label->setText(msg); + dlg.ui->message->setText(msg); if (dlg.exec() == QDialog::Accepted) { return dlg.m_account; } return nullptr; } + +void MSALoginDialog::authorizeWithBrowser(const QUrl& url) +{ + ui->cancel->setEnabled(true); + ui->link->setVisible(true); + ui->copy->setVisible(true); + DesktopServices::openUrl(url); + ui->link->setText(url.toDisplayString()); + ui->message->setText( + tr("Browser opened to complete the login process." + "

    " + "If your browser hasn't opened, please manually open the bellow link in your browser:")); +} + +void MSALoginDialog::copyUrl() +{ + QClipboard* cb = QApplication::clipboard(); + cb->setText(ui->link->text()); +} diff --git a/launcher/ui/dialogs/MSALoginDialog.h b/launcher/ui/dialogs/MSALoginDialog.h index f14e04776..b57b83ec2 100644 --- a/launcher/ui/dialogs/MSALoginDialog.h +++ b/launcher/ui/dialogs/MSALoginDialog.h @@ -18,8 +18,8 @@ #include #include +#include "minecraft/auth/AuthFlow.h" #include "minecraft/auth/MinecraftAccount.h" -#include "minecraft/auth/flows/AuthFlow.h" namespace Ui { class MSALoginDialog; @@ -37,13 +37,12 @@ class MSALoginDialog : public QDialog { private: explicit MSALoginDialog(QWidget* parent = 0); - void setUserInputsEnabled(bool enable); - protected slots: void onTaskFailed(const QString& reason); void onTaskSucceeded(); void onTaskStatus(const QString& status); - void onTaskProgress(qint64 current, qint64 total); + void authorizeWithBrowser(const QUrl& url); + void copyUrl(); private: Ui::MSALoginDialog* ui; diff --git a/launcher/ui/dialogs/MSALoginDialog.ui b/launcher/ui/dialogs/MSALoginDialog.ui index c18d01a16..e6c9f1aad 100644 --- a/launcher/ui/dialogs/MSALoginDialog.ui +++ b/launcher/ui/dialogs/MSALoginDialog.ui @@ -21,11 +21,9 @@ - + - Message label placeholder. - -aaaaa + Qt::RichText @@ -39,36 +37,33 @@ aaaaa - - - 24 - - - false - - - - - + - - - Open page and copy code + + + false - - - Qt::Horizontal + + + - - QDialogButtonBox::Cancel + + + + + + Cancel + + + diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp index cd3102135..b9d1c2915 100644 --- a/launcher/ui/dialogs/OfflineLoginDialog.cpp +++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp @@ -26,7 +26,7 @@ void OfflineLoginDialog::accept() // Setup the login task and start it m_account = MinecraftAccount::createOffline(ui->userTextBox->text()); - m_loginTask = m_account->loginOffline(); + m_loginTask = m_account->login(); connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed); connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus); From 8b051b97fd671d7a04ece2e5df580b3099bb90a3 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 May 2024 17:54:35 +0300 Subject: [PATCH 385/755] Fix CI2 Signed-off-by: Trial97 --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 261f67819..5255f865b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,7 +23,7 @@ jobs: run: sudo apt-get -y update - sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 + sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 libqt5networkauth5 libqt5networkauth5-dev - name: Configure and Build run: | From 1d384544d36fdc31b137827b1fc6e0ca62a350cb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 19:40:15 +0000 Subject: [PATCH 386/755] chore(deps): update cachix/install-nix-action action to v27 --- .github/workflows/update-flake.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index 855b105ea..a923edd1d 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + - uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27 - uses: DeterminateSystems/update-flake-lock@v21 with: From 898ee67a07aa62ff01c0224d7d7dc1c7658eee22 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Thu, 16 May 2024 00:41:07 +0300 Subject: [PATCH 387/755] Update launcher/ui/dialogs/MSALoginDialog.cpp Co-authored-by: Tayou Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/dialogs/MSALoginDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index 33df1876e..b36e8f9b3 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -124,7 +124,7 @@ void MSALoginDialog::authorizeWithBrowser(const QUrl& url) ui->message->setText( tr("Browser opened to complete the login process." "

    " - "If your browser hasn't opened, please manually open the bellow link in your browser:")); + "If your browser hasn't opened, please manually open the below link in your browser:")); } void MSALoginDialog::copyUrl() From c15f6fcd2a8a380b59f7f49cae5010cc93efd386 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 16 May 2024 17:21:30 +0300 Subject: [PATCH 388/755] Fix portable if manifest is missing Signed-off-by: Trial97 --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 5fe22bdd0..e9c75e080 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -474,8 +474,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar target_dir = QDir(m_rootPath).absoluteFilePath(".."); } - QMetaObject::invokeMethod( - this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); @@ -1118,7 +1117,6 @@ void PrismUpdaterApp::backupAppDir() "Qt*.dll", }); } - file_list.append("portable.txt"); logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); From 2a58fb0ac51ebe1a1aa1b81658b4f0334f81cfa8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 16 May 2024 17:31:25 +0300 Subject: [PATCH 389/755] Remove cobrandid Signed-off-by: Trial97 --- launcher/minecraft/auth/steps/MSAStep.cpp | 1 - launcher/net/NetRequest.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 4d3c2d202..3c55540dc 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -95,7 +95,6 @@ void MSAStep::perform() oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMap* map) { #endif map->insert("prompt", "select_account"); - map->insert("cobrandid", "8058f65d-ce06-4c30-9559-473c9275a65d"); }); *m_data = AccountData(); diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 55a4f185c..cfd93b61f 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -37,11 +37,11 @@ */ #include "NetRequest.h" -#include -#include #include #include +#include +#include #include #if defined(LAUNCHER_APPLICATION) From 051e7886837090ba62ad81b6f835372649a93b69 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Thu, 16 May 2024 19:45:00 +0300 Subject: [PATCH 390/755] Update launcher/updater/prismupdater/PrismUpdater.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/updater/prismupdater/PrismUpdater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index e9c75e080..054206c67 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -474,7 +474,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar target_dir = QDir(m_rootPath).absoluteFilePath(".."); } - QMetaObject::invokeMethod(this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection);Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); From ab5f628453de6fc9301f73413027846451a78bd0 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Thu, 16 May 2024 19:45:43 +0300 Subject: [PATCH 391/755] Update launcher/updater/prismupdater/PrismUpdater.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/updater/prismupdater/PrismUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 054206c67..f3cc2ad56 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -475,7 +475,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } QMetaObject::invokeMethod( - this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection);Qt::QueuedConnection); + this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); From 09c0c11033756e7fd18b5bb51d4427f6cf793b42 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 16 May 2024 21:58:25 +0300 Subject: [PATCH 392/755] Add back device code flow Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 2 + launcher/minecraft/auth/AuthFlow.cpp | 17 +- launcher/minecraft/auth/AuthFlow.h | 5 +- launcher/minecraft/auth/MinecraftAccount.cpp | 6 +- launcher/minecraft/auth/MinecraftAccount.h | 2 +- .../auth/steps/MSADeviceCodeStep.cpp | 272 ++++++++++++++++++ .../minecraft/auth/steps/MSADeviceCodeStep.h | 76 +++++ launcher/net/ByteArraySink.h | 4 - launcher/net/NetRequest.cpp | 11 +- launcher/net/Upload.cpp | 3 +- launcher/ui/dialogs/MSALoginDialog.cpp | 61 +++- launcher/ui/dialogs/MSALoginDialog.h | 13 +- launcher/ui/dialogs/MSALoginDialog.ui | 32 ++- launcher/ui/pages/global/AccountListPage.cpp | 13 +- 14 files changed, 481 insertions(+), 36 deletions(-) create mode 100644 launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp create mode 100644 launcher/minecraft/auth/steps/MSADeviceCodeStep.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index e63328e6e..833f994ff 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -228,6 +228,8 @@ set(MINECRAFT_SOURCES minecraft/auth/steps/LauncherLoginStep.h minecraft/auth/steps/MinecraftProfileStep.cpp minecraft/auth/steps/MinecraftProfileStep.h + minecraft/auth/steps/MSADeviceCodeStep.cpp + minecraft/auth/steps/MSADeviceCodeStep.h minecraft/auth/steps/MSAStep.cpp minecraft/auth/steps/MSAStep.h minecraft/auth/steps/XboxAuthorizationStep.cpp diff --git a/launcher/minecraft/auth/AuthFlow.cpp b/launcher/minecraft/auth/AuthFlow.cpp index c98d0b2c9..5648fe9f6 100644 --- a/launcher/minecraft/auth/AuthFlow.cpp +++ b/launcher/minecraft/auth/AuthFlow.cpp @@ -7,22 +7,31 @@ #include "minecraft/auth/steps/EntitlementsStep.h" #include "minecraft/auth/steps/GetSkinStep.h" #include "minecraft/auth/steps/LauncherLoginStep.h" +#include "minecraft/auth/steps/MSADeviceCodeStep.h" #include "minecraft/auth/steps/MSAStep.h" #include "minecraft/auth/steps/MinecraftProfileStep.h" #include "minecraft/auth/steps/XboxAuthorizationStep.h" #include "minecraft/auth/steps/XboxProfileStep.h" #include "minecraft/auth/steps/XboxUserStep.h" +#include "tasks/Task.h" #include "AuthFlow.h" #include -AuthFlow::AuthFlow(AccountData* data, bool silent, QObject* parent) : Task(parent), m_data(data) +AuthFlow::AuthFlow(AccountData* data, Action action, QObject* parent) : Task(parent), m_data(data) { if (data->type == AccountType::MSA) { - auto oauthStep = makeShared(m_data, silent); - connect(oauthStep.get(), &MSAStep::authorizeWithBrowser, this, &AuthFlow::authorizeWithBrowser); - m_steps.append(oauthStep); + if (action == Action::DeviceCode) { + auto oauthStep = makeShared(m_data); + connect(oauthStep.get(), &MSADeviceCodeStep::authorizeWithBrowser, this, &AuthFlow::authorizeWithBrowserWithExtra); + connect(this, &Task::aborted, oauthStep.get(), &MSADeviceCodeStep::abort); + m_steps.append(oauthStep); + } else { + auto oauthStep = makeShared(m_data, action == Action::Refresh); + connect(oauthStep.get(), &MSAStep::authorizeWithBrowser, this, &AuthFlow::authorizeWithBrowser); + m_steps.append(oauthStep); + } m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data, &m_data->xboxApiToken, "http://xboxlive.com", "Xbox")); m_steps.append( diff --git a/launcher/minecraft/auth/AuthFlow.h b/launcher/minecraft/auth/AuthFlow.h index 611c25058..d99deec3c 100644 --- a/launcher/minecraft/auth/AuthFlow.h +++ b/launcher/minecraft/auth/AuthFlow.h @@ -15,7 +15,9 @@ class AuthFlow : public Task { Q_OBJECT public: - explicit AuthFlow(AccountData* data, bool silent = false, QObject* parent = 0); + enum class Action { Refresh, Login, DeviceCode }; + + explicit AuthFlow(AccountData* data, Action action = Action::Refresh, QObject* parent = 0); virtual ~AuthFlow() = default; void executeTask() override; @@ -24,6 +26,7 @@ class AuthFlow : public Task { signals: void authorizeWithBrowser(const QUrl& url); + void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn); protected: void succeed(); diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index f8a43900b..3c7129d5f 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -119,11 +119,11 @@ QPixmap MinecraftAccount::getFace() const return skin.scaled(64, 64, Qt::KeepAspectRatio); } -shared_qobject_ptr MinecraftAccount::login() +shared_qobject_ptr MinecraftAccount::login(bool useDeviceCode) { Q_ASSERT(m_currentTask.get() == nullptr); - m_currentTask.reset(new AuthFlow(&data, false, this)); + m_currentTask.reset(new AuthFlow(&data, useDeviceCode ? AuthFlow::Action::DeviceCode : AuthFlow::Action::Login, this)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); @@ -137,7 +137,7 @@ shared_qobject_ptr MinecraftAccount::refresh() return m_currentTask; } - m_currentTask.reset(new AuthFlow(&data, true, this)); + m_currentTask.reset(new AuthFlow(&data, AuthFlow::Action::Refresh, this)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index 6f540c572..b5c623a26 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -95,7 +95,7 @@ class MinecraftAccount : public QObject, public Usable { QJsonObject saveToJson() const; public: /* manipulation */ - shared_qobject_ptr login(); + shared_qobject_ptr login(bool useDeviceCode = false); shared_qobject_ptr refresh(); diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp new file mode 100644 index 000000000..436ed58bf --- /dev/null +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MSADeviceCodeStep.h" +#include +#include +#include + +#include + +#include "Application.h" +#include "Json.h" +#include "net/StaticHeaderProxy.h" + +// https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code +MSADeviceCodeStep::MSADeviceCodeStep(AccountData* data) : AuthStep(data) +{ + m_clientId = APPLICATION->getMSAClientID(); +} + +QString MSADeviceCodeStep::describe() +{ + return tr("Logging in with Microsoft account(device code)."); +} + +void MSADeviceCodeStep::perform() +{ + QUrlQuery data; + data.addQueryItem("client_id", m_clientId); + data.addQueryItem("scope", "XboxLive.SignIn XboxLive.offline_access"); + auto payload = data.query(QUrl::FullyEncoded).toUtf8(); + QUrl url("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"); + auto headers = QList{ + { "Content-Type", "application/x-www-form-urlencoded" }, + { "Accept", "application/json" }, + }; + m_response.reset(new QByteArray()); + m_task = Net::Upload::makeByteArray(url, m_response, payload); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAutorizationFinished); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); +} + +struct DeviceAutorizationResponse { + QString device_code; + QString user_code; + QString verification_uri; + int expires_in; + int interval; + + QString error; + QString error_description; +}; + +DeviceAutorizationResponse parseDeviceAutorizationResponse(const QByteArray& data) +{ + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(data, &err); + if (err.error != QJsonParseError::NoError) { + qWarning() << "Failed to parse device autorization response due to err:" << err.errorString(); + return {}; + } + + if (!doc.isObject()) { + qWarning() << "Device autorization response is not an object"; + return {}; + } + auto obj = doc.object(); + return { + Json::ensureString(obj, "device_code"), Json::ensureString(obj, "user_code"), Json::ensureString(obj, "verification_uri"), + Json::ensureInteger(obj, "expires_in"), Json::ensureInteger(obj, "interval"), Json::ensureString(obj, "error"), + Json::ensureString(obj, "error_description"), + }; +} + +void MSADeviceCodeStep::deviceAutorizationFinished() +{ + auto rsp = parseDeviceAutorizationResponse(*m_response); + if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) { + qWarning() << "Device authorization failed:" << rsp.error; + emit finished(AccountTaskState::STATE_FAILED_HARD, + tr("Device authorization failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description)); + return; + } + if (!m_task->wasSuccessful() || m_task->error() != QNetworkReply::NoError) { + emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Failed to retrieve device authorization")); + qDebug() << *m_response; + return; + } + + if (rsp.device_code.isEmpty() || rsp.user_code.isEmpty() || rsp.verification_uri.isEmpty() || rsp.expires_in == 0) { + emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: required fields missing")); + return; + } + if (rsp.interval != 0) { + interval = rsp.interval; + } + m_device_code = rsp.device_code; + emit authorizeWithBrowser(rsp.verification_uri, rsp.user_code, rsp.expires_in); + m_expiration_timer.setTimerType(Qt::VeryCoarseTimer); + m_expiration_timer.setInterval(rsp.expires_in * 1000); + m_expiration_timer.setSingleShot(true); + connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort); + m_expiration_timer.start(); + + m_pool_timer.setTimerType(Qt::VeryCoarseTimer); + m_pool_timer.setSingleShot(true); + startPoolTimer(); +} + +void MSADeviceCodeStep::abort() +{ + m_expiration_timer.stop(); + m_pool_timer.stop(); + if (m_task) { + m_task->abort(); + } + m_is_aborted = true; + emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Task aborted")); +} + +void MSADeviceCodeStep::startPoolTimer() +{ + if (m_is_aborted) { + return; + } + m_pool_timer.setInterval(interval * 1000); + connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser); + m_pool_timer.start(); +} + +void MSADeviceCodeStep::authenticateUser() +{ + QUrlQuery data; + data.addQueryItem("client_id", m_clientId); + data.addQueryItem("grant_type", "urn:ietf:params:oauth:grant-type:device_code"); + data.addQueryItem("device_code", m_device_code); + auto payload = data.query(QUrl::FullyEncoded).toUtf8(); + QUrl url("https://login.microsoftonline.com/consumers/oauth2/v2.0/token"); + auto headers = QList{ + { "Content-Type", "application/x-www-form-urlencoded" }, + { "Accept", "application/json" }, + }; + m_response.reset(new QByteArray()); + m_task = Net::Upload::makeByteArray(url, m_response, payload); + m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers)); + + connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::authenticationFinished); + + m_task->setNetwork(APPLICATION->network()); + m_task->start(); +} + +struct AuthenticationResponse { + QString access_token; + QString token_type; + QString refresh_token; + int expires_in; + + QString error; + QString error_description; + + QVariantMap extra; +}; + +AuthenticationResponse parseAuthenticationResponse(const QByteArray& data) +{ + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(data, &err); + if (err.error != QJsonParseError::NoError) { + qWarning() << "Failed to parse device autorization response due to err:" << err.errorString(); + return {}; + } + + if (!doc.isObject()) { + qWarning() << "Device autorization response is not an object"; + return {}; + } + auto obj = doc.object(); + return { Json::ensureString(obj, "access_token"), + Json::ensureString(obj, "token_type"), + Json::ensureString(obj, "refresh_token"), + Json::ensureInteger(obj, "expires_in"), + Json::ensureString(obj, "error"), + Json::ensureString(obj, "error_description"), + obj.toVariantMap() }; +} + +void MSADeviceCodeStep::authenticationFinished() +{ + if (m_task->error() == QNetworkReply::TimeoutError) { + // rfc8628#section-3.5 + // "On encountering a connection timeout, clients MUST unilaterally + // reduce their polling frequency before retrying. The use of an + // exponential backoff algorithm to achieve this, such as doubling the + // polling interval on each such connection timeout, is RECOMMENDED." + interval *= 2; + startPoolTimer(); + return; + } + auto rsp = parseAuthenticationResponse(*m_response); + if (rsp.error == "slow_down") { + // rfc8628#section-3.5 + // "A variant of 'authorization_pending', the authorization request is + // still pending and polling should continue, but the interval MUST + // be increased by 5 seconds for this and all subsequent requests." + interval += 5; + startPoolTimer(); + return; + } + if (rsp.error == "authorization_pending") { + // keep trying - rfc8628#section-3.5 + // "The authorization request is still pending as the end user hasn't + // yet completed the user-interaction steps (Section 3.3)." + startPoolTimer(); + return; + } + if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) { + qWarning() << "Device Access failed:" << rsp.error; + emit finished(AccountTaskState::STATE_FAILED_HARD, + tr("Device Access failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description)); + return; + } + if (!m_task->wasSuccessful() || m_task->error() != QNetworkReply::NoError) { + startPoolTimer(); // it failed so just try again without increasing the interval + return; + } + + m_expiration_timer.stop(); + m_data->msaClientID = m_clientId; + m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc(); + m_data->msaToken.notAfter = QDateTime::currentDateTime().addSecs(rsp.expires_in); + m_data->msaToken.extra = rsp.extra; + m_data->msaToken.refresh_token = rsp.refresh_token; + m_data->msaToken.token = rsp.access_token; + emit finished(AccountTaskState::STATE_WORKING, tr("Got")); +} \ No newline at end of file diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.h b/launcher/minecraft/auth/steps/MSADeviceCodeStep.h new file mode 100644 index 000000000..e53eebc62 --- /dev/null +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include + +#include "minecraft/auth/AuthStep.h" +#include "net/Upload.h" + +class MSADeviceCodeStep : public AuthStep { + Q_OBJECT + public: + explicit MSADeviceCodeStep(AccountData* data); + virtual ~MSADeviceCodeStep() noexcept = default; + + void perform() override; + + QString describe() override; + + public slots: + void abort(); + + signals: + void authorizeWithBrowser(QString url, QString code, int expiresIn); + + private slots: + void deviceAutorizationFinished(); + void startPoolTimer(); + void authenticateUser(); + void authenticationFinished(); + + private: + QString m_clientId; + QString m_device_code; + bool m_is_aborted = false; + int interval = 5; + + QTimer m_pool_timer; + QTimer m_expiration_timer; + + std::shared_ptr m_response; + Net::Upload::Ptr m_task; +}; diff --git a/launcher/net/ByteArraySink.h b/launcher/net/ByteArraySink.h index 7b8f0f8aa..d636f6634 100644 --- a/launcher/net/ByteArraySink.h +++ b/launcher/net/ByteArraySink.h @@ -74,10 +74,6 @@ class ByteArraySink : public Sink { auto abort() -> Task::State override { - if (m_output) - m_output->clear(); - else - qWarning() << "ByteArraySink did not clear the buffer because it's not addressable"; failAllValidators(); return Task::State::Failed; } diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index cfd93b61f..abecc0cf3 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -256,21 +256,18 @@ void NetRequest::downloadFinished() { qCDebug(logCat) << getUid().toString() << "Request failed but we are allowed to proceed:" << m_url.toString(); m_sink->abort(); - m_reply.reset(); emit succeeded(); emit finished(); return; } else if (m_state == State::Failed) { qCDebug(logCat) << getUid().toString() << "Request failed in previous step:" << m_url.toString(); m_sink->abort(); - m_reply.reset(); - emit failed(""); + emit failed(m_reply->errorString()); emit finished(); return; } else if (m_state == State::AbortedByUser) { qCDebug(logCat) << getUid().toString() << "Request aborted in previous step:" << m_url.toString(); m_sink->abort(); - m_reply.reset(); emit aborted(); emit finished(); return; @@ -284,7 +281,7 @@ void NetRequest::downloadFinished() if (m_state != State::Succeeded) { qCDebug(logCat) << getUid().toString() << "Request failed to write:" << m_url.toString(); m_sink->abort(); - emit failed(""); + emit failed("failed to write in sink"); emit finished(); return; } @@ -295,13 +292,11 @@ void NetRequest::downloadFinished() if (m_state != State::Succeeded) { qCDebug(logCat) << getUid().toString() << "Request failed to finalize:" << m_url.toString(); m_sink->abort(); - m_reply.reset(); - emit failed(""); + emit failed("failed to finalize the request"); emit finished(); return; } - m_reply.reset(); qCDebug(logCat) << getUid().toString() << "Request succeeded:" << m_url.toString(); emit succeeded(); emit finished(); diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index 726572e52..623ec80f4 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -46,7 +46,8 @@ namespace Net { QNetworkReply* Upload::getReply(QNetworkRequest& request) { - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + if (!request.hasRawHeader("Content-Type")) + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return m_network->post(request, m_post_data); } diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index b36e8f9b3..a654c1d29 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -51,6 +51,7 @@ MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MS ui->cancel->setEnabled(false); ui->link->setVisible(false); ui->copy->setVisible(false); + ui->progressBar->setVisible(false); connect(ui->cancel, &QPushButton::pressed, this, &QDialog::reject); connect(ui->copy, &QPushButton::pressed, this, &MSALoginDialog::copyUrl); @@ -60,12 +61,15 @@ int MSALoginDialog::exec() { // Setup the login task and start it m_account = MinecraftAccount::createBlankMSA(); - m_loginTask = m_account->login(); - connect(m_loginTask.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed); - connect(m_loginTask.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded); - connect(m_loginTask.get(), &Task::status, this, &MSALoginDialog::onTaskStatus); - connect(m_loginTask.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser); - m_loginTask->start(); + m_task = m_account->login(m_using_device_code); + connect(m_task.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed); + connect(m_task.get(), &Task::succeeded, this, &MSALoginDialog::onTaskSucceeded); + connect(m_task.get(), &Task::status, this, &MSALoginDialog::onTaskStatus); + connect(m_task.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser); + connect(m_task.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra); + connect(ui->cancel, &QPushButton::pressed, m_task.get(), &Task::abort); + connect(&m_external_timer, &QTimer::timeout, this, &MSALoginDialog::externalLoginTick); + m_task->start(); return QDialog::exec(); } @@ -101,12 +105,14 @@ void MSALoginDialog::onTaskStatus(const QString& status) ui->cancel->setEnabled(false); ui->link->setVisible(false); ui->copy->setVisible(false); + ui->progressBar->setVisible(false); } // Public interface -MinecraftAccountPtr MSALoginDialog::newAccount(QWidget* parent, QString msg) +MinecraftAccountPtr MSALoginDialog::newAccount(QWidget* parent, QString msg, bool usingDeviceCode) { MSALoginDialog dlg(parent); + dlg.m_using_device_code = usingDeviceCode; dlg.ui->message->setText(msg); if (dlg.exec() == QDialog::Accepted) { return dlg.m_account; @@ -132,3 +138,44 @@ void MSALoginDialog::copyUrl() QClipboard* cb = QApplication::clipboard(); cb->setText(ui->link->text()); } + +void MSALoginDialog::authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn) +{ + m_external_elapsed = 0; + m_external_timeout = expiresIn; + + m_external_timer.setInterval(1000); + m_external_timer.setSingleShot(false); + m_external_timer.start(); + + ui->progressBar->setMaximum(expiresIn); + ui->progressBar->setValue(m_external_elapsed); + + QString linkString = QString("%2").arg(url, url); + if (url == "https://www.microsoft.com/link" && !code.isEmpty()) { + url += QString("?otc=%1").arg(code); + ui->message->setText(tr("

    Please login in the opened browser. If no browser was opened, please open up %1 in " + "a browser and put in the code %2 to proceed with login.

    ") + .arg(linkString, code)); + } else { + ui->message->setText( + tr("

    Please open up %1 in a browser and put in the code %2 to proceed with login.

    ").arg(linkString, code)); + } + ui->cancel->setEnabled(true); + ui->link->setVisible(true); + ui->copy->setVisible(true); + ui->progressBar->setVisible(true); + DesktopServices::openUrl(url); + ui->link->setText(code); +} + +void MSALoginDialog::externalLoginTick() +{ + m_external_elapsed++; + ui->progressBar->setValue(m_external_elapsed); + ui->progressBar->repaint(); + + if (m_external_elapsed >= m_external_timeout) { + m_external_timer.stop(); + } +} \ No newline at end of file diff --git a/launcher/ui/dialogs/MSALoginDialog.h b/launcher/ui/dialogs/MSALoginDialog.h index b57b83ec2..cef647ee4 100644 --- a/launcher/ui/dialogs/MSALoginDialog.h +++ b/launcher/ui/dialogs/MSALoginDialog.h @@ -15,6 +15,7 @@ #pragma once +#include #include #include @@ -31,7 +32,7 @@ class MSALoginDialog : public QDialog { public: ~MSALoginDialog(); - static MinecraftAccountPtr newAccount(QWidget* parent, QString message); + static MinecraftAccountPtr newAccount(QWidget* parent, QString message, bool usingDeviceCode = false); int exec() override; private: @@ -42,10 +43,18 @@ class MSALoginDialog : public QDialog { void onTaskSucceeded(); void onTaskStatus(const QString& status); void authorizeWithBrowser(const QUrl& url); + void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn); void copyUrl(); + void externalLoginTick(); private: Ui::MSALoginDialog* ui; MinecraftAccountPtr m_account; - shared_qobject_ptr m_loginTask; + shared_qobject_ptr m_task; + + int m_external_elapsed; + int m_external_timeout; + QTimer m_external_timer; + + bool m_using_device_code = false; }; diff --git a/launcher/ui/dialogs/MSALoginDialog.ui b/launcher/ui/dialogs/MSALoginDialog.ui index e6c9f1aad..df1b32044 100644 --- a/launcher/ui/dialogs/MSALoginDialog.ui +++ b/launcher/ui/dialogs/MSALoginDialog.ui @@ -7,11 +7,11 @@ 0 0 491 - 143 + 208 - + 0 0 @@ -22,12 +22,27 @@ + + + 0 + 0 + + + + + 500 + 500 + + Qt::RichText + + true + true @@ -51,12 +66,23 @@ - + + .. + + + + 24 + + + false + + + diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index abd8fa228..9aec32cef 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -40,6 +40,7 @@ #include #include +#include #include @@ -134,8 +135,16 @@ void AccountListPage::listChanged() void AccountListPage::on_actionAddMicrosoft_triggered() { - MinecraftAccountPtr account = - MSALoginDialog::newAccount(this, tr("Please enter your Mojang account email and password to add your account.")); + QMessageBox box(this); + box.setWindowTitle(tr("Add account")); + box.setText(tr("How do you want to login?")); + box.setIcon(QMessageBox::Question); + auto deviceCode = box.addButton(tr("Using device code"), QMessageBox::ButtonRole::YesRole); + auto authCode = box.addButton(tr("Using auth code"), QMessageBox::ButtonRole::NoRole); + box.setDefaultButton(authCode); + box.exec(); + MinecraftAccountPtr account = MSALoginDialog::newAccount( + this, tr("Please enter your Mojang account email and password to add your account."), box.clickedButton() == deviceCode); if (account) { m_accounts->addAccount(account); From ce09f06bda74552fddd8e6fbf6588057cd2fb98b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 16 May 2024 22:16:42 +0300 Subject: [PATCH 393/755] Fix CI3 Signed-off-by: Trial97 --- launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp index 436ed58bf..22f5d4069 100644 --- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp @@ -34,10 +34,8 @@ */ #include "MSADeviceCodeStep.h" -#include -#include -#include +#include #include #include "Application.h" From 75a457ebfaf9009d87f9baa772df993e1ca32644 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 17 May 2024 00:28:37 +0300 Subject: [PATCH 394/755] Add custom page to land after microsoft login Signed-off-by: Trial97 --- launcher/minecraft/auth/steps/MSAStep.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 3c55540dc..3f31cdc16 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -46,6 +46,10 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile m_clientId = APPLICATION->getMSAClientID(); auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this); + replyHandler->setCallbackText( + "