Started workin on stuff

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2024-01-24 18:26:43 +02:00
parent 33cf7066b4
commit a0e7729aa6
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
28 changed files with 908 additions and 247 deletions

View File

@ -44,6 +44,7 @@
#include "BuildConfig.h" #include "BuildConfig.h"
#include "DataMigrationTask.h" #include "DataMigrationTask.h"
#include "java/JavaInstallList.h"
#include "net/PasteUpload.h" #include "net/PasteUpload.h"
#include "pathmatcher/MultiMatcher.h" #include "pathmatcher/MultiMatcher.h"
#include "pathmatcher/SimplePrefixMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h"
@ -125,6 +126,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys.h> #include <sys.h>
#include "SysInfo.h"
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#include <dlfcn.h> #include <dlfcn.h>
@ -607,7 +609,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Memory // Memory
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem()); m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::suitableMaxMem());
m_settings->registerSetting("PermGen", 128); m_settings->registerSetting("PermGen", 128);
// Java Settings // Java Settings
@ -1667,20 +1669,6 @@ QString Application::getUserAgentUncached()
return BuildConfig.USER_AGENT_UNCACHED; 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, bool Application::handleDataMigration(const QString& currentData,
const QString& oldData, const QString& oldData,
const QString& name, const QString& name,

View File

@ -180,8 +180,6 @@ class Application : public QApplication {
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString()); void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
int suitableMaxMem();
bool updaterEnabled(); bool updaterEnabled();
QString updaterBinaryName(); QString updaterBinaryName();

View File

@ -422,8 +422,6 @@ set(SETTINGS_SOURCES
set(JAVA_SOURCES set(JAVA_SOURCES
java/JavaChecker.h java/JavaChecker.h
java/JavaChecker.cpp java/JavaChecker.cpp
java/JavaCheckerJob.h
java/JavaCheckerJob.cpp
java/JavaInstall.h java/JavaInstall.h
java/JavaInstall.cpp java/JavaInstall.cpp
java/JavaInstallList.h java/JavaInstallList.h

View File

@ -63,7 +63,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
return true; return true;
} }
void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result) void JavaCommon::javaWasOk(QWidget* parent, const JavaChecker::Result& result)
{ {
QString text; QString text;
text += QObject::tr( 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(); 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; auto htmlError = result.errorLog;
QString text; 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(); 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; QString text;
text += QObject::tr( text += QObject::tr(
@ -116,34 +116,26 @@ void JavaCommon::TestCheck::run()
emit finished(); emit finished();
return; 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); connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
checker->m_path = m_path; checker->start();
checker->performCheck();
} }
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); javaBinaryWasBad(m_parent, result);
emit finished(); emit finished();
return; 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); connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
checker->m_path = m_path; checker->start();
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();
} }
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); javaWasOk(m_parent, result);
emit finished(); emit finished();
return; return;

View File

@ -10,11 +10,11 @@ namespace JavaCommon {
bool checkJVMArgs(QString args, QWidget* parent); bool checkJVMArgs(QString args, QWidget* parent);
// Show a dialog saying that the Java binary was usable // 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 // 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 // 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 // Show a dialog if we couldn't find Java Checker
void javaCheckNotFound(QWidget* parent); void javaCheckNotFound(QWidget* parent);
@ -32,11 +32,11 @@ class TestCheck : public QObject {
void finished(); void finished();
private slots: private slots:
void checkFinished(JavaCheckResult result); void checkFinished(JavaChecker::Result result);
void checkFinishedWithArgs(JavaCheckResult result); void checkFinishedWithArgs(JavaChecker::Result result);
private: private:
std::shared_ptr<JavaChecker> checker; JavaChecker::Ptr checker;
QWidget* m_parent = nullptr; QWidget* m_parent = nullptr;
QString m_path; QString m_path;
QString m_args; QString m_args;

View File

@ -507,6 +507,112 @@ bool ExportToZipTask::abort()
} }
return false; return false;
} }
void ExtractZipTask::executeTask()
{
m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); });
connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::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 #endif
} // namespace MMCZip } // namespace MMCZip

View File

