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() {