diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 498427856..6b7333972 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -633,6 +633,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); m_settings->registerSetting("JavaExtraSearchPaths", QStringList()); + m_settings->registerSetting("AutomaticJavaSwitch", false); + m_settings->registerSetting("AutomaticJavaDownload", false); // Legacy settings m_settings->registerSetting("OnlineFixes", false); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index c7945d1b8..fcc3c2f3a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -281,6 +281,8 @@ set(MINECRAFT_SOURCES minecraft/launch/ScanModFolders.h minecraft/launch/VerifyJavaInstall.cpp minecraft/launch/VerifyJavaInstall.h + minecraft/launch/AutoInstallJava.cpp + minecraft/launch/AutoInstallJava.h minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4229f73eb..00ce405aa 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -38,6 +38,8 @@ #include "MinecraftInstance.h" #include "Application.h" #include "BuildConfig.h" +#include "QObjectPtr.h" +#include "minecraft/launch/AutoInstallJava.h" #include "minecraft/launch/CreateGameFolders.h" #include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/PrintInstanceInfo.h" @@ -1041,11 +1043,6 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(makeShared(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher)); } - // check java - { - process->appendStep(makeShared(pptr)); - } - // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) { process->appendStep(makeShared(pptr)); @@ -1105,6 +1102,11 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt { process->appendStep(makeShared(pptr)); } + // check java + { + process->appendStep(makeShared(pptr)); + process->appendStep(makeShared(pptr)); + } // verify that minimum Java requirements are met { diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp new file mode 100644 index 000000000..0d3740c93 --- /dev/null +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AutoInstallJava.h" +#include +#include +#include + +#include "Application.h" +#include "FileSystem.h" +#include "MessageLevel.h" +#include "SysInfo.h" +#include "java/JavaInstall.h" +#include "java/JavaInstallList.h" +#include "java/JavaVersion.h" +#include "java/download/ArchiveDownloadTask.h" +#include "java/download/ManifestDownloadTask.h" +#include "meta/Index.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" +#include "net/Mode.h" + +AutoInstallJava::AutoInstallJava(LaunchTask* parent) + : LaunchStep(parent) + , m_instance(std::dynamic_pointer_cast(m_parent->instance())) + , m_supported_arch(SysInfo::getSupportedJavaArchitecture()){}; + +void AutoInstallJava::executeTask() +{ + auto settings = m_instance->settings(); + if (!APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() || + (settings->get("OverrideJava").toBool() && settings->get("OverrideJavaLocation").toBool())) { + emitSucceeded(); + return; + } + auto packProfile = m_instance->getPackProfile(); + if (!APPLICATION->settings()->get("AutomaticJavaDownload").toBool()) { + auto javas = APPLICATION->javalist().get(); + m_current_task = javas->getLoadTask(); + connect(m_current_task.get(), &Task::finished, this, [this, javas, packProfile] { + for (auto i = 0; i < javas->count(); i++) { + auto java = std::dynamic_pointer_cast(javas->at(i)); + if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) { + setJavaPath(java->path); + return; + } + } + emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emitSucceeded(); + }); + return; + } + auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); + QDir javaDir(APPLICATION->javaPath()); + auto wantedJavaPath = javaDir.absoluteFilePath(wantedJavaName); + if (QFileInfo::exists(wantedJavaPath)) { + setJavaPathFromPartial(); + return; + } + auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); + m_current_task = versionList->getLoadTask(); + connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::tryNextMajorJava); + connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::emitFailed); +} + +void AutoInstallJava::setJavaPath(QString path) +{ + auto settings = m_instance->settings(); + settings->set("OverrideJava", true); + settings->set("OverrideJavaLocation", true); + settings->set("JavaPath", path); + emit logLine(tr("Compatible java found at: %1.").arg(path), MessageLevel::Info); + emitSucceeded(); +} + +void AutoInstallJava::setJavaPathFromPartial() +{ + QString executable = "java"; +#if defined(Q_OS_WIN32) + executable += "w.exe"; +#endif + auto packProfile = m_instance->getPackProfile(); + auto javaName = packProfile->getProfile()->getCompatibleJavaName(); + QDir javaDir(APPLICATION->javaPath()); + // just checking if the executable is there should suffice + // but if needed this can be achieved through refreshing the javalist + // and retrieving the path that contains the java name + auto relativeBinary = FS::PathCombine(javaName, "bin", executable); + auto finalPath = javaDir.absoluteFilePath(relativeBinary); + if (QFileInfo::exists(finalPath)) { + setJavaPath(finalPath); + } else { + emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emitSucceeded(); + } + return; +} + +void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName) +{ + auto runtimes = version->data()->runtimes; + if (runtimes.contains(m_supported_arch)) { + for (auto java : runtimes.value(m_supported_arch)) { + if (java->name() == javaName) { + Task::Ptr task; + QDir javaDir(APPLICATION->javaPath()); + auto final_path = javaDir.absoluteFilePath(java->m_name); + switch (java->downloadType) { + case Java::DownloadType::Manifest: + task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + break; + case Java::DownloadType::Archive: + task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); + break; + } + QEventLoop loop; + auto deletePath = [final_path] { FS::deletePath(final_path); }; + connect(task.get(), &Task::failed, this, [this, deletePath](QString reason) { + deletePath(); + emitFailed(reason); + }); + connect(this, &Task::aborted, this, [task, deletePath] { + task->abort(); + deletePath(); + }); + connect(task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); + task->start(); + return; + } + } + } + tryNextMajorJava(); +} + +void AutoInstallJava::tryNextMajorJava() +{ + if (!isRunning()) + return; + auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); + auto packProfile = m_instance->getPackProfile(); + auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); + auto majorJavaVersions = packProfile->getProfile()->getCompatibleJavaMajors(); + if (m_majorJavaVersionIndex >= majorJavaVersions.length()) { + emit logLine(tr("No comptatible java version was found. Using the default one."), MessageLevel::Warning); + emitSucceeded(); + return; + } + auto majorJavaVersion = majorJavaVersions[m_majorJavaVersionIndex]; + m_majorJavaVersionIndex++; + + auto javaMajor = versionList->getVersion(QString("java%1").arg(majorJavaVersion)); + javaMajor->load(Net::Mode::Online); + auto task = javaMajor->getCurrentTask(); + if (javaMajor->isLoaded() || !task) { + downloadJava(javaMajor, wantedJavaName); + } else { + connect(task.get(), &Task::succeeded, this, [this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); }); + connect(task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); + } +} diff --git a/launcher/minecraft/launch/AutoInstallJava.h b/launcher/minecraft/launch/AutoInstallJava.h new file mode 100644 index 000000000..4dcd3796a --- /dev/null +++ b/launcher/minecraft/launch/AutoInstallJava.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include "java/JavaMetadata.h" +#include "meta/Version.h" +#include "minecraft/MinecraftInstance.h" +#include "tasks/Task.h" + +class AutoInstallJava : public LaunchStep { + Q_OBJECT + + public: + explicit AutoInstallJava(LaunchTask* parent); + ~AutoInstallJava() override = default; + + void executeTask() override; + bool canAbort() const override { return m_current_task ? m_current_task->canAbort() : false; } + + protected: + void setJavaPath(QString path); + void setJavaPathFromPartial(); + void downloadJava(Meta::Version::Ptr version, QString javaName); + void tryNextMajorJava(); + + private: + MinecraftInstancePtr m_instance; + Task::Ptr m_current_task; + + qsizetype m_majorJavaVersionIndex = 0; + const QString m_supported_arch; +}; diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index 218811290..279545fed 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -50,7 +50,6 @@ void VerifyJavaInstall::executeTask() auto settings = instance->settings(); auto storedVersion = settings->get("JavaVersion").toString(); auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool(); - auto automaticJavaSwitch = settings->get("AutomaticJavaSwitch").toBool(); auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors(); @@ -67,38 +66,16 @@ void VerifyJavaInstall::executeTask() return; } - auto logFail = [this, &javaVersion, compatibleMajors] { - emit logLine(tr("This instance is not compatible with Java version %1.\n" - "Please switch to one of the following Java versions for this instance:") - .arg(javaVersion.major()), - MessageLevel::Error); - for (auto major : compatibleMajors) { - emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); - } - emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " - "you're doing."), - MessageLevel::Error); - - emitFailed(QString("Incompatible Java major version")); - }; - - if (automaticJavaSwitch || true) { - settings->set("OverrideJava", true); - auto javas = APPLICATION->javalist().get(); - auto task = javas->getLoadTask(); - connect(task.get(), &Task::finished, this, [this, javas, compatibleMajors, settings, &logFail] { - for (auto i = 0; i < javas->count(); i++) { - auto java = std::dynamic_pointer_cast(javas->at(i)); - if (java && compatibleMajors.contains(java->id.major())) { - settings->set("OverrideJavaLocation", true); - settings->set("JavaPath", java->path); - emitSucceeded(); - return; - } - } - logFail(); - }); - } else { - logFail(); + emit logLine(tr("This instance is not compatible with Java version %1.\n" + "Please switch to one of the following Java versions for this instance:") + .arg(javaVersion.major()), + MessageLevel::Error); + for (auto major : compatibleMajors) { + emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); } + emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " + "you're doing."), + MessageLevel::Error); + + emitFailed(QString("Incompatible Java major version")); } diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 9b2aa7637..496cb69a7 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -102,6 +102,8 @@ void JavaPage::applySettings() s->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " ")); s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked()); s->set("IgnoreJavaWizard", ui->skipJavaWizardCheckbox->isChecked()); + s->set("AutomaticJavaSwitch", ui->autodetectJavaCheckBox->isChecked()); + s->set("AutomaticJavaDownload", ui->autodownloadCheckBox->isChecked()); s->set("JavaExtraSearchPaths", m_extra_paths->stringList()); JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget()); } @@ -125,6 +127,8 @@ void JavaPage::loadSettings() ui->jvmArgsTextBox->setPlainText(s->get("JvmArgs").toString()); ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool()); ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); + ui->autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool()); + ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaDownload").toBool()); m_extra_paths = new QStringListModel(s->get("JavaExtraSearchPaths").toStringList()); ui->extraJavaPathsList->setModel(m_extra_paths); } diff --git a/launcher/ui/pages/global/JavaPage.ui b/launcher/ui/pages/global/JavaPage.ui index a518b0b14..d135adc9a 100644 --- a/launcher/ui/pages/global/JavaPage.ui +++ b/launcher/ui/pages/global/JavaPage.ui @@ -160,25 +160,6 @@ Java Runtime - - - - true - - - - 0 - 0 - - - - - 16777215 - 100 - - - - @@ -232,7 +213,7 @@ - + @@ -248,6 +229,42 @@ + + + + If enabled, the launcher will not prompt you to choose a Java version if one isn't found. + + + Skip Java &Wizard + + + + + + + true + + + + 0 + 0 + + + + + 16777215 + 100 + + + + + + + + Autodetect Java version + + + @@ -284,13 +301,10 @@ - - - - If enabled, the launcher will not prompt you to choose a Java version if one isn't found. - + + - Skip Java &Wizard + Autodownload Mojang Java