@ -194,5 +194,33 @@ class ExportToZipTask : public Task {
QFuture<ZipResult> m_build_zip_future; QFuture<ZipResult> m_build_zip_future;
QFutureWatcher<ZipResult> m_build_zip_watcher; QFutureWatcher<ZipResult> m_build_zip_watcher;
}; };
class ExtractZipTask : public Task {
public:
ExtractZipTask(QString input, QDir outputDir, QString subdirectory = "")
: ExtractZipTask(std::make_shared<QuaZip>(input), outputDir, subdirectory)
{}
ExtractZipTask(std::shared_ptr<QuaZip> input, QDir outputDir, QString subdirectory = "")
: m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory)
{}
virtual ~ExtractZipTask() = default;
typedef std::optional<QString> ZipResult;
protected:
virtual void executeTask() override;
bool abort() override;
ZipResult extractZip();
void finish();
private:
std::shared_ptr<QuaZip> m_input;
QDir m_output_dir;
QString m_subdirectory;
QFuture<ZipResult> m_zip_future;
QFutureWatcher<ZipResult> m_zip_watcher;
};
#endif #endif
} // namespace MMCZip } // namespace MMCZip

View File

@ -1,5 +1,6 @@
#include <QDebug> #include <QDebug>
#include <QString> #include <QString>
#include "sys.h"
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
#include <sys/sysctl.h> #include <sys/sysctl.h>
#endif #endif
@ -7,9 +8,6 @@
#include <QMap> #include <QMap>
#include <QProcess> #include <QProcess>
#include <QStandardPaths> #include <QStandardPaths>
#include "Application.h"
#include "Commandline.h"
#include "java/JavaUtils.h"
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
bool rosettaDetect() bool rosettaDetect()
@ -59,4 +57,18 @@ QString useQTForArch()
#endif #endif
return qtArch; 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 } // namespace SysInfo

View File

@ -3,4 +3,5 @@
namespace SysInfo { namespace SysInfo {
QString currentSystem(); QString currentSystem();
QString useQTForArch(); QString useQTForArch();
int suitableMaxMem();
} // namespace SysInfo } // namespace SysInfo

View File

@ -40,14 +40,13 @@
#include <QMap> #include <QMap>
#include <QProcess> #include <QProcess>
#include "Application.h"
#include "Commandline.h" #include "Commandline.h"
#include "FileSystem.h" #include "java/JavaUtils.h"
#include "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(); QString checkerJar = JavaUtils::getJavaCheckPath();
@ -69,7 +68,7 @@ void JavaChecker::performCheck()
if (m_maxMem != 0) { if (m_maxMem != 0) {
args << QString("-Xmx%1m").arg(m_maxMem); 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); args << QString("-XX:PermSize=%1m").arg(m_permGen);
} }
@ -112,11 +111,10 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
QProcessPtr _process = process; QProcessPtr _process = process;
process.reset(); process.reset();
JavaCheckResult result; Result result = {
{ m_path,
result.path = m_path; m_id,
result.id = m_id; };
}
result.errorLog = m_stderr; result.errorLog = m_stderr;
result.outLog = m_stdout; result.outLog = m_stdout;
qDebug() << "STDOUT" << 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; qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
if (status == QProcess::CrashExit || exitcode == 1) { if (status == QProcess::CrashExit || exitcode == 1) {
result.validity = JavaCheckResult::Validity::Errored; result.validity = Result::Validity::Errored;
emit checkFinished(result); emit checkFinished(result);
emitSucceeded();
return; 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) { 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); emit checkFinished(result);
emitSucceeded();
return; return;
} }
@ -168,7 +168,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
auto java_vendor = results["java.vendor"]; auto java_vendor = results["java.vendor"];
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64"; 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.is_64bit = is_64;
result.mojangPlatform = is_64 ? "64" : "32"; result.mojangPlatform = is_64 ? "64" : "32";
result.realPlatform = os_arch; result.realPlatform = os_arch;
@ -176,6 +176,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
result.javaVendor = java_vendor; result.javaVendor = java_vendor;
qDebug() << "Java checker succeeded."; qDebug() << "Java checker succeeded.";
emit checkFinished(result); emit checkFinished(result);
emitSucceeded();
} }
void JavaChecker::error(QProcess::ProcessError err) void JavaChecker::error(QProcess::ProcessError err)
@ -187,15 +188,9 @@ void JavaChecker::error(QProcess::ProcessError err)
qDebug() << "Native environment:"; qDebug() << "Native environment:";
qDebug() << QProcessEnvironment::systemEnvironment().toStringList(); qDebug() << QProcessEnvironment::systemEnvironment().toStringList();
killTimer.stop(); killTimer.stop();
JavaCheckResult result; emit checkFinished({ m_path, m_id });
{
result.path = m_path;
result.id = m_id;
}
emit checkFinished(result);
return;
} }
emitSucceeded();
} }
void JavaChecker::timeout() void JavaChecker::timeout()

View File

@ -3,49 +3,51 @@
#include <QTimer> #include <QTimer>
#include <memory> #include <memory>
#include "QObjectPtr.h"
#include "JavaVersion.h" #include "JavaVersion.h"
#include "QObjectPtr.h"
#include "tasks/Task.h"
class JavaChecker; class JavaChecker : public Task {
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<QProcess>;
using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
class JavaChecker : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit JavaChecker(QObject* parent = 0); using QProcessPtr = shared_qobject_ptr<QProcess>;
void performCheck(); using Ptr = shared_qobject_ptr<JavaChecker>;
QString m_path; struct Result {
QString m_args; QString path;
int m_id = 0; int id;
int m_minMem = 0; QString mojangPlatform;
int m_maxMem = 0; QString realPlatform;
int m_permGen = 64; 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: signals:
void checkFinished(JavaCheckResult result); void checkFinished(Result result);
protected:
virtual void executeTask() override;
private: private:
QProcessPtr process; QProcessPtr process;
QTimer killTimer; QTimer killTimer;
QString m_stdout; QString m_stdout;
QString m_stderr; 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 timeout();
void finished(int exitcode, QProcess::ExitStatus); void finished(int exitcode, QProcess::ExitStatus);
void error(QProcess::ProcessError); void error(QProcess::ProcessError);

View File

@ -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 <QDebug>
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();
}
}

View File

@ -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 <QtNetwork>
#include "JavaChecker.h"
#include "tasks/Task.h"
class JavaCheckerJob;
using JavaCheckerJobPtr = shared_qobject_ptr<JavaCheckerJob>;
// 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<JavaCheckResult> getResults() { return javaresults; }
private slots:
void partFinished(JavaCheckResult result);
protected:
virtual void executeTask() override;
private:
QString m_job_name;
QList<JavaCheckerPtr> javacheckers;
QList<JavaCheckResult> javaresults;
int num_finished = 0;
};

View File

@ -38,11 +38,13 @@
#include <QtXml> #include <QtXml>
#include <QDebug> #include <QDebug>
#include <algorithm>
#include "java/JavaCheckerJob.h" #include "Application.h"
#include "java/JavaChecker.h"
#include "java/JavaInstallList.h" #include "java/JavaInstallList.h"
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "minecraft/VersionFilterData.h" #include "tasks/ConcurrentTask.h"
JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {} JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {}
@ -55,7 +57,7 @@ Task::Ptr JavaInstallList::getLoadTask()
Task::Ptr JavaInstallList::getCurrentTask() Task::Ptr JavaInstallList::getCurrentTask()
{ {
if (m_status == Status::InProgress) { if (m_status == Status::InProgress) {
return m_loadTask; return m_load_task;
} }
return nullptr; return nullptr;
} }
@ -64,8 +66,8 @@ void JavaInstallList::load()
{ {
if (m_status != Status::InProgress) { if (m_status != Status::InProgress) {
m_status = Status::InProgress; m_status = Status::InProgress;
m_loadTask.reset(new JavaListLoadTask(this)); m_load_task.reset(new JavaListLoadTask(this));
m_loadTask->start(); m_load_task->start();
} }
} }
@ -129,7 +131,7 @@ void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
} }
endResetModel(); endResetModel();
m_status = Status::Done; m_status = Status::Done;
m_loadTask.reset(); m_load_task.reset();
} }
bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right)
@ -149,11 +151,9 @@ void JavaInstallList::sortVersions()
JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task() JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task()
{ {
m_list = vlist; m_list = vlist;
m_currentRecommended = NULL; m_current_recommended = NULL;
} }
JavaListLoadTask::~JavaListLoadTask() {}
void JavaListLoadTask::executeTask() void JavaListLoadTask::executeTask()
{ {
setStatus(tr("Detecting Java installations...")); setStatus(tr("Detecting Java installations..."));
@ -161,20 +161,17 @@ void JavaListLoadTask::executeTask()
JavaUtils ju; JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths(); QList<QString> 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::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress); connect(m_job.get(), &Task::progress, this, &Task::setProgress);
qDebug() << "Probing the following Java paths: "; qDebug() << "Probing the following Java paths: ";
int id = 0; int id = 0;
for (QString candidate : candidate_paths) { for (QString candidate : candidate_paths) {
qDebug() << " " << candidate; auto checker = new JavaChecker(candidate, "", 0, 0, 0, id, this);
connect(checker, &JavaChecker::checkFinished, [this](JavaChecker::Result result) { m_results << result; });
auto candidate_checker = new JavaChecker(); job->addTask(Task::Ptr(checker));
candidate_checker->m_path = candidate;
candidate_checker->m_id = id;
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
id++; id++;
} }
@ -184,11 +181,11 @@ void JavaListLoadTask::executeTask()
void JavaListLoadTask::javaCheckerFinished() void JavaListLoadTask::javaCheckerFinished()
{ {
QList<JavaInstallPtr> candidates; QList<JavaInstallPtr> 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:"; qDebug() << "Found the following valid Java installations:";
for (JavaCheckResult result : results) { for (auto result : m_results) {
if (result.validity == JavaCheckResult::Validity::Valid) { if (result.validity == JavaChecker::Result::Validity::Valid) {
JavaInstallPtr javaVersion(new JavaInstall()); JavaInstallPtr javaVersion(new JavaInstall());
javaVersion->id = result.javaVersion; javaVersion->id = result.javaVersion;

View File

@ -19,9 +19,9 @@
#include <QObject> #include <QObject>
#include "BaseVersionList.h" #include "BaseVersionList.h"
#include "java/JavaChecker.h"
#include "tasks/Task.h" #include "tasks/Task.h"
#include "JavaCheckerJob.h"
#include "JavaInstall.h" #include "JavaInstall.h"
#include "QObjectPtr.h" #include "QObjectPtr.h"
@ -53,7 +53,7 @@ class JavaInstallList : public BaseVersionList {
protected: protected:
Status m_status = Status::NotDone; Status m_status = Status::NotDone;
shared_qobject_ptr<JavaListLoadTask> m_loadTask; shared_qobject_ptr<JavaListLoadTask> m_load_task;
QList<BaseVersion::Ptr> m_vlist; QList<BaseVersion::Ptr> m_vlist;
}; };
@ -62,14 +62,16 @@ class JavaListLoadTask : public Task {
public: public:
explicit JavaListLoadTask(JavaInstallList* vlist); explicit JavaListLoadTask(JavaInstallList* vlist);
virtual ~JavaListLoadTask(); virtual ~JavaListLoadTask() = default;
protected:
void executeTask() override; void executeTask() override;
public slots: public slots:
void javaCheckerFinished(); void javaCheckerFinished();
protected: protected:
shared_qobject_ptr<JavaCheckerJob> m_job; Task::Ptr m_job;
JavaInstallList* m_list; JavaInstallList* m_list;
JavaInstall* m_currentRecommended; JavaInstall* m_current_recommended;
QList<JavaChecker::Result> m_results;
}; };

View File

@ -15,10 +15,9 @@
#pragma once #pragma once
#include <QProcess>
#include <QStringList> #include <QStringList>
#include "java/JavaInstall.h"
#include "JavaChecker.h"
#include "JavaInstallList.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>

View File

@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#include "java/providers/AdoptiumJavaDownloader.h"
#include <quazip.h>
#include <memory>
#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<NetJob>(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<QuaZip>(input);
auto files = zip->getFileNameList();
if (files.isEmpty()) {
emitFailed("Empty archive");
return;
}
auto zipTask = makeShared<MMCZip::ExtractZipTask>(input, m_final_path, files[0]);
auto progressStep = std::make_shared<TaskStepProgress>();
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());
};

View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#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;
};

View File

@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#include "java/providers/AzulJavaDownloader.h"
#include <qcontainerfwd.h>
#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<QByteArray>();
auto downloadJob = makeShared<NetJob>(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<NetJob>(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<MMCZip::ExtractZipTask>(input, m_final_path, subdirectory);
auto progressStep = std::make_shared<TaskStepProgress>();
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());
};

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#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;
};

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#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)
{}

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#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;
};

View File

@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#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<NetJob>(QString("JRE::QueryVersions"), APPLICATION->network());
auto response = std::make_shared<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));
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<NetJob>(QString("JRE::DownloadJava"), APPLICATION->network());
auto files = std::make_shared<QByteArray>();
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<File> 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();
};

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.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 <https://www.gnu.org/licenses/>.
*/
#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;
};

View File

@ -37,6 +37,7 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <launch/LaunchTask.h> #include <launch/LaunchTask.h>
#include <sys.h> #include <sys.h>
#include <QCryptographicHash>
#include <QFileInfo> #include <QFileInfo>
#include <QStandardPaths> #include <QStandardPaths>
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
@ -90,11 +91,10 @@ void CheckJava::executeTask()
// if timestamps are not the same, or something is missing, check! // if timestamps are not the same, or something is missing, check!
if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 || if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 ||
storedRealArchitecture.size() == 0 || storedVendor.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); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath; m_JavaChecker->start();
m_JavaChecker->performCheck();
return; return;
} else { } else {
auto verString = instance->settings()->get("JavaVersion").toString(); auto verString = instance->settings()->get("JavaVersion").toString();
@ -106,10 +106,10 @@ void CheckJava::executeTask()
emitSucceeded(); emitSucceeded();
} }
void CheckJava::checkJavaFinished(JavaCheckResult result) void CheckJava::checkJavaFinished(JavaChecker::Result result)
{ {
switch (result.validity) { switch (result.validity) {
case JavaCheckResult::Validity::Errored: { case JavaChecker::Result::Validity::Errored: {
// Error message displayed if java can't start // Error message displayed if java can't start
emit logLine(QString("Could not start java:"), MessageLevel::Error); emit logLine(QString("Could not start java:"), MessageLevel::Error);
emit logLines(result.errorLog.split('\n'), 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!")); emitFailed(QString("Could not start java!"));
return; 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 logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher); emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
emitSucceeded(); emitSucceeded();
return; return;
} }
case JavaCheckResult::Validity::Valid: { case JavaChecker::Result::Validity::Valid: {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor); printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor);
instance->settings()->set("JavaVersion", result.javaVersion.toString()); instance->settings()->set("JavaVersion", result.javaVersion.toString());

View File

@ -28,7 +28,7 @@ class CheckJava : public LaunchStep {
virtual void executeTask(); virtual void executeTask();
virtual bool canAbort() const { return false; } virtual bool canAbort() const { return false; }
private slots: private slots:
void checkJavaFinished(JavaCheckResult result); void checkJavaFinished(JavaChecker::Result result);
private: private:
void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor); void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor);
@ -37,5 +37,5 @@ class CheckJava : public LaunchStep {
private: private:
QString m_javaPath; QString m_javaPath;
QString m_javaSignature; QString m_javaSignature;
JavaCheckerPtr m_JavaChecker; JavaChecker::Ptr m_JavaChecker;
}; };

View File

@ -14,7 +14,9 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "JavaCommon.h" #include "JavaCommon.h"
#include "JavaDownloader.h" #include "JavaDownloader.h"
#include "java/JavaChecker.h"
#include "java/JavaInstall.h" #include "java/JavaInstall.h"
#include "java/JavaInstallList.h"
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
@ -368,30 +370,25 @@ void JavaSettingsWidget::checkJavaPath(const QString& path)
return; return;
} }
setJavaStatus(JavaStatus::Pending); setJavaStatus(JavaStatus::Pending);
m_checker.reset(new JavaChecker()); m_checker.reset(
m_checker->m_path = path; new JavaChecker(path, "", minHeapSize(), maxHeapSize(), m_permGenSpinBox->isVisible() ? m_permGenSpinBox->value() : 0, 0, this));
m_checker->m_minMem = minHeapSize();
m_checker->m_maxMem = maxHeapSize();
if (m_permGenSpinBox->isVisible()) {
m_checker->m_permGen = m_permGenSpinBox->value();
}
connect(m_checker.get(), &JavaChecker::checkFinished, this, &JavaSettingsWidget::checkFinished); 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; m_result = result;
switch (result.validity) { switch (result.validity) {
case JavaCheckResult::Validity::Valid: { case JavaChecker::Result::Validity::Valid: {
setJavaStatus(JavaStatus::Good); setJavaStatus(JavaStatus::Good);
break; break;
} }
case JavaCheckResult::Validity::ReturnedInvalidData: { case JavaChecker::Result::Validity::ReturnedInvalidData: {
setJavaStatus(JavaStatus::ReturnedInvalidData); setJavaStatus(JavaStatus::ReturnedInvalidData);
break; break;
} }
case JavaCheckResult::Validity::Errored: { case JavaChecker::Result::Validity::Errored: {
setJavaStatus(JavaStatus::DoesNotStart); setJavaStatus(JavaStatus::DoesNotStart);
break; break;
} }

View File

@ -51,7 +51,7 @@ class JavaSettingsWidget : public QWidget {
void on_javaBrowseBtn_clicked(); void on_javaBrowseBtn_clicked();
void on_javaStatusBtn_clicked(); void on_javaStatusBtn_clicked();
void on_javaDownloadBtn_clicked(); void on_javaDownloadBtn_clicked();
void checkFinished(JavaCheckResult result); void checkFinished(JavaChecker::Result result);
protected: /* methods */ protected: /* methods */
void checkJavaPathOnEdit(const QString& path); void checkJavaPathOnEdit(const QString& path);
@ -89,5 +89,5 @@ class JavaSettingsWidget : public QWidget {
QString queuedCheck; QString queuedCheck;
uint64_t m_availableMemory = 0ull; uint64_t m_availableMemory = 0ull;
shared_qobject_ptr<JavaChecker> m_checker; shared_qobject_ptr<JavaChecker> m_checker;
JavaCheckResult m_result; JavaChecker::Result m_result;
}; };