Merge pull request #2069 from Trial97/feature/java-downloader

Feature/java downloader
This commit is contained in:
Alexandru Ionut Tripon 2024-08-23 08:17:15 +03:00 committed by GitHub
commit eae5d70385
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 3010 additions and 431 deletions

View File

@ -266,23 +266,23 @@ jobs:
- name: Configure CMake (macOS) - name: Configure CMake (macOS)
if: runner.os == 'macOS' && matrix.qt_ver == 6 if: runner.os == 'macOS' && matrix.qt_ver == 6
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
- name: Configure CMake (macOS-Legacy) - name: Configure CMake (macOS-Legacy)
if: runner.os == 'macOS' && matrix.qt_ver == 5 if: runner.os == 'macOS' && matrix.qt_ver == 5
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
- name: Configure CMake (Windows MinGW-w64) - name: Configure CMake (Windows MinGW-w64)
if: runner.os == 'Windows' && matrix.msystem != '' if: runner.os == 'Windows' && matrix.msystem != ''
shell: msys2 {0} shell: msys2 {0}
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
- name: Configure CMake (Windows MSVC) - name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == '' if: runner.os == 'Windows' && matrix.msystem == ''
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}") if ("${{ env.CCACHE_VAR }}")
{ {
@ -297,7 +297,7 @@ jobs:
- name: Configure CMake (Linux) - name: Configure CMake (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
## ##
# BUILD # BUILD

View File

@ -219,6 +219,19 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules") set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
# Java downloader
set(ENABLE_JAVA_DOWNLOADER_DEFAULT ON)
# Although we recommend enabling this, we cannot guarantee binary compatibility on
# differing Linux/BSD/etc distributions. Downstream packagers should be explicitly opt-ing into this
# feature if they know it will work with their distribution.
if(UNIX AND NOT APPLE)
set(ENABLE_JAVA_DOWNLOADER_DEFAULT OFF)
endif()
# Java downloader
option(ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ${ENABLE_JAVA_DOWNLOADER_DEFAULT})
# Native libraries # Native libraries
if(UNIX AND APPLE) if(UNIX AND APPLE)
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library") set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")

View File

@ -81,6 +81,9 @@ Config::Config()
UPDATER_ENABLED = true; UPDATER_ENABLED = true;
} }
#cmakedefine01 ENABLE_JAVA_DOWNLOADER
JAVA_DOWNLOADER_ENABLED = ENABLE_JAVA_DOWNLOADER;
GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_COMMIT = "@Launcher_GIT_COMMIT@";
GIT_TAG = "@Launcher_GIT_TAG@"; GIT_TAG = "@Launcher_GIT_TAG@";
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@"; GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";

View File

@ -67,6 +67,7 @@ class Config {
QString VERSION_CHANNEL; QString VERSION_CHANNEL;
bool UPDATER_ENABLED = false; bool UPDATER_ENABLED = false;
bool JAVA_DOWNLOADER_ENABLED = false;
/// A short string identifying this build's platform or distribution. /// A short string identifying this build's platform or distribution.
QString BUILD_PLATFORM; QString BUILD_PLATFORM;

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>
@ -602,6 +604,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
m_settings->registerSetting("DownloadsDirWatchRecursive", false); m_settings->registerSetting("DownloadsDirWatchRecursive", false);
m_settings->registerSetting("SkinsDir", "skins"); m_settings->registerSetting("SkinsDir", "skins");
m_settings->registerSetting("JavaDir", "java");
// Editors // Editors
m_settings->registerSetting("JsonEditor", QString()); m_settings->registerSetting("JsonEditor", QString());
@ -630,7 +633,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
@ -644,6 +647,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("JvmArgs", ""); m_settings->registerSetting("JvmArgs", "");
m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaCompatibility", false);
m_settings->registerSetting("IgnoreJavaWizard", false); m_settings->registerSetting("IgnoreJavaWizard", false);
auto defaultEnableAutoJava = m_settings->get("JavaPath").toString().isEmpty();
m_settings->registerSetting("AutomaticJavaSwitch", defaultEnableAutoJava);
m_settings->registerSetting("AutomaticJavaDownload", defaultEnableAutoJava);
// Legacy settings // Legacy settings
m_settings->registerSetting("OnlineFixes", false); m_settings->registerSetting("OnlineFixes", false);
@ -873,6 +879,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath()); m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath()); m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->addBase("java", QDir("cache/java").absolutePath());
m_metacache->Load(); m_metacache->Load();
qDebug() << "<> Cache initialized."; qDebug() << "<> Cache initialized.";
} }
@ -1740,20 +1747,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,
@ -1860,3 +1853,7 @@ QUrl Application::normalizeImportUrl(QString const& url)
return QUrl::fromUserInput(url); return QUrl::fromUserInput(url);
} }
} }
const QString Application::javaPath()
{
return m_settings->get("JavaDir").toString();
}

View File

@ -161,6 +161,9 @@ class Application : public QApplication {
/// the data path the application is using /// the data path the application is using
const QString& dataRoot() { return m_dataPath; } const QString& dataRoot() { return m_dataPath; }
/// the java installed path the application is using
const QString javaPath();
bool isPortable() { return m_portable; } bool isPortable() { return m_portable; }
const Capabilities capabilities() { return m_capabilities; } const Capabilities capabilities() { return m_capabilities; }
@ -179,8 +182,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

@ -78,6 +78,14 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const
case TypeRole: case TypeRole:
return version->typeString(); return version->typeString();
case JavaMajorRole: {
auto major = version->name();
if (major.startsWith("java")) {
major = "Java " + major.mid(4);
}
return major;
}
default: default:
return QVariant(); return QVariant();
} }
@ -110,6 +118,8 @@ QHash<int, QByteArray> BaseVersionList::roleNames() const
roles.insert(TypeRole, "type"); roles.insert(TypeRole, "type");
roles.insert(BranchRole, "branch"); roles.insert(BranchRole, "branch");
roles.insert(PathRole, "path"); roles.insert(PathRole, "path");
roles.insert(ArchitectureRole, "architecture"); roles.insert(JavaNameRole, "javaName");
roles.insert(CPUArchitectureRole, "architecture");
roles.insert(JavaMajorRole, "javaMajor");
return roles; return roles;
} }

View File

@ -48,7 +48,9 @@ class BaseVersionList : public QAbstractListModel {
TypeRole, TypeRole,
BranchRole, BranchRole,
PathRole, PathRole,
ArchitectureRole, JavaNameRole,
JavaMajorRole,
CPUArchitectureRole,
SortRole SortRole
}; };
using RoleList = QList<int>; using RoleList = QList<int>;

View File

@ -24,6 +24,8 @@ set(CORE_SOURCES
NullInstance.h NullInstance.h
MMCZip.h MMCZip.h
MMCZip.cpp MMCZip.cpp
Untar.h
Untar.cpp
StringUtils.h StringUtils.h
StringUtils.cpp StringUtils.cpp
QVariantUtils.h QVariantUtils.h
@ -272,6 +274,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/ScanModFolders.h minecraft/launch/ScanModFolders.h
minecraft/launch/VerifyJavaInstall.cpp minecraft/launch/VerifyJavaInstall.cpp
minecraft/launch/VerifyJavaInstall.h minecraft/launch/VerifyJavaInstall.h
minecraft/launch/AutoInstallJava.cpp
minecraft/launch/AutoInstallJava.h
minecraft/GradleSpecifier.h minecraft/GradleSpecifier.h
minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.cpp
@ -417,8 +421,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
@ -427,6 +429,18 @@ set(JAVA_SOURCES
java/JavaUtils.cpp java/JavaUtils.cpp
java/JavaVersion.h java/JavaVersion.h
java/JavaVersion.cpp java/JavaVersion.cpp
java/JavaMetadata.h
java/JavaMetadata.cpp
java/download/ArchiveDownloadTask.cpp
java/download/ArchiveDownloadTask.h
java/download/ManifestDownloadTask.cpp
java/download/ManifestDownloadTask.h
ui/java/InstallJavaDialog.h
ui/java/InstallJavaDialog.cpp
ui/java/VersionList.h
ui/java/VersionList.cpp
) )
set(TRANSLATIONS_SOURCES set(TRANSLATIONS_SOURCES
@ -746,6 +760,8 @@ SET(LAUNCHER_SOURCES
DataMigrationTask.cpp DataMigrationTask.cpp
ApplicationMessage.h ApplicationMessage.h
ApplicationMessage.cpp ApplicationMessage.cpp
SysInfo.h
SysInfo.cpp
# GUI - general utilities # GUI - general utilities
DesktopServices.h DesktopServices.h

View File

@ -276,6 +276,9 @@ bool ensureFolderPathExists(const QFileInfo folderPath)
{ {
QDir dir; QDir dir;
QString ensuredPath = folderPath.filePath(); QString ensuredPath = folderPath.filePath();
if (folderPath.exists())
return true;
bool success = dir.mkpath(ensuredPath); bool success = dir.mkpath(ensuredPath);
return success; return success;
} }

View File

@ -1,16 +1,12 @@
#include "Filter.h" #include "Filter.h"
Filter::~Filter() {}
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {} ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
ContainsFilter::~ContainsFilter() {}
bool ContainsFilter::accepts(const QString& value) bool ContainsFilter::accepts(const QString& value)
{ {
return value.contains(pattern); return value.contains(pattern);
} }
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {} ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
ExactFilter::~ExactFilter() {}
bool ExactFilter::accepts(const QString& value) bool ExactFilter::accepts(const QString& value)
{ {
return value == pattern; return value == pattern;
@ -27,10 +23,15 @@ RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
pattern.setPattern(regexp); pattern.setPattern(regexp);
pattern.optimize(); pattern.optimize();
} }
RegexpFilter::~RegexpFilter() {}
bool RegexpFilter::accepts(const QString& value) bool RegexpFilter::accepts(const QString& value)
{ {
auto match = pattern.match(value); auto match = pattern.match(value);
bool matched = match.hasMatch(); bool matched = match.hasMatch();
return invert ? (!matched) : (matched); return invert ? (!matched) : (matched);
} }
ExactListFilter::ExactListFilter(const QStringList& pattern) : m_pattern(pattern) {}
bool ExactListFilter::accepts(const QString& value)
{
return m_pattern.isEmpty() || m_pattern.contains(value);
}

View File

@ -5,14 +5,14 @@
class Filter { class Filter {
public: public:
virtual ~Filter(); virtual ~Filter() = default;
virtual bool accepts(const QString& value) = 0; virtual bool accepts(const QString& value) = 0;
}; };
class ContainsFilter : public Filter { class ContainsFilter : public Filter {
public: public:
ContainsFilter(const QString& pattern); ContainsFilter(const QString& pattern);
virtual ~ContainsFilter(); virtual ~ContainsFilter() = default;
bool accepts(const QString& value) override; bool accepts(const QString& value) override;
private: private:
@ -22,7 +22,7 @@ class ContainsFilter : public Filter {
class ExactFilter : public Filter { class ExactFilter : public Filter {
public: public:
ExactFilter(const QString& pattern); ExactFilter(const QString& pattern);
virtual ~ExactFilter(); virtual ~ExactFilter() = default;
bool accepts(const QString& value) override; bool accepts(const QString& value) override;
private: private:
@ -32,7 +32,7 @@ class ExactFilter : public Filter {
class ExactIfPresentFilter : public Filter { class ExactIfPresentFilter : public Filter {
public: public:
ExactIfPresentFilter(const QString& pattern); ExactIfPresentFilter(const QString& pattern);
~ExactIfPresentFilter() override = default; virtual ~ExactIfPresentFilter() override = default;
bool accepts(const QString& value) override; bool accepts(const QString& value) override;
private: private:
@ -42,10 +42,20 @@ class ExactIfPresentFilter : public Filter {
class RegexpFilter : public Filter { class RegexpFilter : public Filter {
public: public:
RegexpFilter(const QString& regexp, bool invert); RegexpFilter(const QString& regexp, bool invert);
virtual ~RegexpFilter(); virtual ~RegexpFilter() = default;
bool accepts(const QString& value) override; bool accepts(const QString& value) override;
private: private:
QRegularExpression pattern; QRegularExpression pattern;
bool invert = false; bool invert = false;
}; };
class ExactListFilter : public Filter {
public:
ExactListFilter(const QStringList& pattern = {});
virtual ~ExactListFilter() = default;
bool accepts(const QString& value) override;
private:
const QStringList& m_pattern;
};

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(const 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(const 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(const JavaChecker::Result& result);
void checkFinishedWithArgs(JavaCheckResult result); void checkFinishedWithArgs(const 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

@ -2,7 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> * Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -536,6 +536,10 @@ bool ExportToZipTask::abort()
void ExtractZipTask::executeTask() void ExtractZipTask::executeTask()
{ {
if (!m_input->isOpen() && !m_input->open(QuaZip::mdUnzip)) {
emitFailed(tr("Unable to open supplied zip file."));
return;
}
m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); }); m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); });
connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExtractZipTask::finish); connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExtractZipTask::finish);
m_zip_watcher.setFuture(m_zip_future); m_zip_watcher.setFuture(m_zip_future);

View File

@ -2,7 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> * Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -208,6 +208,9 @@ class ExportToZipTask : public Task {
class ExtractZipTask : public Task { class ExtractZipTask : public Task {
public: 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 = "") ExtractZipTask(std::shared_ptr<QuaZip> input, QDir outputDir, QString subdirectory = "")
: m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory) : m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory)
{} {}

99
launcher/SysInfo.cpp Normal file
View File

@ -0,0 +1,99 @@
#include <QDebug>
#include <QString>
#include "sys.h"
#ifdef Q_OS_MACOS
#include <sys/sysctl.h>
#endif
#include <QFile>
#include <QMap>
#include <QProcess>
#include <QStandardPaths>
#ifdef Q_OS_MACOS
bool rosettaDetect()
{
int ret = 0;
size_t size = sizeof(ret);
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
return false;
}
return ret == 1;
}
#endif
namespace SysInfo {
QString currentSystem()
{
#if defined(Q_OS_LINUX)
return "linux";
#elif defined(Q_OS_MACOS)
return "osx";
#elif defined(Q_OS_WINDOWS)
return "windows";
#elif defined(Q_OS_FREEBSD)
return "freebsd";
#elif defined(Q_OS_OPENBSD)
return "openbsd";
#else
return "unknown";
#endif
}
QString useQTForArch()
{
#if defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM)
if (rosettaDetect()) {
return "arm64";
} else {
return "x86_64";
}
#endif
return QSysInfo::currentCpuArchitecture();
}
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;
}
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

8
launcher/SysInfo.h Normal file
View File

@ -0,0 +1,8 @@
#include <QString>
namespace SysInfo {
QString currentSystem();
QString useQTForArch();
QString getSupportedJavaArchitecture();
int suitableMaxMem();
} // namespace SysInfo

260
launcher/Untar.cpp Normal file
View File

@ -0,0 +1,260 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Untar.h"
#include <quagzipfile.h>
#include <QByteArray>
#include <QFileInfo>
#include <QIODevice>
#include <QString>
#include "FileSystem.h"
// adaptation of the:
// - https://github.com/madler/zlib/blob/develop/contrib/untgz/untgz.c
// - https://en.wikipedia.org/wiki/Tar_(computing)
// - https://github.com/euroelessar/cutereader/blob/master/karchive/src/ktar.cpp
#define BLOCKSIZE 512
#define SHORTNAMESIZE 100
enum class TypeFlag : char {
Regular = '0', // regular file
ARegular = 0, // regular file
Link = '1', // link
Symlink = '2', // reserved
Character = '3', // character special
Block = '4', // block special
Directory = '5', // directory
FIFO = '6', // FIFO special
Contiguous = '7', // reserved
// Posix stuff
GlobalPosixHeader = 'g',
ExtendedPosixHeader = 'x',
// 'A' 'Z' Vendor specific extensions(POSIX .1 - 1988)
// GNU
GNULongLink = 'K', /* long link name */
GNULongName = 'L', /* long file name */
};
// struct Header { /* byte offset */
// char name[100]; /* 0 */
// char mode[8]; /* 100 */
// char uid[8]; /* 108 */
// char gid[8]; /* 116 */
// char size[12]; /* 124 */
// char mtime[12]; /* 136 */
// char chksum[8]; /* 148 */
// TypeFlag typeflag; /* 156 */
// char linkname[100]; /* 157 */
// char magic[6]; /* 257 */
// char version[2]; /* 263 */
// char uname[32]; /* 265 */
// char gname[32]; /* 297 */
// char devmajor[8]; /* 329 */
// char devminor[8]; /* 337 */
// char prefix[155]; /* 345 */
// /* 500 */
// };
bool readLonglink(QIODevice* in, qint64 size, QByteArray& longlink)
{
qint64 n = 0;
size--; // ignore trailing null
if (size < 0) {
qCritical() << "The filename size is negative";
return false;
}
longlink.resize(size + (BLOCKSIZE - size % BLOCKSIZE)); // make the size divisible by BLOCKSIZE
for (qint64 offset = 0; offset < longlink.size(); offset += BLOCKSIZE) {
n = in->read(longlink.data() + offset, BLOCKSIZE);
if (n != BLOCKSIZE) {
qCritical() << "The expected blocksize was not respected for the name";
return false;
}
}
longlink.truncate(qstrlen(longlink.constData()));
return true;
}
int getOctal(char* buffer, int maxlenght, bool* ok)
{
return QByteArray(buffer, qstrnlen(buffer, maxlenght)).toInt(ok, 8);
}
QString decodeName(char* name)
{
return QFile::decodeName(QByteArray(name, qstrnlen(name, 100)));
}
bool Tar::extract(QIODevice* in, QString dst)
{
char buffer[BLOCKSIZE];
QString name, symlink, firstFolderName;
bool doNotReset = false, ok;
while (true) {
auto n = in->read(buffer, BLOCKSIZE);
if (n != BLOCKSIZE) { // allways expect complete blocks
qCritical() << "The expected blocksize was not respected";
return false;
}
if (buffer[0] == 0) { // end of archive
return true;
}
int mode = getOctal(buffer + 100, 8, &ok) | QFile::ReadUser | QFile::WriteUser; // hack to ensure write and read permisions
if (!ok) {
qCritical() << "The file mode can't be read";
return false;
}
// there are names that are exactly 100 bytes long
// and neither longlink nor \0 terminated (bug:101472)
if (name.isEmpty()) {
name = decodeName(buffer);
if (!firstFolderName.isEmpty() && name.startsWith(firstFolderName)) {
name = name.mid(firstFolderName.size());
}
}
if (symlink.isEmpty())
symlink = decodeName(buffer);
qint64 size = getOctal(buffer + 124, 12, &ok);
if (!ok) {
qCritical() << "The file size can't be read";
return false;
}
switch (TypeFlag(buffer[156])) {
case TypeFlag::Regular:
/* fallthrough */
case TypeFlag::ARegular: {
auto fileName = FS::PathCombine(dst, name);
if (!FS::ensureFilePathExists(fileName)) {
qCritical() << "Can't ensure the file path to exist: " << fileName;
return false;
}
QFile out(fileName);
if (!out.open(QFile::WriteOnly)) {
qCritical() << "Can't open file:" << fileName;
return false;
}
out.setPermissions(QFile::Permissions(mode));
while (size > 0) {
QByteArray tmp(BLOCKSIZE, 0);
n = in->read(tmp.data(), BLOCKSIZE);
if (n != BLOCKSIZE) {
qCritical() << "The expected blocksize was not respected when reading file";
return false;
}
tmp.truncate(qMin(qint64(BLOCKSIZE), size));
out.write(tmp);
size -= BLOCKSIZE;
}
break;
}
case TypeFlag::Directory: {
if (firstFolderName.isEmpty()) {
firstFolderName = name;
break;
}
auto folderPath = FS::PathCombine(dst, name);
if (!FS::ensureFolderPathExists(folderPath)) {
qCritical() << "Can't ensure that folder exists: " << folderPath;
return false;
}
break;
}
case TypeFlag::GNULongLink: {
doNotReset = true;
QByteArray longlink;
if (readLonglink(in, size, longlink)) {
symlink = QFile::decodeName(longlink.constData());
} else {
qCritical() << "Failed to read long link";
return false;
}
break;
}
case TypeFlag::GNULongName: {
doNotReset = true;
QByteArray longlink;
if (readLonglink(in, size, longlink)) {
name = QFile::decodeName(longlink.constData());
} else {
qCritical() << "Failed to read long name";
return false;
}
break;
}
case TypeFlag::Link:
/* fallthrough */
case TypeFlag::Symlink: {
auto fileName = FS::PathCombine(dst, name);
if (!FS::create_link(FS::PathCombine(QFileInfo(fileName).path(), symlink), fileName)()) { // do not use symlinks
qCritical() << "Can't create link for:" << fileName << " to:" << FS::PathCombine(QFileInfo(fileName).path(), symlink);
return false;
}
FS::ensureFilePathExists(fileName);
QFile::setPermissions(fileName, QFile::Permissions(mode));
break;
}
case TypeFlag::Character:
/* fallthrough */
case TypeFlag::Block:
/* fallthrough */
case TypeFlag::FIFO:
/* fallthrough */
case TypeFlag::Contiguous:
/* fallthrough */
case TypeFlag::GlobalPosixHeader:
/* fallthrough */
case TypeFlag::ExtendedPosixHeader:
/* fallthrough */
default:
break;
}
if (!doNotReset) {
name.truncate(0);
symlink.truncate(0);
}
doNotReset = false;
}
return true;
}
bool GZTar::extract(QString src, QString dst)
{
QuaGzipFile a(src);
if (!a.open(QIODevice::ReadOnly)) {
qCritical() << "Can't open tar file:" << src;
return false;
}
return Tar::extract(&a, dst);
}

46
launcher/Untar.h Normal file
View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/>.
*
* 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 <QIODevice>
// this is a hack used for the java downloader (feel free to remove it in favor of a library)
// both extract functions will extract the first folder inside dest(disregarding the prefix)
namespace Tar {
bool extract(QIODevice* in, QString dst);
}
namespace GZTar {
bool extract(QString src, QString dst);
}

View File

@ -114,10 +114,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
return tr("Branch"); return tr("Branch");
case Type: case Type:
return tr("Type"); return tr("Type");
case Architecture: case CPUArchitecture:
return tr("Architecture"); return tr("Architecture");
case Path: case Path:
return tr("Path"); return tr("Path");
case JavaName:
return tr("Java Name");
case JavaMajor:
return tr("Major Version");
case Time: case Time:
return tr("Released"); return tr("Released");
} }
@ -131,10 +135,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
return tr("The version's branch"); return tr("The version's branch");
case Type: case Type:
return tr("The version's type"); return tr("The version's type");
case Architecture: case CPUArchitecture:
return tr("CPU Architecture"); return tr("CPU Architecture");
case Path: case Path:
return tr("Filesystem path to this version"); return tr("Filesystem path to this version");
case JavaName:
return tr("The alternative name of the java version");
case JavaMajor:
return tr("The java major version");
case Time: case Time:
return tr("Release date of this version"); return tr("Release date of this version");
} }
@ -165,10 +173,14 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole); return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
case Type: case Type:
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole); return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
case Architecture: case CPUArchitecture:
return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole); return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole);
case Path: case Path:
return sourceModel()->data(parentIndex, BaseVersionList::PathRole); return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
case JavaName:
return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole);
case JavaMajor:
return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole);
case Time: case Time:
return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate(); return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate();
default: default:
@ -308,12 +320,18 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
m_columns.push_back(ParentVersion); m_columns.push_back(ParentVersion);
} }
*/ */
if (roles.contains(BaseVersionList::ArchitectureRole)) { if (roles.contains(BaseVersionList::CPUArchitectureRole)) {
m_columns.push_back(Architecture); m_columns.push_back(CPUArchitecture);
} }
if (roles.contains(BaseVersionList::PathRole)) { if (roles.contains(BaseVersionList::PathRole)) {
m_columns.push_back(Path); m_columns.push_back(Path);
} }
if (roles.contains(BaseVersionList::JavaNameRole)) {
m_columns.push_back(JavaName);
}
if (roles.contains(BaseVersionList::JavaMajorRole)) {
m_columns.push_back(JavaMajor);
}
if (roles.contains(Meta::VersionList::TimeRole)) { if (roles.contains(Meta::VersionList::TimeRole)) {
m_columns.push_back(Time); m_columns.push_back(Time);
} }

View File

@ -9,7 +9,7 @@ class VersionFilterModel;
class VersionProxyModel : public QAbstractProxyModel { class VersionProxyModel : public QAbstractProxyModel {
Q_OBJECT Q_OBJECT
public: public:
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time }; enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor };
using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>; using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
public: public:

View File

@ -40,14 +40,15 @@
#include <QMap> #include <QMap>
#include <QProcess> #include <QProcess>
#include "Application.h"
#include "Commandline.h" #include "Commandline.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "JavaUtils.h" #include "java/JavaUtils.h"
JavaChecker::JavaChecker(QObject* parent) : QObject(parent) {} JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id, QObject* parent)
: Task(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id)
{}
void JavaChecker::performCheck() void JavaChecker::executeTask()
{ {
QString checkerJar = JavaUtils::getJavaCheckPath(); QString checkerJar = JavaUtils::getJavaCheckPath();
@ -72,7 +73,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);
} }
@ -115,11 +116,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;
@ -127,8 +127,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;
} }
@ -161,8 +162,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;
} }
@ -171,7 +173,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;
@ -179,6 +181,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)
@ -190,15 +193,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,14 +3,19 @@
#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 {
Q_OBJECT
public:
using QProcessPtr = shared_qobject_ptr<QProcess>;
using Ptr = shared_qobject_ptr<JavaChecker>;
struct JavaCheckResult { struct Result {
QString path; QString path;
int id;
QString mojangPlatform; QString mojangPlatform;
QString realPlatform; QString realPlatform;
JavaVersion javaVersion; JavaVersion javaVersion;
@ -18,34 +23,31 @@ struct JavaCheckResult {
QString outLog; QString outLog;
QString errorLog; QString errorLog;
bool is_64bit = false; bool is_64bit = false;
int id;
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
}; };
using QProcessPtr = shared_qobject_ptr<QProcess>; explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0, QObject* parent = 0);
using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
class JavaChecker : public QObject {
Q_OBJECT
public:
explicit JavaChecker(QObject* parent = 0);
void performCheck();
QString m_path;
QString m_args;
int m_id = 0;
int m_minMem = 0;
int m_maxMem = 0;
int m_permGen = 64;
signals: signals:
void checkFinished(JavaCheckResult result); void checkFinished(const 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

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> * Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> * Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -40,6 +40,7 @@ struct JavaInstall : public BaseVersion {
QString arch; QString arch;
QString path; QString path;
bool recommended = false; bool recommended = false;
bool is_64bit = false;
}; };
using JavaInstallPtr = std::shared_ptr<JavaInstall>; using JavaInstallPtr = std::shared_ptr<JavaInstall>;

View File

@ -38,13 +38,17 @@
#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, bool onlyManagedVersions)
: BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions)
{}
Task::Ptr JavaInstallList::getLoadTask() Task::Ptr JavaInstallList::getLoadTask()
{ {
@ -55,7 +59,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 +68,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_only_managed_versions));
m_loadTask->start(); m_load_task->start();
} }
} }
@ -106,7 +110,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
return version->recommended; return version->recommended;
case PathRole: case PathRole:
return version->path; return version->path;
case ArchitectureRole: case CPUArchitectureRole:
return version->arch; return version->arch;
default: default:
return QVariant(); return QVariant();
@ -115,7 +119,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
BaseVersionList::RoleList JavaInstallList::providesRoles() const BaseVersionList::RoleList JavaInstallList::providesRoles() const
{ {
return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole }; return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, CPUArchitectureRole };
} }
void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions) void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
@ -129,7 +133,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)
@ -146,35 +150,30 @@ void JavaInstallList::sortVersions()
endResetModel(); endResetModel();
} }
JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task() JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions) : Task(), m_only_managed_versions(onlyManagedVersions)
{ {
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..."));
JavaUtils ju; JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths(); QList<QString> candidate_paths = m_only_managed_versions ? getPrismJavaBundle() : 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](const 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,16 +183,17 @@ 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(), [](const JavaChecker::Result& a, const 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;
javaVersion->arch = result.realPlatform; javaVersion->arch = result.realPlatform;
javaVersion->path = result.path; javaVersion->path = result.path;
javaVersion->is_64bit = result.is_64bit;
candidates.append(javaVersion); candidates.append(javaVersion);
qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path; qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;

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"
@ -33,7 +33,7 @@ class JavaInstallList : public BaseVersionList {
enum class Status { NotDone, InProgress, Done }; enum class Status { NotDone, InProgress, Done };
public: public:
explicit JavaInstallList(QObject* parent = 0); explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false);
[[nodiscard]] Task::Ptr getLoadTask() override; [[nodiscard]] Task::Ptr getLoadTask() override;
bool isLoaded() override; bool isLoaded() override;
@ -53,23 +53,27 @@ 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;
bool m_only_managed_versions;
}; };
class JavaListLoadTask : public Task { class JavaListLoadTask : public Task {
Q_OBJECT Q_OBJECT
public: public:
explicit JavaListLoadTask(JavaInstallList* vlist); explicit JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions = false);
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;
bool m_only_managed_versions;
}; };

View File

@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/JavaMetadata.h"
#include <memory>
#include "Json.h"
#include "StringUtils.h"
#include "java/JavaVersion.h"
#include "minecraft/ParseUtils.h"
namespace Java {
DownloadType parseDownloadType(QString javaDownload)
{
if (javaDownload == "manifest")
return DownloadType::Manifest;
else if (javaDownload == "archive")
return DownloadType::Archive;
else
return DownloadType::Unknown;
}
QString downloadTypeToString(DownloadType javaDownload)
{
switch (javaDownload) {
case DownloadType::Manifest:
return "manifest";
case DownloadType::Archive:
return "archive";
case DownloadType::Unknown:
break;
}
return "unknown";
}
MetadataPtr parseJavaMeta(const QJsonObject& in)
{
auto meta = std::make_shared<Metadata>();
meta->m_name = Json::ensureString(in, "name", "");
meta->vendor = Json::ensureString(in, "vendor", "");
meta->url = Json::ensureString(in, "url", "");
meta->releaseTime = timeFromS3Time(Json::ensureString(in, "releaseTime", ""));
meta->downloadType = parseDownloadType(Json::ensureString(in, "downloadType", ""));
meta->packageType = Json::ensureString(in, "packageType", "");
meta->runtimeOS = Json::ensureString(in, "runtimeOS", "unknown");
if (in.contains("checksum")) {
auto obj = Json::requireObject(in, "checksum");
meta->checksumHash = Json::ensureString(obj, "hash", "");
meta->checksumType = Json::ensureString(obj, "type", "");
}
if (in.contains("version")) {
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);
auto security = Json::ensureInteger(obj, "security", 0);
auto build = Json::ensureInteger(obj, "build", 0);
meta->version = JavaVersion(major, minor, security, build, name);
}
return meta;
}
bool Metadata::operator<(const Metadata& rhs)
{
auto id = version;
if (id < rhs.version) {
return true;
}
if (id > rhs.version) {
return false;
}
auto date = releaseTime;
if (date < rhs.releaseTime) {
return true;
}
if (date > rhs.releaseTime) {
return false;
}
return StringUtils::naturalCompare(m_name, rhs.m_name, Qt::CaseInsensitive) < 0;
}
bool Metadata::operator==(const Metadata& rhs)
{
return version == rhs.version && m_name == rhs.m_name;
}
bool Metadata::operator>(const Metadata& rhs)
{
return (!operator<(rhs)) && (!operator==(rhs));
}
bool Metadata::operator<(BaseVersion& a)
{
try {
return operator<(dynamic_cast<Metadata&>(a));
} catch (const std::bad_cast& e) {
return BaseVersion::operator<(a);
}
}
bool Metadata::operator>(BaseVersion& a)
{
try {
return operator>(dynamic_cast<Metadata&>(a));
} catch (const std::bad_cast& e) {
return BaseVersion::operator>(a);
}
}
} // namespace Java

View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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 <QDateTime>
#include <QJsonObject>
#include <QString>
#include <memory>
#include "BaseVersion.h"
#include "java/JavaVersion.h"
namespace Java {
enum class DownloadType { Manifest, Archive, Unknown };
class Metadata : public BaseVersion {
public:
virtual QString descriptor() override { return version.toString(); }
virtual QString name() override { return m_name; }
virtual QString typeString() const override { return vendor; }
virtual bool operator<(BaseVersion& a) override;
virtual bool operator>(BaseVersion& a) override;
bool operator<(const Metadata& rhs);
bool operator==(const Metadata& rhs);
bool operator>(const Metadata& rhs);
QString m_name;
QString vendor;
QString url;
QDateTime releaseTime;
QString checksumType;
QString checksumHash;
DownloadType downloadType;
QString packageType;
JavaVersion version;
QString runtimeOS;
};
using MetadataPtr = std::shared_ptr<Metadata>;
DownloadType parseDownloadType(QString javaDownload);
QString downloadTypeToString(DownloadType javaDownload);
MetadataPtr parseJavaMeta(const QJsonObject& libObj);
} // namespace Java

View File

@ -347,6 +347,7 @@ QList<QString> JavaUtils::FindJavaPaths()
} }
candidates.append(getMinecraftJavaBundle()); candidates.append(getMinecraftJavaBundle());
candidates.append(getPrismJavaBundle());
candidates = addJavasFromEnv(candidates); candidates = addJavasFromEnv(candidates);
candidates.removeDuplicates(); candidates.removeDuplicates();
return candidates; return candidates;
@ -391,6 +392,7 @@ QList<QString> JavaUtils::FindJavaPaths()
} }
javas.append(getMinecraftJavaBundle()); javas.append(getMinecraftJavaBundle());
javas.append(getPrismJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -454,6 +456,7 @@ QList<QString> JavaUtils::FindJavaPaths()
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks")); scanJavaDirs(FS::PathCombine(home, ".gradle/jdks"));
javas.append(getMinecraftJavaBundle()); javas.append(getMinecraftJavaBundle());
javas.append(getPrismJavaBundle());
javas = addJavasFromEnv(javas); javas = addJavasFromEnv(javas);
javas.removeDuplicates(); javas.removeDuplicates();
return javas; return javas;
@ -467,6 +470,8 @@ QList<QString> JavaUtils::FindJavaPaths()
javas.append(this->GetDefaultJava()->path); javas.append(this->GetDefaultJava()->path);
javas.append(getMinecraftJavaBundle()); javas.append(getMinecraftJavaBundle());
javas.append(getPrismJavaBundle());
javas.removeDuplicates();
return addJavasFromEnv(javas); return addJavasFromEnv(javas);
} }
#endif #endif
@ -478,12 +483,10 @@ QString JavaUtils::getJavaCheckPath()
QStringList getMinecraftJavaBundle() QStringList getMinecraftJavaBundle()
{ {
QString executable = "java";
QStringList processpaths; QStringList processpaths;
#if defined(Q_OS_OSX) #if defined(Q_OS_OSX)
processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime")); processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime"));
#elif defined(Q_OS_WIN32) #elif defined(Q_OS_WIN32)
executable += "w.exe";
auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", ""); auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", "");
processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime"); processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime");
@ -508,7 +511,7 @@ QStringList getMinecraftJavaBundle()
auto binFound = false; auto binFound = false;
for (auto& entry : entries) { for (auto& entry : entries) {
if (entry.baseName() == "bin") { if (entry.baseName() == "bin") {
javas.append(FS::PathCombine(entry.canonicalFilePath(), executable)); javas.append(FS::PathCombine(entry.canonicalFilePath(), JavaUtils::javaExecutable));
binFound = true; binFound = true;
break; break;
} }
@ -521,3 +524,33 @@ QStringList getMinecraftJavaBundle()
} }
return javas; return javas;
} }
#if defined(Q_OS_WIN32)
const QString JavaUtils::javaExecutable = "javaw.exe";
#else
const QString JavaUtils::javaExecutable = "java";
#endif
QStringList getPrismJavaBundle()
{
QList<QString> javas;
auto scanDir = [&](QString prefix) {
javas.append(FS::PathCombine(prefix, "jre", "bin", JavaUtils::javaExecutable));
javas.append(FS::PathCombine(prefix, "bin", JavaUtils::javaExecutable));
javas.append(FS::PathCombine(prefix, JavaUtils::javaExecutable));
};
auto scanJavaDir = [&](const QString& dirPath) {
QDir dir(dirPath);
if (!dir.exists())
return;
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (auto& entry : entries) {
scanDir(entry.canonicalFilePath());
}
};
scanJavaDir(APPLICATION->javaPath());
return javas;
}

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>
@ -27,6 +26,7 @@
QString stripVariableEntries(QString name, QString target, QString remove); QString stripVariableEntries(QString name, QString target, QString remove);
QProcessEnvironment CleanEnviroment(); QProcessEnvironment CleanEnviroment();
QStringList getMinecraftJavaBundle(); QStringList getMinecraftJavaBundle();
QStringList getPrismJavaBundle();
class JavaUtils : public QObject { class JavaUtils : public QObject {
Q_OBJECT Q_OBJECT
@ -42,4 +42,5 @@ class JavaUtils : public QObject {
#endif #endif
static QString getJavaCheckPath(); static QString getJavaCheckPath();
static const QString javaExecutable;
}; };

View File

@ -43,12 +43,12 @@ QString JavaVersion::toString() const
return m_string; return m_string;
} }
bool JavaVersion::requiresPermGen() bool JavaVersion::requiresPermGen() const
{ {
return !m_parseable || m_major < 8; return !m_parseable || m_major < 8;
} }
bool JavaVersion::isModular() bool JavaVersion::isModular() const
{ {
return m_parseable && m_major >= 9; return m_parseable && m_major >= 9;
} }
@ -59,12 +59,6 @@ bool JavaVersion::operator<(const JavaVersion& rhs)
auto major = m_major; auto major = m_major;
auto rmajor = rhs.m_major; auto rmajor = rhs.m_major;
// HACK: discourage using java 9
if (major > 8)
major = -major;
if (rmajor > 8)
rmajor = -rmajor;
if (major < rmajor) if (major < rmajor)
return true; return true;
if (major > rmajor) if (major > rmajor)
@ -109,3 +103,24 @@ bool JavaVersion::operator>(const JavaVersion& rhs)
{ {
return (!operator<(rhs)) && (!operator==(rhs)); return (!operator<(rhs)) && (!operator==(rhs));
} }
JavaVersion::JavaVersion(int major, int minor, int security, int build, QString name)
: m_major(major), m_minor(minor), m_security(security), m_name(name), m_parseable(true)
{
QStringList versions;
if (build != 0) {
m_prerelease = QString::number(build);
versions.push_front(m_prerelease);
}
if (m_security != 0)
versions.push_front(QString::number(m_security));
else if (!versions.isEmpty())
versions.push_front("0");
if (m_minor != 0)
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(".");
}

View File

@ -16,6 +16,7 @@ class JavaVersion {
public: public:
JavaVersion() {} JavaVersion() {}
JavaVersion(const QString& rhs); JavaVersion(const QString& rhs);
JavaVersion(int major, int minor, int security, int build = 0, QString name = "");
JavaVersion& operator=(const QString& rhs); JavaVersion& operator=(const QString& rhs);
@ -23,21 +24,24 @@ class JavaVersion {
bool operator==(const JavaVersion& rhs); bool operator==(const JavaVersion& rhs);
bool operator>(const JavaVersion& rhs); bool operator>(const JavaVersion& rhs);
bool requiresPermGen(); bool requiresPermGen() const;
bool isModular(); bool isModular() const;
QString toString() const; QString toString() const;
int major() { return m_major; } int major() const { return m_major; }
int minor() { return m_minor; } int minor() const { return m_minor; }
int security() { return m_security; } int security() const { return m_security; }
QString build() const { return m_prerelease; }
QString name() const { return m_name; }
private: private:
QString m_string; QString m_string;
int m_major = 0; int m_major = 0;
int m_minor = 0; int m_minor = 0;
int m_security = 0; int m_security = 0;
QString m_name = "";
bool m_parseable = false; bool m_parseable = false;
QString m_prerelease; QString m_prerelease;
}; };

View File

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/download/ArchiveDownloadTask.h"
#include <quazip.h>
#include <memory>
#include "MMCZip.h"
#include "Application.h"
#include "Untar.h"
#include "net/ChecksumValidator.h"
#include "net/NetJob.h"
#include "tasks/Task.h"
namespace Java {
ArchiveDownloadTask::ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash)
: m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash)
{}
void ArchiveDownloadTask::executeTask()
{
// JRE found ! download the zip
setStatus(tr("Downloading Java"));
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.fileName());
auto download = makeShared<NetJob>(QString("JRE::DownloadJava"), APPLICATION->network());
auto action = Net::Download::makeCached(m_url, entry);
if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) {
auto hashType = QCryptographicHash::Algorithm::Sha1;
if (m_checksum_type == "sha256") {
hashType = QCryptographicHash::Algorithm::Sha256;
}
action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8())));
}
download->addNetAction(action);
auto fullPath = entry->getFullPath();
connect(download.get(), &Task::failed, this, &ArchiveDownloadTask::emitFailed);
connect(download.get(), &Task::progress, this, &ArchiveDownloadTask::setProgress);
connect(download.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);
connect(download.get(), &Task::status, this, &ArchiveDownloadTask::setStatus);
connect(download.get(), &Task::details, this, &ArchiveDownloadTask::setDetails);
connect(download.get(), &Task::succeeded, [this, fullPath] {
// This should do all of the extracting and creating folders
extractJava(fullPath);
});
m_task = download;
m_task->start();
}
void ArchiveDownloadTask::extractJava(QString input)
{
setStatus(tr("Extracting java"));
if (input.endsWith("tar")) {
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
QFile in(input);
if (!in.open(QFile::ReadOnly)) {
emitFailed(tr("Unable to open supplied tar file."));
return;
}
if (!Tar::extract(&in, QDir(m_final_path).absolutePath())) {
emitFailed(tr("Unable to extract supplied tar file."));
return;
}
emitSucceeded();
return;
} else if (input.endsWith("tar.gz") || input.endsWith("taz") || input.endsWith("tgz")) {
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
if (!GZTar::extract(input, QDir(m_final_path).absolutePath())) {
emitFailed(tr("Unable to extract supplied tar file."));
return;
}
emitSucceeded();
return;
} else if (input.endsWith("zip")) {
auto zip = std::make_shared<QuaZip>(input);
if (!zip->open(QuaZip::mdUnzip)) {
emitFailed(tr("Unable to open supplied zip file."));
return;
}
auto files = zip->getFileNameList();
if (files.isEmpty()) {
emitFailed(tr("No files were found in the supplied zip file,"));
return;
}
m_task = makeShared<MMCZip::ExtractZipTask>(zip, m_final_path, files[0]);
auto progressStep = std::make_shared<TaskStepProgress>();
connect(m_task.get(), &Task::finished, this, [this, progressStep] {
progressStep->state = TaskStepState::Succeeded;
stepProgress(*progressStep);
});
connect(m_task.get(), &Task::succeeded, this, &ArchiveDownloadTask::emitSucceeded);
connect(m_task.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted);
connect(m_task.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
emitFailed(reason);
});
connect(m_task.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress);
connect(m_task.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
stepProgress(*progressStep);
});
connect(m_task.get(), &Task::status, this, [this, progressStep](QString status) {
progressStep->status = status;
stepProgress(*progressStep);
});
m_task->start();
return;
}
emitFailed(tr("Could not determine archive type!"));
}
bool ArchiveDownloadTask::abort()
{
auto aborted = canAbort();
if (m_task)
aborted = m_task->abort();
emitAborted();
return aborted;
};
} // namespace Java

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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 <QUrl>
#include "tasks/Task.h"
namespace Java {
class ArchiveDownloadTask : public Task {
Q_OBJECT
public:
ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = "");
virtual ~ArchiveDownloadTask() = default;
[[nodiscard]] bool canAbort() const override { return true; }
void executeTask() override;
virtual bool abort() override;
private slots:
void extractJava(QString input);
protected:
QUrl m_url;
QString m_final_path;
QString m_checksum_type;
QString m_checksum_hash;
Task::Ptr m_task;
};
} // namespace Java

View File

@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/download/ManifestDownloadTask.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;
};
namespace Java {
ManifestDownloadTask::ManifestDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash)
: m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash)
{}
void ManifestDownloadTask::executeTask()
{
setStatus(tr("Downloading Java"));
auto download = makeShared<NetJob>(QString("JRE::DownloadJava"), APPLICATION->network());
auto files = std::make_shared<QByteArray>();
auto action = Net::Download::makeByteArray(m_url, files);
if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) {
auto hashType = QCryptographicHash::Algorithm::Sha1;
if (m_checksum_type == "sha256") {
hashType = QCryptographicHash::Algorithm::Sha256;
}
action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8())));
}
download->addNetAction(action);
connect(download.get(), &Task::failed, this, &ManifestDownloadTask::emitFailed);
connect(download.get(), &Task::progress, this, &ManifestDownloadTask::setProgress);
connect(download.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress);
connect(download.get(), &Task::status, this, &ManifestDownloadTask::setStatus);
connect(download.get(), &Task::details, this, &ManifestDownloadTask::setDetails);
connect(download.get(), &Task::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);
});
m_task = download;
m_task->start();
}
void ManifestDownloadTask::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 = makeShared<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.get(), &Task::failed, this, &ManifestDownloadTask::emitFailed);
connect(elementDownload.get(), &Task::progress, this, &ManifestDownloadTask::setProgress);
connect(elementDownload.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress);
connect(elementDownload.get(), &Task::status, this, &ManifestDownloadTask::setStatus);
connect(elementDownload.get(), &Task::details, this, &ManifestDownloadTask::setDetails);
connect(elementDownload.get(), &Task::succeeded, this, &ManifestDownloadTask::emitSucceeded);
m_task = elementDownload;
m_task->start();
}
bool ManifestDownloadTask::abort()
{
auto aborted = canAbort();
if (m_task)
aborted = m_task->abort();
emitAborted();
return aborted;
};
} // namespace Java

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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 <QUrl>
#include "tasks/Task.h"
namespace Java {
class ManifestDownloadTask : public Task {
Q_OBJECT
public:
ManifestDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = "");
virtual ~ManifestDownloadTask() = default;
[[nodiscard]] bool canAbort() const override { return true; }
void executeTask() override;
virtual bool abort() override;
private slots:
void downloadJava(const QJsonDocument& doc);
protected:
QUrl m_url;
QString m_final_path;
QString m_checksum_type;
QString m_checksum_hash;
Task::Ptr m_task;
};
} // namespace Java

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"
@ -93,11 +94,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();
@ -109,10 +109,10 @@ void CheckJava::executeTask()
emitSucceeded(); emitSucceeded();
} }
void CheckJava::checkJavaFinished(JavaCheckResult result) void CheckJava::checkJavaFinished(const 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);
@ -120,14 +120,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(const 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

@ -100,6 +100,13 @@ QVariant VersionList::data(const QModelIndex& index, int role) const
return QVariant::fromValue(version); return QVariant::fromValue(version);
case RecommendedRole: case RecommendedRole:
return version->isRecommended(); return version->isRecommended();
case JavaMajorRole: {
auto major = version->version();
if (major.startsWith("java")) {
major = "Java " + major.mid(4);
}
return major;
}
// FIXME: this should be determined in whatever view/proxy is used... // FIXME: this should be determined in whatever view/proxy is used...
// case LatestRole: return version == getLatestStable(); // case LatestRole: return version == getLatestStable();
default: default:
@ -109,10 +116,14 @@ QVariant VersionList::data(const QModelIndex& index, int role) const
BaseVersionList::RoleList VersionList::providesRoles() const BaseVersionList::RoleList VersionList::providesRoles() const
{ {
return { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole, return m_provided_roles;
TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole };
} }
void VersionList::setProvidedRoles(RoleList roles)
{
m_provided_roles = roles;
};
QHash<int, QByteArray> VersionList::roleNames() const QHash<int, QByteArray> VersionList::roleNames() const
{ {
QHash<int, QByteArray> roles = BaseVersionList::roleNames(); QHash<int, QByteArray> roles = BaseVersionList::roleNames();

View File

@ -48,6 +48,8 @@ class VersionList : public BaseVersionList, public BaseEntity {
RoleList providesRoles() const override; RoleList providesRoles() const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
void setProvidedRoles(RoleList roles);
QString localFilename() const override; QString localFilename() const override;
QString uid() const { return m_uid; } QString uid() const { return m_uid; }
@ -83,6 +85,9 @@ class VersionList : public BaseVersionList, public BaseEntity {
Version::Ptr m_recommended; Version::Ptr m_recommended;
RoleList m_provided_roles = { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole,
TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole };
void setupAddedVersion(int row, const Version::Ptr& version); void setupAddedVersion(int row, const Version::Ptr& version);
}; };
} // namespace Meta } // namespace Meta

View File

@ -164,6 +164,11 @@ void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
{ {
m_compatibleJavaMajors.append(javaMajor); m_compatibleJavaMajors.append(javaMajor);
} }
void LaunchProfile::applyCompatibleJavaName(QString javaName)
{
if (!javaName.isEmpty())
m_compatibleJavaName = javaName;
}
void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext) void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext)
{ {
@ -334,6 +339,11 @@ const QList<int>& LaunchProfile::getCompatibleJavaMajors() const
return m_compatibleJavaMajors; return m_compatibleJavaMajors;
} }
const QString LaunchProfile::getCompatibleJavaName() const
{
return m_compatibleJavaName;
}
void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext, void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext,
QStringList& jars, QStringList& jars,
QStringList& nativeJars, QStringList& nativeJars,

View File

@ -59,6 +59,7 @@ class LaunchProfile : public ProblemProvider {
void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext); void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext);
void applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext); void applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext);
void applyCompatibleJavaMajors(QList<int>& javaMajor); void applyCompatibleJavaMajors(QList<int>& javaMajor);
void applyCompatibleJavaName(QString javaName);
void applyMainJar(LibraryPtr jar); void applyMainJar(LibraryPtr jar);
void applyProblemSeverity(ProblemSeverity severity); void applyProblemSeverity(ProblemSeverity severity);
/// clear the profile /// clear the profile
@ -80,6 +81,7 @@ class LaunchProfile : public ProblemProvider {
const QList<LibraryPtr>& getMavenFiles() const; const QList<LibraryPtr>& getMavenFiles() const;
const QList<AgentPtr>& getAgents() const; const QList<AgentPtr>& getAgents() const;
const QList<int>& getCompatibleJavaMajors() const; const QList<int>& getCompatibleJavaMajors() const;
const QString getCompatibleJavaName() const;
const LibraryPtr getMainJar() const; const LibraryPtr getMainJar() const;
void getLibraryFiles(const RuntimeContext& runtimeContext, void getLibraryFiles(const RuntimeContext& runtimeContext,
QStringList& jars, QStringList& jars,
@ -150,5 +152,7 @@ class LaunchProfile : public ProblemProvider {
/// compatible java major versions /// compatible java major versions
QList<int> m_compatibleJavaMajors; QList<int> m_compatibleJavaMajors;
QString m_compatibleJavaName;
ProblemSeverity m_problemSeverity = ProblemSeverity::None; ProblemSeverity m_problemSeverity = ProblemSeverity::None;
}; };

View File

@ -38,6 +38,8 @@
#include "MinecraftInstance.h" #include "MinecraftInstance.h"
#include "Application.h" #include "Application.h"
#include "BuildConfig.h" #include "BuildConfig.h"
#include "QObjectPtr.h"
#include "minecraft/launch/AutoInstallJava.h"
#include "minecraft/launch/CreateGameFolders.h" #include "minecraft/launch/CreateGameFolders.h"
#include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/ExtractNatives.h"
#include "minecraft/launch/PrintInstanceInfo.h" #include "minecraft/launch/PrintInstanceInfo.h"
@ -134,25 +136,21 @@ void MinecraftInstance::loadSpecificSettings()
return; return;
// Java Settings // Java Settings
auto javaOverride = m_settings->registerSetting("OverrideJava", false);
auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false); auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false); auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false);
m_settings->registerSetting("AutomaticJava", false);
// combinations
auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride);
auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride);
if (auto global_settings = globalSettings()) { if (auto global_settings = globalSettings()) {
m_settings->registerOverride(global_settings->getSetting("JavaPath"), javaOrLocation); m_settings->registerOverride(global_settings->getSetting("JavaPath"), locationOverride);
m_settings->registerOverride(global_settings->getSetting("JvmArgs"), javaOrArgs); m_settings->registerOverride(global_settings->getSetting("JvmArgs"), argsOverride);
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation); m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), locationOverride);
// special! // special!
m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), locationOverride);
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), locationOverride);
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), locationOverride);
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), locationOverride);
m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), locationOverride);
// Window Size // Window Size
auto windowSetting = m_settings->registerSetting("OverrideWindow", false); auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
@ -1060,11 +1058,6 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
process->appendStep(makeShared<TextPrint>(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher)); process->appendStep(makeShared<TextPrint>(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::Launcher));
} }
// check java
{
process->appendStep(makeShared<CheckJava>(pptr));
}
// create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732)
{ {
process->appendStep(makeShared<CreateGameFolders>(pptr)); process->appendStep(makeShared<CreateGameFolders>(pptr));
@ -1107,6 +1100,12 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
process->appendStep(makeShared<Update>(pptr, Net::Mode::Offline)); process->appendStep(makeShared<Update>(pptr, Net::Mode::Offline));
} }
// check java
{
process->appendStep(makeShared<AutoInstallJava>(pptr));
process->appendStep(makeShared<CheckJava>(pptr));
}
// if there are any jar mods // if there are any jar mods
{ {
process->appendStep(makeShared<ModMinecraftJar>(pptr)); process->appendStep(makeShared<ModMinecraftJar>(pptr));

View File

@ -185,6 +185,9 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi
out->compatibleJavaMajors.append(requireInteger(compatible)); out->compatibleJavaMajors.append(requireInteger(compatible));
} }
} }
if (in.contains("compatibleJavaName")) {
out->compatibleJavaName = requireString(in.value("compatibleJavaName"));
}
if (in.contains("downloads")) { if (in.contains("downloads")) {
auto downloadsObj = requireObject(in, "downloads"); auto downloadsObj = requireObject(in, "downloads");
@ -259,6 +262,9 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
} }
out.insert("compatibleJavaMajors", compatibleJavaMajorsOut); out.insert("compatibleJavaMajors", compatibleJavaMajorsOut);
} }
if (!in->compatibleJavaName.isEmpty()) {
writeString(out, "compatibleJavaName", in->compatibleJavaName);
}
} }
QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch) QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch)

View File

@ -36,6 +36,8 @@
#include "OneSixVersionFormat.h" #include "OneSixVersionFormat.h"
#include <Json.h> #include <Json.h>
#include <minecraft/MojangVersionFormat.h> #include <minecraft/MojangVersionFormat.h>
#include <QList>
#include "java/JavaMetadata.h"
#include "minecraft/Agent.h" #include "minecraft/Agent.h"
#include "minecraft/ParseUtils.h" #include "minecraft/ParseUtils.h"
@ -255,6 +257,13 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc
out->m_volatile = requireBoolean(root, "volatile"); out->m_volatile = requireBoolean(root, "volatile");
} }
if (root.contains("runtimes")) {
out->runtimes = {};
for (auto runtime : ensureArray(root, "runtimes")) {
out->runtimes.append(Java::parseJavaMeta(ensureObject(runtime)));
}
}
/* removed features that shouldn't be used */ /* removed features that shouldn't be used */
if (root.contains("tweakers")) { if (root.contains("tweakers")) {
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'")); out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));

View File

@ -73,6 +73,7 @@ void VersionFile::applyTo(LaunchProfile* profile, const RuntimeContext& runtimeC
profile->applyMods(mods); profile->applyMods(mods);
profile->applyTraits(traits); profile->applyTraits(traits);
profile->applyCompatibleJavaMajors(compatibleJavaMajors); profile->applyCompatibleJavaMajors(compatibleJavaMajors);
profile->applyCompatibleJavaName(compatibleJavaName);
for (auto library : libraries) { for (auto library : libraries) {
profile->applyLibrary(library, runtimeContext); profile->applyLibrary(library, runtimeContext);

View File

@ -36,6 +36,8 @@
#pragma once #pragma once
#include <QDateTime> #include <QDateTime>
#include <QHash>
#include <QList>
#include <QSet> #include <QSet>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -45,6 +47,7 @@
#include "Agent.h" #include "Agent.h"
#include "Library.h" #include "Library.h"
#include "ProblemProvider.h" #include "ProblemProvider.h"
#include "java/JavaMetadata.h"
#include "minecraft/Rule.h" #include "minecraft/Rule.h"
class PackProfile; class PackProfile;
@ -98,6 +101,9 @@ class VersionFile : public ProblemContainer {
/// Mojang: list of compatible java majors /// Mojang: list of compatible java majors
QList<int> compatibleJavaMajors; QList<int> compatibleJavaMajors;
/// Mojang: the name of recommended java version
QString compatibleJavaName;
/// Mojang: type of the Minecraft version /// Mojang: type of the Minecraft version
QString type; QString type;
@ -149,6 +155,8 @@ class VersionFile : public ProblemContainer {
/// is volatile -- may be removed as soon as it is no longer needed by something else /// is volatile -- may be removed as soon as it is no longer needed by something else
bool m_volatile = false; bool m_volatile = false;
QList<Java::MetadataPtr> runtimes;
public: public:
// Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more. // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more.
QMap<QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads; QMap<QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads;

View File

@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/>.
*
* 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 <QDir>
#include <QFileInfo>
#include <memory>
#include "Application.h"
#include "FileSystem.h"
#include "MessageLevel.h"
#include "SysInfo.h"
#include "java/JavaInstall.h"
#include "java/JavaInstallList.h"
#include "java/JavaUtils.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<MinecraftInstance>(m_parent->instance()))
, m_supported_arch(SysInfo::getSupportedJavaArchitecture()) {};
void AutoInstallJava::executeTask()
{
auto settings = m_instance->settings();
if (!APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() ||
(settings->get("OverrideJavaLocation").toBool() && QFileInfo::exists(settings->get("JavaPath").toString()))) {
emitSucceeded();
return;
}
auto packProfile = m_instance->getPackProfile();
if (!APPLICATION->settings()->get("AutomaticJavaDownload").toBool()) {
auto javas = APPLICATION->javalist();
m_current_task = javas->getLoadTask();
connect(m_current_task.get(), &Task::finished, this, [this, javas, packProfile] {
for (auto i = 0; i < javas->count(); i++) {
auto java = std::dynamic_pointer_cast<JavaInstall>(javas->at(i));
if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) {
if (!java->is_64bit) {
emit logLine(tr("The automatic Java mechanism detected a 32-bit installation of Java."), MessageLevel::Info);
}
setJavaPath(java->path);
return;
}
}
emit logLine(tr("No compatible Java version was found. Using the default one."), MessageLevel::Warning);
emitSucceeded();
});
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
emit progressReportingRequest();
return;
}
if (m_supported_arch.isEmpty()) {
emit logLine(tr("Your system (%1-%2) is not compatible with automatic Java installation. Using the default Java path.")
.arg(SysInfo::currentSystem(), SysInfo::useQTForArch()),
MessageLevel::Warning);
emitSucceeded();
return;
}
auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName();
if (wantedJavaName.isEmpty()) {
emit logLine(tr("Your meta information is out of date or doesn't have the information necessary to determine what installation of "
"Java should be used. "
"Using the default Java path."),
MessageLevel::Warning);
emitSucceeded();
return;
}
QDir javaDir(APPLICATION->javaPath());
auto wantedJavaPath = javaDir.absoluteFilePath(wantedJavaName);
if (QFileInfo::exists(wantedJavaPath)) {
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);
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
if (!m_current_task->isRunning()) {
m_current_task->start();
}
emit progressReportingRequest();
}
void AutoInstallJava::setJavaPath(QString path)
{
auto settings = m_instance->settings();
settings->set("OverrideJavaLocation", true);
settings->set("JavaPath", path);
settings->set("AutomaticJava", true);
emit logLine(tr("Compatible Java found at: %1.").arg(path), MessageLevel::Info);
emitSucceeded();
}
void AutoInstallJava::setJavaPathFromPartial()
{
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", JavaUtils::javaExecutable);
auto finalPath = javaDir.absoluteFilePath(relativeBinary);
if (QFileInfo::exists(finalPath)) {
setJavaPath(finalPath);
} else {
emit logLine(tr("No compatible Java version was found (the binary file does not exist). Using the default one."),
MessageLevel::Warning);
emitSucceeded();
}
return;
}
void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName)
{
auto runtimes = version->data()->runtimes;
for (auto java : runtimes) {
if (java->runtimeOS == m_supported_arch && java->name() == javaName) {
QDir javaDir(APPLICATION->javaPath());
auto final_path = javaDir.absoluteFilePath(java->m_name);
switch (java->downloadType) {
case Java::DownloadType::Manifest:
m_current_task = makeShared<Java::ManifestDownloadTask>(java->url, final_path, java->checksumType, java->checksumHash);
break;
case Java::DownloadType::Archive:
m_current_task = makeShared<Java::ArchiveDownloadTask>(java->url, final_path, java->checksumType, java->checksumHash);
break;
case Java::DownloadType::Unknown:
emitFailed(tr("Could not determine Java download type!"));
return;
}
auto deletePath = [final_path] { FS::deletePath(final_path); };
connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) {
deletePath();
emitFailed(reason);
});
connect(this, &Task::aborted, this, [this, deletePath] {
m_current_task->abort();
deletePath();
});
connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial);
connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava);
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
m_current_task->start();
return;
}
}
tryNextMajorJava();
}
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 versions of Java were found for your operating system: %1-%2").arg(SysInfo::currentSystem(), SysInfo::useQTForArch()),
MessageLevel::Warning);
emit logLine(tr("No compatible version of Java 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));
if (javaMajor->isLoaded()) {
downloadJava(javaMajor, wantedJavaName);
} else {
m_current_task = APPLICATION->metadataIndex()->loadVersion("net.minecraft.java", javaMajor->version(), Net::Mode::Online);
connect(m_current_task.get(), &Task::succeeded, this,
[this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); });
connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava);
connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress);
connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress);
connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus);
connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails);
if (!m_current_task->isRunning()) {
m_current_task->start();
}
}
}
bool AutoInstallJava::abort()
{
if (m_current_task && m_current_task->canAbort())
return m_current_task->abort();
return true;
}

View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023-2024 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/>.
*
* 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 <launch/LaunchStep.h>
#include <launch/LaunchTask.h>
#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; }
bool abort() override;
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;
};

View File

@ -34,7 +34,12 @@
*/ */
#include "VerifyJavaInstall.h" #include "VerifyJavaInstall.h"
#include <memory>
#include "Application.h"
#include "MessageLevel.h"
#include "java/JavaInstall.h"
#include "java/JavaInstallList.h"
#include "java/JavaVersion.h" #include "java/JavaVersion.h"
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
@ -46,6 +51,15 @@ void VerifyJavaInstall::executeTask()
auto settings = instance->settings(); auto settings = instance->settings();
auto storedVersion = settings->get("JavaVersion").toString(); auto storedVersion = settings->get("JavaVersion").toString();
auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool(); auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool();
auto javaArchitecture = settings->get("JavaArchitecture").toString();
auto maxMemAlloc = settings->get("MaxMemAlloc").toInt();
if (javaArchitecture == "32" && maxMemAlloc > 2048) {
emit logLine(tr("Max memory allocation exceeds the supported value.\n"
"The selected installation of Java is 32-bit and doesn't support more than 2048MiB of RAM.\n"
"The instance may not start due to this."),
MessageLevel::Error);
}
auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors(); auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors();

View File

@ -353,5 +353,11 @@
<file>scalable/instances/neoforged.svg</file> <file>scalable/instances/neoforged.svg</file>
<file>128x128/instances/forge.png</file> <!-- LGPL3 Forge Development LLC --> <file>128x128/instances/forge.png</file> <!-- LGPL3 Forge Development LLC -->
<file>128x128/instances/liteloader.png</file> <!-- CC-BY-SA 4.0 LiteLoader --> <file>128x128/instances/liteloader.png</file> <!-- CC-BY-SA 4.0 LiteLoader -->
<!-- java providers -->
<file>scalable/adoptium.svg</file> <!-- The Adoptium Logo is a registered trademark of the Eclipse Foundation. -->
<file>scalable/azul.svg</file> <!-- Azul, Zulu, Azul Systems, the Azul Systems logo, Azul Zulu are either registered trademarks or trademarks of Azul Systems, registered in the U.S. and elsewhere. -->
<file>scalable/mojang.svg</file> <!-- The Mojang Logo is a registered trademark of Mojang AB. -->
</qresource> </qresource>
</RCC> </RCC>

View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
sodipodi:docname="Eclipse_Adoptium_Logo_A_only_no_outline.svg"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
id="svg8"
version="1.1"
viewBox="0 0 800 800"
height="800"
width="800"
inkscape:export-filename="Eclipse_Adoptium_Logo.png"
inkscape:export-xdpi="61.439999"
inkscape:export-ydpi="61.439999"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath242">
<path
d="m 987.855,678.469 -0.019,-0.008 -107.453,233.098 -81.121,-175.989 235.718,-106.09 0.01,0.02 c -21.01,10.078 -37.838,27.52 -47.135,48.969 z"
id="path240" />
</clipPath>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-481.882,253.823,253.823,481.882,1152.35,658.95)"
spreadMethod="pad"
id="linearGradient248">
<stop
style="stop-opacity:1;stop-color:#421644"
offset="0"
id="stop244" />
<stop
style="stop-opacity:1;stop-color:#151530"
offset="1"
id="stop246" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath258">
<path
d="m 1164.62,758.102 0.08,0.039 -194.618,422.029 c -0.547,1.32 -1.184,2.59 -1.789,3.88 l -1.453,3.16 -0.059,-0.03 c -0.324,0.65 -0.703,1.27 -1.054,1.9 7.382,-13.68 11.589,-29.34 11.589,-45.98 0,-14.34 -3.191,-27.91 -8.777,-40.15 l 0.059,-0.03 -88.215,-191.361 107.453,-233.098 0.019,0.008 c 14.915,-34.387 49.125,-58.457 89.005,-58.457 53.57,0 96.99,43.429 96.99,97 0,14.707 -3.37,28.597 -9.23,41.09 z"
id="path256" />
</clipPath>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(265.274,-595.434,-595.434,-265.274,846.292,1230.46)"
spreadMethod="pad"
id="linearGradient264">
<stop
style="stop-opacity:1;stop-color:#a21058"
offset="0"
id="stop260" />
<stop
style="stop-opacity:1;stop-color:#421644"
offset="1"
id="stop262" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath274">
<path
d="m 880.316,1240.11 c -37.687,0 -70.273,-21.54 -86.328,-52.93 l -0.058,0.03 -1.457,-3.16 c -0.606,-1.29 -1.239,-2.56 -1.785,-3.88 l -194.622,-422.029 0.079,-0.039 c -5.86,-12.493 -9.227,-26.383 -9.227,-41.09 0,-53.571 43.418,-97 96.992,-97 39.871,0 74.09,24.07 89.004,58.457 l 0.02,-0.008 195.664,424.459 -0.059,0.03 c 5.586,12.24 8.777,25.81 8.777,40.15 0,53.58 -43.425,97.01 -97,97.01 z"
id="path272" />
</clipPath>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(259.548,595.434,595.434,-259.548,649.304,625.36)"
spreadMethod="pad"
id="linearGradient280">
<stop
style="stop-opacity:1;stop-color:#ee1a6d"
offset="0"
id="stop276" />
<stop
style="stop-opacity:1;stop-color:#a21058"
offset="1"
id="stop278" />
</linearGradient>
</defs>
<sodipodi:namedview
height="100mm"
inkscape:window-maximized="1"
inkscape:window-y="-8"
inkscape:window-x="1912"
inkscape:window-height="1057"
inkscape:window-width="1920"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="g216"
inkscape:document-units="px"
inkscape:cy="396.88538"
inkscape:cx="322.07976"
inkscape:zoom="0.72187089"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#494949"
id="base"
inkscape:showpageshadow="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
transform="matrix(0.0826697,0,0,-0.0826697,-36.864228,126.62103)"
id="g216">
<g
id="g236"
transform="matrix(13.679999,0,0,13.679976,-6760.5652,-16025.746)">
<g
clip-path="url(#clipPath242)"
id="g238">
<path
id="path250"
style="fill:url(#linearGradient248);fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 987.855,678.469 -0.019,-0.008 -107.453,233.098 -81.121,-175.989 235.718,-106.09 0.01,0.02 c -21.01,10.078 -37.838,27.52 -47.135,48.969" />
</g>
</g>
<g
id="g252"
transform="matrix(13.679999,0,0,13.679976,-6760.5652,-16025.746)">
<g
clip-path="url(#clipPath258)"
id="g254">
<path
id="path266"
style="fill:url(#linearGradient264);fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 1164.62,758.102 0.08,0.039 -194.618,422.029 c -0.547,1.32 -1.184,2.59 -1.789,3.88 l -1.453,3.16 -0.059,-0.03 c -0.324,0.65 -0.703,1.27 -1.054,1.9 7.382,-13.68 11.589,-29.34 11.589,-45.98 0,-14.34 -3.191,-27.91 -8.777,-40.15 l 0.059,-0.03 -88.215,-191.361 107.453,-233.098 0.019,0.008 c 14.915,-34.387 49.125,-58.457 89.005,-58.457 53.57,0 96.99,43.429 96.99,97 0,14.707 -3.37,28.597 -9.23,41.09" />
</g>
</g>
<g
id="g268"
transform="matrix(1.2000009,0,0,1.1999989,-853.84911,665.71675)">
<g
clip-path="url(#clipPath274)"
id="g270"
transform="matrix(11.39999,0,0,11.39999,-4922.2595,-13909.564)">
<path
id="path282"
style="fill:url(#linearGradient280);fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 880.316,1240.11 c -37.687,0 -70.273,-21.54 -86.328,-52.93 l -0.058,0.03 -1.457,-3.16 c -0.606,-1.29 -1.239,-2.56 -1.785,-3.88 l -194.622,-422.029 0.079,-0.039 c -5.86,-12.493 -9.227,-26.383 -9.227,-41.09 0,-53.571 43.418,-97 96.992,-97 39.871,0 74.09,24.07 89.004,58.457 l 0.02,-0.008 195.664,424.459 -0.059,0.03 c 5.586,12.24 8.777,25.81 8.777,40.15 0,53.58 -43.425,97.01 -97,97.01" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64" fill="none" style="scroll-behavior: auto !important;">
<path d="M26.792 4.49429L7.62244 44.4309C7.20797 45.2944 8.12743 46.1903 8.97988 45.7536L28.26 35.8772C28.6557 35.6745 29.1376 35.7574 29.4429 36.0805L53.2128 61.2399C53.994 62.0668 55.3295 61.1553 54.844 60.1264L28.5979 4.50031C28.2386 3.73874 27.1564 3.73514 26.792 4.49429Z" fill="#00374A"/>
<path d="M26.792 4.49429L7.62244 44.4309C7.20797 45.2944 8.12743 46.1903 8.97988 45.7536L28.26 35.8772C28.6557 35.6745 29.1376 35.7574 29.4429 36.0805L53.2128 61.2399C53.994 62.0668 55.3295 61.1553 54.844 60.1264L28.5979 4.50031C28.2386 3.73874 27.1564 3.73514 26.792 4.49429Z" fill="url(#paint0_linear_3578_2134)" fill-opacity="0.7" style="mix-blend-mode:overlay"/>
<path d="M34.0172 15.5765L60.9237 19.1042C61.9046 19.2329 62.1231 20.5555 61.2357 20.9928L33.4872 34.6659L9.3254 45.9918C8.34481 46.4514 7.43514 45.2419 8.14881 44.4273L33.135 15.909C33.3551 15.6578 33.686 15.5331 34.0172 15.5765Z" fill="#006588"/>
<path d="M34.0172 15.5765L60.9237 19.1042C61.9046 19.2329 62.1231 20.5555 61.2357 20.9928L33.4872 34.6659L9.3254 45.9918C8.34481 46.4514 7.43514 45.2419 8.14881 44.4273L33.135 15.909C33.3551 15.6578 33.686 15.5331 34.0172 15.5765Z" fill="url(#paint1_linear_3578_2134)" fill-opacity="0.7" style="mix-blend-mode:overlay"/>
<defs>
<linearGradient id="paint0_linear_3578_2134" x1="31.2618" y1="2.59998" x2="31.2618" y2="65.8" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint1_linear_3578_2134" x1="46.2268" y1="4.17812" x2="31.5574" y2="56.1518" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="800"
height="800"
viewBox="0 0 800 800"
xml:space="preserve"
style="scroll-behavior: auto !important;"
id="svg4"
sodipodi:docname="Mojang_Studios_Logo_(2020,_icon).svg"
inkscape:export-filename="Mojang_Studios_Logo_(2020,_icon).png"
inkscape:export-xdpi="61.439999"
inkscape:export-ydpi="61.439999"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview4"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="1.46625"
inkscape:cx="374.08355"
inkscape:cy="369.30946"
inkscape:window-width="2560"
inkscape:window-height="1369"
inkscape:window-x="6392"
inkscape:window-y="802"
inkscape:window-maximized="1"
inkscape:current-layer="g3" />&#10;<desc
id="desc1">Created with Fabric.js 3.6.3</desc>&#10;<defs
id="defs1">&#10;</defs>&#10;<g
transform="matrix(2 0 0 2 399.75 399.75)"
id="g4">&#10;<g
style=""
id="g3">&#10; <g
transform="matrix(3.37 0 0 3.37 0 0)"
id="g1">&#10;<polygon
style="display:inline;opacity:1;fill:#f0313b;fill-rule:nonzero;stroke:#f0313b;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0"
vector-effect="non-scaling-stroke"
points="50,50 50,-50 -50,-50 -50,50 "
id="polygon1" />&#10;</g>&#10; <g
transform="matrix(3.78 0 0 3.78 0.01 0)"
id="g2">&#10;<path
style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;"
vector-effect="non-scaling-stroke"
transform=" translate(-44.68, -41.16)"
d="M 13.51988 41.11152 q 0 -12.31086 0.0047 -24.62173 c 0.00469 -2.87722 0.16418 -3.03875 3.01027 -3.04146 q 21.527 -0.02051 43.054 -0.01353 c 2.06114 0.0004 2.36366 0.32134 2.39718 2.42144 c 0.0238 1.49194 -0.01523 2.98567 0.03993 4.47616 a 6.77817 6.77817 0 0 0 6.69647 6.91868 c 1.65784 0.11443 3.328 0.04044 4.99213 0.07612 c 1.797 0.03852 2.04939 0.30467 2.12207 2.13943 c 0.01137 0.28664 0.01239 0.57383 0.01247 0.86076 q 0.00432 17.64834 0.00423 35.29668 c -0.00158 3.2118 -0.0499 3.24957 -3.28974 3.26017 q -3.78871 0.01236 -7.57745 -0.01166 c -2.96088 -0.02069 -3.03087 -0.09046 -3.032 -2.98084 q -0.00659 -16.87367 -0.00343 -33.74735 c 0 -0.40175 0.01018 -0.80387 -0.00321 -1.20519 a 3.329 3.329 0 0 0 -3.548 -3.56756 c -1.08962 -0.032 -2.18238 -0.03167 -3.27158 0.01034 a 3.34773 3.34773 0 0 0 -3.482 3.47659 c -0.03516 0.62965 -0.02128 1.26231 -0.02172 1.8936 q -0.00641 9.21165 -0.01056 18.42326 c -0.00046 0.68869 0.01794 1.37909 -0.02307 2.06567 c -0.075 1.25525 -0.44257 1.71282 -1.65149 1.72825 q -5.25132 0.06705 -10.50391 -0.00754 c -1.2165 -0.01879 -1.58169 -0.48679 -1.66785 -1.72683 c -0.03575 -0.51435 -0.01843 -1.03265 -0.019 -1.54914 q -0.0112 -9.81428 -0.0222 -19.62854 a 13.12088 13.12088 0 0 0 -0.05025 -1.71908 a 3.25128 3.25128 0 0 0 -3.10179 -2.93609 a 33.39881 33.39881 0 0 0 -3.95853 0.00271 a 3.1568 3.1568 0 0 0 -3.15611 3.21043 c -0.05842 1.26029 -0.03349 2.52474 -0.03457 3.78735 q -0.01343 15.66842 -0.02277 31.33681 c -0.00035 0.45913 0.01748 0.92 -0.01557 1.377 c -0.09 1.24451 -0.45629 1.713 -1.67232 1.72878 q -5.25117 0.06813 -10.50381 -0.01831 c -1.23915 -0.02079 -1.58295 -0.457 -1.6812 -1.71692 c -0.04454 -0.5709 -0.02489 -1.1472 -0.025 -1.72107 q -0.00183 -12.13872 -0.00083 -24.27744 Z"
stroke-linecap="round"
id="path1" />&#10;</g>&#10;</g>&#10;</g>&#10;</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -26,10 +26,6 @@ class QDialogButtonBox;
class VersionSelectWidget; class VersionSelectWidget;
class QPushButton; class QPushButton;
namespace Ui {
class VersionSelectDialog;
}
class VersionProxyModel; class VersionProxyModel;
class VersionSelectDialog : public QDialog { class VersionSelectDialog : public QDialog {

View File

@ -0,0 +1,338 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2024 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 "InstallJavaDialog.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include "Application.h"
#include "BaseVersionList.h"
#include "FileSystem.h"
#include "Filter.h"
#include "java/download/ArchiveDownloadTask.h"
#include "java/download/ManifestDownloadTask.h"
#include "meta/Index.h"
#include "meta/VersionList.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/java/VersionList.h"
#include "ui/widgets/PageContainer.h"
#include "ui/widgets/VersionSelectWidget.h"
class InstallJavaPage : public QWidget, public BasePage {
public:
Q_OBJECT
public:
explicit InstallJavaPage(const QString& id, const QString& iconName, const QString& name, QWidget* parent = nullptr)
: QWidget(parent), uid(id), iconName(iconName), name(name)
{
setObjectName(QStringLiteral("VersionSelectWidget"));
horizontalLayout = new QHBoxLayout(this);
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
horizontalLayout->setContentsMargins(0, 0, 0, 0);
majorVersionSelect = new VersionSelectWidget(this);
majorVersionSelect->selectCurrent();
majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta."));
majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!"));
horizontalLayout->addWidget(majorVersionSelect, 1);
javaVersionSelect = new VersionSelectWidget(this);
javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS."));
javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!"));
horizontalLayout->addWidget(javaVersionSelect, 4);
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion);
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
connect(javaVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
QMetaObject::connectSlotsByName(this);
}
~InstallJavaPage()
{
delete horizontalLayout;
delete majorVersionSelect;
delete javaVersionSelect;
}
//! loads the list if needed.
void initialize(Meta::VersionList::Ptr vlist)
{
vlist->setProvidedRoles({ BaseVersionList::JavaMajorRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole });
majorVersionSelect->initialize(vlist.get());
}
void setSelectedVersion(BaseVersion::Ptr version)
{
auto dcast = std::dynamic_pointer_cast<Meta::Version>(version);
if (!dcast) {
return;
}
javaVersionSelect->initialize(new Java::VersionList(dcast, this));
javaVersionSelect->selectCurrent();
}
QString id() const override { return uid; }
QString displayName() const override { return name; }
QIcon icon() const override { return APPLICATION->getThemedIcon(iconName); }
void openedImpl() override
{
if (loaded)
return;
const auto versions = APPLICATION->metadataIndex()->get(uid);
if (!versions)
return;
initialize(versions);
loaded = true;
}
void setParentContainer(BasePageContainer* container) override
{
auto dialog = dynamic_cast<QDialog*>(dynamic_cast<PageContainer*>(container)->parent());
connect(javaVersionSelect->view(), &QAbstractItemView::doubleClicked, dialog, &QDialog::accept);
}
BaseVersion::Ptr selectedVersion() const { return javaVersionSelect->selectedVersion(); }
void selectSearch() { javaVersionSelect->selectSearch(); }
void loadList()
{
majorVersionSelect->loadList();
javaVersionSelect->loadList();
}
public slots:
void setRecommendedMajors(const QStringList& majors)
{
m_recommended_majors = majors;
recommendedFilterChanged();
}
void setRecomend(bool recomend)
{
m_recommend = recomend;
recommendedFilterChanged();
}
void recommendedFilterChanged()
{
if (m_recommend) {
majorVersionSelect->setFilter(BaseVersionList::ModelRoles::JavaMajorRole, new ExactListFilter(m_recommended_majors));
} else {
majorVersionSelect->setFilter(BaseVersionList::ModelRoles::JavaMajorRole, new ExactListFilter());
}
}
signals:
void selectionChanged();
private:
const QString uid;
const QString iconName;
const QString name;
bool loaded = false;
QHBoxLayout* horizontalLayout = nullptr;
VersionSelectWidget* majorVersionSelect = nullptr;
VersionSelectWidget* javaVersionSelect = nullptr;
QStringList m_recommended_majors;
bool m_recommend;
};
static InstallJavaPage* pageCast(BasePage* page)
{
auto result = dynamic_cast<InstallJavaPage*>(page);
Q_ASSERT(result != nullptr);
return result;
}
namespace Java {
QStringList getRecommendedJavaVersionsFromVersionList(Meta::VersionList::Ptr list)
{
QStringList recommendedJavas;
for (auto ver : list->versions()) {
auto major = ver->version();
if (major.startsWith("java")) {
major = "Java " + major.mid(4);
}
recommendedJavas.append(major);
}
return recommendedJavas;
}
InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget* parent)
: QDialog(parent), container(new PageContainer(this, QString(), this)), buttons(new QDialogButtonBox(this))
{
auto layout = new QVBoxLayout(this);
container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
layout->addWidget(container);
auto buttonLayout = new QHBoxLayout(this);
auto refreshLayout = new QHBoxLayout(this);
auto refreshButton = new QPushButton(tr("&Refresh"), this);
connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(); });
refreshLayout->addWidget(refreshButton);
auto recommendedCheckBox = new QCheckBox("Recommended", this);
recommendedCheckBox->setCheckState(Qt::CheckState::Checked);
connect(recommendedCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
for (BasePage* page : container->getPages()) {
pageCast(page)->setRecomend(state == Qt::Checked);
}
});
refreshLayout->addWidget(recommendedCheckBox);
buttonLayout->addLayout(refreshLayout);
buttons->setOrientation(Qt::Horizontal);
buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttons->button(QDialogButtonBox::Ok)->setText(tr("Download"));
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
buttonLayout->addWidget(buttons);
layout->addLayout(buttonLayout);
setWindowTitle(dialogTitle());
setWindowModality(Qt::WindowModal);
resize(840, 480);
QStringList recommendedJavas;
if (auto mcInst = dynamic_cast<MinecraftInstance*>(instance); mcInst) {
auto mc = mcInst->getPackProfile()->getComponent("net.minecraft");
if (mc) {
auto file = mc->getVersionFile(); // no need for load as it should already be loaded
if (file) {
for (auto major : file->compatibleJavaMajors) {
recommendedJavas.append(QString("Java %1").arg(major));
}
}
}
} else {
const auto versions = APPLICATION->metadataIndex()->get("net.minecraft.java");
if (versions) {
if (versions->isLoaded()) {
recommendedJavas = getRecommendedJavaVersionsFromVersionList(versions);
} else {
auto newTask = versions->getLoadTask();
if (newTask) {
connect(newTask.get(), &Task::succeeded, this, [this, versions] {
auto recommendedJavas = getRecommendedJavaVersionsFromVersionList(versions);
for (BasePage* page : container->getPages()) {
pageCast(page)->setRecommendedMajors(recommendedJavas);
}
});
if (!newTask->isRunning())
newTask->start();
} else {
recommendedJavas = getRecommendedJavaVersionsFromVersionList(versions);
}
}
}
}
for (BasePage* page : container->getPages()) {
if (page->id() == uid)
container->selectPage(page->id());
auto cast = pageCast(page);
cast->setRecomend(true);
connect(cast, &InstallJavaPage::selectionChanged, this, [this, cast] { validate(cast); });
if (!recommendedJavas.isEmpty()) {
cast->setRecommendedMajors(recommendedJavas);
}
}
connect(container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* selected) { validate(selected); });
pageCast(container->selectedPage())->selectSearch();
validate(container->selectedPage());
}
QList<BasePage*> InstallDialog::getPages()
{
return {
// Mojang
new InstallJavaPage("net.minecraft.java", "mojang", tr("Mojang")),
// Adoptium
new InstallJavaPage("net.adoptium.java", "adoptium", tr("Adoptium")),
// Azul
new InstallJavaPage("com.azul.java", "azul", tr("Azul Zulu")),
};
}
QString InstallDialog::dialogTitle()
{
return tr("Install Java");
}
void InstallDialog::validate(BasePage* selected)
{
buttons->button(QDialogButtonBox::Ok)->setEnabled(!!std::dynamic_pointer_cast<Java::Metadata>(pageCast(selected)->selectedVersion()));
}
void InstallDialog::done(int result)
{
if (result == Accepted) {
auto* page = pageCast(container->selectedPage());
if (page->selectedVersion()) {
auto meta = std::dynamic_pointer_cast<Java::Metadata>(page->selectedVersion());
if (meta) {
Task::Ptr task;
auto final_path = FS::PathCombine(APPLICATION->javaPath(), meta->m_name);
auto deletePath = [final_path] { FS::deletePath(final_path); };
switch (meta->downloadType) {
case Java::DownloadType::Manifest:
task = makeShared<ManifestDownloadTask>(meta->url, final_path, meta->checksumType, meta->checksumHash);
break;
case Java::DownloadType::Archive:
task = makeShared<ArchiveDownloadTask>(meta->url, final_path, meta->checksumType, meta->checksumHash);
break;
case Java::DownloadType::Unknown:
QString error = QString(tr("Could not determine Java download type!"));
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
deletePath();
}
connect(task.get(), &Task::failed, this, [this, &deletePath](QString reason) {
QString error = QString("Java download failed: %1").arg(reason);
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
deletePath();
});
connect(task.get(), &Task::aborted, this, deletePath);
ProgressDialog pg(this);
pg.setSkipButton(true, tr("Abort"));
pg.execWithTask(task.get());
} else {
return;
}
} else {
return;
}
}
QDialog::done(result);
}
} // namespace Java
#include "InstallJavaDialog.moc"

View File

@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2024 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 <QDialog>
#include "BaseInstance.h"
#include "ui/pages/BasePageProvider.h"
class MinecraftInstance;
class PageContainer;
class PackProfile;
class QDialogButtonBox;
namespace Java {
class InstallDialog final : public QDialog, private BasePageProvider {
Q_OBJECT
public:
explicit InstallDialog(const QString& uid = QString(), BaseInstance* instance = nullptr, QWidget* parent = nullptr);
QList<BasePage*> getPages() override;
QString dialogTitle() override;
void validate(BasePage* selected);
void done(int result) override;
private:
PageContainer* container;
QDialogButtonBox* buttons;
};
} // namespace Java

View File

@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2024 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 "VersionList.h"
#include <memory>
#include "BaseVersionList.h"
#include "SysInfo.h"
#include "java/JavaMetadata.h"
#include "meta/VersionList.h"
namespace Java {
VersionList::VersionList(Meta::Version::Ptr version, QObject* parent) : BaseVersionList(parent), m_version(version)
{
if (version->isLoaded())
sortVersions();
}
Task::Ptr VersionList::getLoadTask()
{
auto task = m_version->loadTask(Net::Mode::Online);
connect(task.get(), &Task::finished, this, &VersionList::sortVersions);
return task;
}
const BaseVersion::Ptr VersionList::at(int i) const
{
return m_vlist.at(i);
}
bool VersionList::isLoaded()
{
return m_version->isLoaded();
}
int VersionList::count() const
{
return m_vlist.count();
}
QVariant VersionList::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<BaseVersion>(m_vlist[index.row()]));
case VersionIdRole:
return version->descriptor();
case VersionRole:
return version->version.toString();
case RecommendedRole:
return false; // do not recommend any version
case JavaNameRole:
return version->name();
case JavaMajorRole: {
auto major = version->version.toString();
if (major.startsWith("java")) {
major = "Java " + major.mid(4);
}
return major;
}
case TypeRole:
return version->packageType;
case Meta::VersionList::TimeRole:
return version->releaseTime;
default:
return QVariant();
}
}
BaseVersionList::RoleList VersionList::providesRoles() const
{
return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, JavaNameRole, TypeRole, Meta::VersionList::TimeRole };
}
bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right)
{
auto rleft = std::dynamic_pointer_cast<Java::Metadata>(right);
auto rright = std::dynamic_pointer_cast<Java::Metadata>(left);
return (*rleft) < (*rright);
}
void VersionList::sortVersions()
{
if (!m_version || !m_version->data())
return;
QString versionStr = SysInfo::getSupportedJavaArchitecture();
beginResetModel();
auto runtimes = m_version->data()->runtimes;
m_vlist = {};
if (!versionStr.isEmpty() && !runtimes.isEmpty()) {
std::copy_if(runtimes.begin(), runtimes.end(), std::back_inserter(m_vlist),
[versionStr](Java::MetadataPtr val) { return val->runtimeOS == versionStr; });
std::sort(m_vlist.begin(), m_vlist.end(), sortJavas);
} else {
qWarning() << "No Java versions found for your operating system." << SysInfo::currentSystem() << " " << SysInfo::useQTForArch();
}
endResetModel();
}
} // namespace Java

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2024 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 "BaseVersionList.h"
#include "java/JavaMetadata.h"
#include "meta/Version.h"
namespace Java {
class VersionList : public BaseVersionList {
Q_OBJECT
public:
explicit VersionList(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<BaseVersion::Ptr>) override {}
protected:
Meta::Version::Ptr m_version;
QList<Java::MetadataPtr> m_vlist;
};
} // namespace Java

View File

@ -16,7 +16,6 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <memory>
#include "ui/pages/BasePage.h" #include "ui/pages/BasePage.h"
class BasePageProvider { class BasePageProvider {

View File

@ -35,12 +35,18 @@
*/ */
#include "JavaPage.h" #include "JavaPage.h"
#include "BuildConfig.h"
#include "JavaCommon.h" #include "JavaCommon.h"
#include "java/JavaInstall.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/java/InstallJavaDialog.h"
#include "ui_JavaPage.h" #include "ui_JavaPage.h"
#include <QCheckBox>
#include <QDir> #include <QDir>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QStringListModel>
#include <QTabBar> #include <QTabBar>
#include "ui/dialogs/VersionSelectDialog.h" #include "ui/dialogs/VersionSelectDialog.h"
@ -56,7 +62,22 @@
JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage)
{ {
ui->setupUi(this); ui->setupUi(this);
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
ui->managedJavaList->initialize(new JavaInstallList(this, true));
ui->managedJavaList->setResizeOn(2);
ui->managedJavaList->selectCurrent();
ui->managedJavaList->setEmptyString(tr("No managed java versions are installed"));
ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed java list!"));
connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] {
ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked());
if (!ui->autodetectJavaCheckBox->isChecked())
ui->autodownloadCheckBox->setChecked(false);
});
} else {
ui->autodownloadCheckBox->setHidden(true);
ui->tabWidget->tabBar()->hide(); ui->tabWidget->tabBar()->hide();
}
loadSettings(); loadSettings();
updateThresholds(); updateThresholds();
@ -94,6 +115,8 @@ void JavaPage::applySettings()
s->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " ")); s->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " "));
s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked()); s->set("IgnoreJavaCompatibility", ui->skipCompatibilityCheckbox->isChecked());
s->set("IgnoreJavaWizard", ui->skipJavaWizardCheckbox->isChecked()); s->set("IgnoreJavaWizard", ui->skipJavaWizardCheckbox->isChecked());
s->set("AutomaticJavaSwitch", ui->autodetectJavaCheckBox->isChecked());
s->set("AutomaticJavaDownload", ui->autodownloadCheckBox->isChecked());
JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget()); JavaCommon::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget());
} }
void JavaPage::loadSettings() void JavaPage::loadSettings()
@ -116,6 +139,8 @@ void JavaPage::loadSettings()
ui->jvmArgsTextBox->setPlainText(s->get("JvmArgs").toString()); ui->jvmArgsTextBox->setPlainText(s->get("JvmArgs").toString());
ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool()); ui->skipCompatibilityCheckbox->setChecked(s->get("IgnoreJavaCompatibility").toBool());
ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool()); ui->skipJavaWizardCheckbox->setChecked(s->get("IgnoreJavaWizard").toBool());
ui->autodetectJavaCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool());
ui->autodownloadCheckBox->setChecked(s->get("AutomaticJavaSwitch").toBool() && s->get("AutomaticJavaDownload").toBool());
} }
void JavaPage::on_javaDetectBtn_clicked() void JavaPage::on_javaDetectBtn_clicked()
@ -134,6 +159,14 @@ void JavaPage::on_javaDetectBtn_clicked()
if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) { if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) {
java = std::dynamic_pointer_cast<JavaInstall>(vselect.selectedVersion()); java = std::dynamic_pointer_cast<JavaInstall>(vselect.selectedVersion());
ui->javaPathTextBox->setText(java->path); ui->javaPathTextBox->setText(java->path);
if (!java->is_64bit && APPLICATION->settings()->get("MaxMemAlloc").toInt() > 2048) {
CustomMessageBox::selectable(this, tr("Confirm Selection"),
tr("You selected a 32-bit version of Java.\n"
"This installation does not support more than 2048MiB of RAM.\n"
"Please make sure that the maximum memory value is lower."),
QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok)
->exec();
}
} }
} }
@ -166,6 +199,13 @@ void JavaPage::on_javaTestBtn_clicked()
checker->run(); checker->run();
} }
void JavaPage::on_downloadJavaButton_clicked()
{
auto jdialog = new Java::InstallDialog({}, nullptr, this);
jdialog->exec();
ui->managedJavaList->loadList();
}
void JavaPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i) void JavaPage::on_maxMemSpinBox_valueChanged([[maybe_unused]] int i)
{ {
updateThresholds(); updateThresholds();
@ -210,3 +250,35 @@ void JavaPage::updateThresholds()
ui->labelMaxMemIcon->setPixmap(pix); ui->labelMaxMemIcon->setPixmap(pix);
} }
} }
void JavaPage::on_removeJavaButton_clicked()
{
auto version = ui->managedJavaList->selectedVersion();
auto dcast = std::dynamic_pointer_cast<JavaInstall>(version);
if (!dcast) {
return;
}
QDir dir(APPLICATION->javaPath());
auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (auto& entry : entries) {
if (dcast->path.startsWith(entry.canonicalFilePath())) {
auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"),
tr("You are about to remove the Java installation named \"%1\".\n"
"Are you sure?")
.arg(entry.fileName()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response == QMessageBox::Yes) {
FS::deletePath(entry.canonicalFilePath());
ui->managedJavaList->loadList();
}
break;
}
}
}
void JavaPage::on_refreshJavaButton_clicked()
{
ui->managedJavaList->loadList();
}

View File

@ -38,7 +38,7 @@
#include <Application.h> #include <Application.h>
#include <QObjectPtr.h> #include <QObjectPtr.h>
#include <QDialog> #include <QDialog>
#include <memory> #include <QStringListModel>
#include "JavaCommon.h" #include "JavaCommon.h"
#include "ui/pages/BasePage.h" #include "ui/pages/BasePage.h"
@ -72,6 +72,9 @@ class JavaPage : public QWidget, public BasePage {
void on_javaDetectBtn_clicked(); void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked(); void on_javaTestBtn_clicked();
void on_javaBrowseBtn_clicked(); void on_javaBrowseBtn_clicked();
void on_downloadJavaButton_clicked();
void on_removeJavaButton_clicked();
void on_refreshJavaButton_clicked();
void on_maxMemSpinBox_valueChanged(int i); void on_maxMemSpinBox_valueChanged(int i);
void checkerFinished(); void checkerFinished();

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>545</width> <width>559</width>
<height>580</height> <height>659</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -34,9 +34,9 @@
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="general">
<attribute name="title"> <attribute name="title">
<string notr="true">Tab 1</string> <string notr="true">General</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
@ -160,25 +160,6 @@
<string>Java Runtime</string> <string>Java Runtime</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="7" column="0" colspan="3">
<widget class="QPlainTextEdit" name="jvmArgsTextBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
</widget>
</item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QCheckBox" name="skipCompatibilityCheckbox"> <widget class="QCheckBox" name="skipCompatibilityCheckbox">
<property name="sizePolicy"> <property name="sizePolicy">
@ -225,7 +206,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="6" column="0"> <item row="8" column="0">
<widget class="QLabel" name="labelJVMArgs"> <widget class="QLabel" name="labelJVMArgs">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
@ -241,6 +222,45 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0">
<widget class="QCheckBox" name="skipJavaWizardCheckbox">
<property name="toolTip">
<string>If enabled, the launcher will not prompt you to choose a Java version if one isn't found.</string>
</property>
<property name="text">
<string>Skip Java &amp;Wizard</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="3">
<widget class="QPlainTextEdit" name="jvmArgsTextBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="autodetectJavaCheckBox">
<property name="toolTip">
<string>Automatically selects the Java version that is compatible with the current Minecraft instance, based on the major version required.</string>
</property>
<property name="text">
<string>Autodetect Java version</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3"> <item row="1" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
@ -277,13 +297,16 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="5" column="0"> <item row="7" column="0">
<widget class="QCheckBox" name="skipJavaWizardCheckbox"> <widget class="QCheckBox" name="autodownloadCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip"> <property name="toolTip">
<string>If enabled, the launcher will not prompt you to choose a Java version if one isn't found.</string> <string>Automatically downloads and selects the Java version recommended by Mojang.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Skip Java &amp;Wizard</string> <string>Auto-download Mojang Java</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -305,16 +328,106 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="management">
<attribute name="title">
<string>Management</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Downloaded Java Versions</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="VersionSelectWidget" name="managedJavaList" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="managedJavaBtnLayout">
<item>
<widget class="QPushButton" name="downloadJavaButton">
<property name="text">
<string>Download</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeJavaButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="refreshJavaButton">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="managementSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>VersionSelectWidget</class>
<extends>QWidget</extends>
<header>ui/widgets/VersionSelectWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops> <tabstops>
<tabstop>minMemSpinBox</tabstop> <tabstop>minMemSpinBox</tabstop>
<tabstop>maxMemSpinBox</tabstop> <tabstop>maxMemSpinBox</tabstop>
<tabstop>permGenSpinBox</tabstop> <tabstop>permGenSpinBox</tabstop>
<tabstop>javaBrowseBtn</tabstop>
<tabstop>javaPathTextBox</tabstop> <tabstop>javaPathTextBox</tabstop>
<tabstop>javaBrowseBtn</tabstop>
<tabstop>javaDetectBtn</tabstop>
<tabstop>javaTestBtn</tabstop>
<tabstop>skipCompatibilityCheckbox</tabstop>
<tabstop>skipJavaWizardCheckbox</tabstop>
<tabstop>jvmArgsTextBox</tabstop>
<tabstop>tabWidget</tabstop> <tabstop>tabWidget</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>

View File

@ -173,6 +173,16 @@ void LauncherPage::on_downloadsDirBrowseBtn_clicked()
} }
} }
void LauncherPage::on_javaDirBrowseBtn_clicked()
{
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Java Folder"), ui->javaDirTextBox->text());
if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) {
QString cooked_dir = FS::NormalizePath(raw_dir);
ui->javaDirTextBox->setText(cooked_dir);
}
}
void LauncherPage::on_skinsDirBrowseBtn_clicked() void LauncherPage::on_skinsDirBrowseBtn_clicked()
{ {
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Skins Folder"), ui->skinsDirTextBox->text()); QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Skins Folder"), ui->skinsDirTextBox->text());
@ -223,6 +233,7 @@ void LauncherPage::applySettings()
s->set("IconsDir", ui->iconsDirTextBox->text()); s->set("IconsDir", ui->iconsDirTextBox->text());
s->set("DownloadsDir", ui->downloadsDirTextBox->text()); s->set("DownloadsDir", ui->downloadsDirTextBox->text());
s->set("SkinsDir", ui->skinsDirTextBox->text()); s->set("SkinsDir", ui->skinsDirTextBox->text());
s->set("JavaDir", ui->javaDirTextBox->text());
s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked()); s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked());
auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId(); auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId();
@ -289,6 +300,7 @@ void LauncherPage::loadSettings()
ui->iconsDirTextBox->setText(s->get("IconsDir").toString()); ui->iconsDirTextBox->setText(s->get("IconsDir").toString());
ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString()); ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString());
ui->skinsDirTextBox->setText(s->get("SkinsDir").toString()); ui->skinsDirTextBox->setText(s->get("SkinsDir").toString());
ui->javaDirTextBox->setText(s->get("JavaDir").toString());
ui->downloadsDirWatchRecursiveCheckBox->setChecked(s->get("DownloadsDirWatchRecursive").toBool()); ui->downloadsDirWatchRecursiveCheckBox->setChecked(s->get("DownloadsDirWatchRecursive").toBool());
QString sortMode = s->get("InstSortMode").toString(); QString sortMode = s->get("InstSortMode").toString();

View File

@ -73,6 +73,7 @@ class LauncherPage : public QWidget, public BasePage {
void on_modsDirBrowseBtn_clicked(); void on_modsDirBrowseBtn_clicked();
void on_iconsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked();
void on_downloadsDirBrowseBtn_clicked(); void on_downloadsDirBrowseBtn_clicked();
void on_javaDirBrowseBtn_clicked();
void on_skinsDirBrowseBtn_clicked(); void on_skinsDirBrowseBtn_clicked();
void on_metadataDisableBtn_clicked(); void on_metadataDisableBtn_clicked();

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>511</width> <width>511</width>
<height>691</height> <height>726</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -94,7 +94,7 @@
<string>Folders</string> <string>Folders</string>
</property> </property>
<layout class="QGridLayout" name="foldersBoxLayout"> <layout class="QGridLayout" name="foldersBoxLayout">
<item row="4" column="0"> <item row="8" column="0">
<widget class="QLabel" name="labelDownloadsDir"> <widget class="QLabel" name="labelDownloadsDir">
<property name="text"> <property name="text">
<string>&amp;Downloads:</string> <string>&amp;Downloads:</string>
@ -104,42 +104,59 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="8" column="2">
<widget class="QLabel" name="labelInstDir">
<property name="text">
<string>I&amp;nstances:</string>
</property>
<property name="buddy">
<cstring>instDirTextBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="instDirTextBox"/>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="downloadsDirTextBox"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="iconsDirTextBox"/>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="skinsDirTextBox"/>
</item>
<item row="4" column="2">
<widget class="QToolButton" name="downloadsDirBrowseBtn"> <widget class="QToolButton" name="downloadsDirBrowseBtn">
<property name="text"> <property name="text">
<string>Browse</string> <string>Browse</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="1">
<widget class="QLineEdit" name="modsDirTextBox"/> <widget class="QLineEdit" name="iconsDirTextBox"/>
</item> </item>
<item row="1" column="2"> <item row="3" column="1">
<widget class="QToolButton" name="modsDirBrowseBtn"> <widget class="QLineEdit" name="javaDirTextBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelSkinsDir">
<property name="text"> <property name="text">
<string>Browse</string> <string>&amp;Skins:</string>
</property>
<property name="buddy">
<cstring>skinsDirTextBox</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelIconsDir">
<property name="text">
<string>&amp;Icons:</string>
</property>
<property name="buddy">
<cstring>iconsDirTextBox</cstring>
</property>
</widget>
</item>
<item row="9" column="1" colspan="2">
<widget class="QCheckBox" name="downloadsDirWatchRecursiveCheckBox">
<property name="toolTip">
<string>When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge).</string>
</property>
<property name="text">
<string>Check downloads folder recursively</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLineEdit" name="downloadsDirTextBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelJavaDir">
<property name="text">
<string>&amp;Java:</string>
</property>
<property name="buddy">
<cstring>javaDirTextBox</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -153,6 +170,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QLineEdit" name="skinsDirTextBox"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="modsDirTextBox"/>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="instDirTextBox"/>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="modsDirBrowseBtn">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="0" column="2"> <item row="0" column="2">
<widget class="QToolButton" name="instDirBrowseBtn"> <widget class="QToolButton" name="instDirBrowseBtn">
<property name="text"> <property name="text">
@ -167,40 +200,27 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="0" column="0">
<widget class="QLabel" name="labelIconsDir"> <widget class="QLabel" name="labelInstDir">
<property name="text"> <property name="text">
<string>&amp;Icons:</string> <string>I&amp;nstances:</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>iconsDirTextBox</cstring> <cstring>instDirTextBox</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="3" column="2">
<widget class="QToolButton" name="skinsDirBrowseBtn"> <widget class="QToolButton" name="javaDirBrowseBtn">
<property name="text"> <property name="text">
<string>Browse</string> <string>Browse</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="4" column="2">
<widget class="QLabel" name="labelSkinsDir"> <widget class="QToolButton" name="skinsDirBrowseBtn">
<property name="text"> <property name="text">
<string>&amp;Skins:</string> <string>Browse</string>
</property>
<property name="buddy">
<cstring>skinsDirTextBox</cstring>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="QCheckBox" name="downloadsDirWatchRecursiveCheckBox">
<property name="toolTip">
<string>When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge).</string>
</property>
<property name="text">
<string>Check downloads folder recursively</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -38,6 +38,9 @@
#include "InstanceSettingsPage.h" #include "InstanceSettingsPage.h"
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/WorldList.h" #include "minecraft/WorldList.h"
#include "settings/Setting.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/java/InstallJavaDialog.h"
#include "ui_InstanceSettingsPage.h" #include "ui_InstanceSettingsPage.h"
#include <QDialog> #include <QDialog>
@ -64,6 +67,8 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent)
m_settings = inst->settings(); m_settings = inst->settings();
ui->setupUi(this); ui->setupUi(this);
ui->javaDownloadBtn->setHidden(!BuildConfig.JAVA_DOWNLOADER_ENABLED);
connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked); connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked);
connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings); connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings); connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
@ -204,9 +209,6 @@ void InstanceSettingsPage::applySettings()
m_settings->reset("JvmArgs"); m_settings->reset("JvmArgs");
} }
// old generic 'override both' is removed.
m_settings->reset("OverrideJava");
// Custom Commands // Custom Commands
bool custcmd = ui->customCommands->checked(); bool custcmd = ui->customCommands->checked();
m_settings->set("OverrideCommands", custcmd); m_settings->set("OverrideCommands", custcmd);
@ -342,10 +344,11 @@ void InstanceSettingsPage::loadSettings()
ui->labelPermgenNote->setVisible(permGenVisible); ui->labelPermgenNote->setVisible(permGenVisible);
// Java Settings // Java Settings
bool overrideJava = m_settings->get("OverrideJava").toBool(); bool overrideLocation = m_settings->get("OverrideJavaLocation").toBool();
bool overrideLocation = m_settings->get("OverrideJavaLocation").toBool() || overrideJava; bool overrideArgs = m_settings->get("OverrideJavaArgs").toBool();
bool overrideArgs = m_settings->get("OverrideJavaArgs").toBool() || overrideJava;
connect(m_settings->getSetting("OverrideJavaLocation").get(), &Setting::SettingChanged, ui->javaSettingsGroupBox,
[this] { ui->javaSettingsGroupBox->setChecked(m_settings->get("OverrideJavaLocation").toBool()); });
ui->javaSettingsGroupBox->setChecked(overrideLocation); ui->javaSettingsGroupBox->setChecked(overrideLocation);
ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString()); ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString());
ui->skipCompatibilityCheckbox->setChecked(m_settings->get("IgnoreJavaCompatibility").toBool()); ui->skipCompatibilityCheckbox->setChecked(m_settings->get("IgnoreJavaCompatibility").toBool());
@ -431,6 +434,12 @@ void InstanceSettingsPage::loadSettings()
ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool());
} }
void InstanceSettingsPage::on_javaDownloadBtn_clicked()
{
auto jdialog = new Java::InstallDialog({}, m_instance, this);
jdialog->exec();
}
void InstanceSettingsPage::on_javaDetectBtn_clicked() void InstanceSettingsPage::on_javaDetectBtn_clicked()
{ {
if (JavaUtils::getJavaCheckPath().isEmpty()) { if (JavaUtils::getJavaCheckPath().isEmpty()) {
@ -452,6 +461,15 @@ void InstanceSettingsPage::on_javaDetectBtn_clicked()
ui->labelPermGen->setVisible(visible); ui->labelPermGen->setVisible(visible);
ui->labelPermgenNote->setVisible(visible); ui->labelPermgenNote->setVisible(visible);
m_settings->set("PermGenVisible", visible); m_settings->set("PermGenVisible", visible);
if (!java->is_64bit && m_settings->get("MaxMemAlloc").toInt() > 2048) {
CustomMessageBox::selectable(this, tr("Confirm Selection"),
tr("You selected a 32-bit version of Java.\n"
"This installation does not support more than 2048MiB of RAM.\n"
"Please make sure that the maximum memory value is lower."),
QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok)
->exec();
}
} }
} }

View File

@ -69,6 +69,7 @@ class InstanceSettingsPage : public QWidget, public BasePage {
void on_javaDetectBtn_clicked(); void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked(); void on_javaTestBtn_clicked();
void on_javaBrowseBtn_clicked(); void on_javaBrowseBtn_clicked();
void on_javaDownloadBtn_clicked();
void on_maxMemSpinBox_valueChanged(int i); void on_maxMemSpinBox_valueChanged(int i);
void on_serverJoinAddressButton_toggled(bool checked); void on_serverJoinAddressButton_toggled(bool checked);
void on_worldJoinButton_toggled(bool checked); void on_worldJoinButton_toggled(bool checked);

View File

@ -84,6 +84,13 @@
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="javaDownloadBtn">
<property name="text">
<string>Download Java</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="javaDetectBtn"> <widget class="QPushButton" name="javaDetectBtn">
<property name="text"> <property name="text">
@ -764,6 +771,12 @@
<tabstop>openGlobalJavaSettingsButton</tabstop> <tabstop>openGlobalJavaSettingsButton</tabstop>
<tabstop>settingsTabs</tabstop> <tabstop>settingsTabs</tabstop>
<tabstop>javaSettingsGroupBox</tabstop> <tabstop>javaSettingsGroupBox</tabstop>
<tabstop>javaPathTextBox</tabstop>
<tabstop>javaBrowseBtn</tabstop>
<tabstop>javaDownloadBtn</tabstop>
<tabstop>javaDetectBtn</tabstop>
<tabstop>javaTestBtn</tabstop>
<tabstop>skipCompatibilityCheckbox</tabstop>
<tabstop>memoryGroupBox</tabstop> <tabstop>memoryGroupBox</tabstop>
<tabstop>minMemSpinBox</tabstop> <tabstop>minMemSpinBox</tabstop>
<tabstop>maxMemSpinBox</tabstop> <tabstop>maxMemSpinBox</tabstop>
@ -783,6 +796,18 @@
<tabstop>useNativeOpenALCheck</tabstop> <tabstop>useNativeOpenALCheck</tabstop>
<tabstop>showGameTime</tabstop> <tabstop>showGameTime</tabstop>
<tabstop>recordGameTime</tabstop> <tabstop>recordGameTime</tabstop>
<tabstop>miscellaneousSettingsBox</tabstop>
<tabstop>closeAfterLaunchCheck</tabstop>
<tabstop>quitAfterGameStopCheck</tabstop>
<tabstop>perfomanceGroupBox</tabstop>
<tabstop>enableFeralGamemodeCheck</tabstop>
<tabstop>enableMangoHud</tabstop>
<tabstop>useDiscreteGpuCheck</tabstop>
<tabstop>gameTimeGroupBox</tabstop>
<tabstop>serverJoinGroupBox</tabstop>
<tabstop>serverJoinAddress</tabstop>
<tabstop>instanceAccountGroupBox</tabstop>
<tabstop>instanceAccountSelector</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -393,6 +393,11 @@ void VersionPage::on_actionChange_version_triggered()
bool important = false; bool important = false;
if (uid == "net.minecraft") { if (uid == "net.minecraft") {
important = true; important = true;
if (APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() && m_inst->settings()->get("AutomaticJava").toBool() &&
m_inst->settings()->get("OverrideJavaLocation").toBool()) {
m_inst->settings()->set("OverrideJavaLocation", false);
m_inst->settings()->set("JavaPath", "");
}
} }
m_profile->setComponentVersion(uid, vselect.selectedVersion()->descriptor(), important); m_profile->setComponentVersion(uid, vselect.selectedVersion()->descriptor(), important);
m_profile->resolve(Net::Mode::Online); m_profile->resolve(Net::Mode::Online);

View File

@ -12,12 +12,8 @@
#include <sys.h> #include <sys.h>
#include "FileSystem.h"
#include "JavaCommon.h" #include "JavaCommon.h"
#include "java/JavaInstall.h"
#include "java/JavaUtils.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/widgets/JavaSettingsWidget.h" #include "ui/widgets/JavaSettingsWidget.h"
#include "ui/widgets/VersionSelectWidget.h" #include "ui/widgets/VersionSelectWidget.h"
@ -57,6 +53,8 @@ bool JavaWizardPage::validatePage()
{ {
auto settings = APPLICATION->settings(); auto settings = APPLICATION->settings();
auto result = m_java_widget->validate(); auto result = m_java_widget->validate();
settings->set("AutomaticJavaSwitch", m_java_widget->autoDetectJava());
settings->set("AutomaticJavaDownload", m_java_widget->autoDownloadJava());
switch (result) { switch (result) {
default: default:
case JavaSettingsWidget::ValidationStatus::Bad: { case JavaSettingsWidget::ValidationStatus::Bad: {

View File

@ -4,6 +4,7 @@
#include <QGroupBox> #include <QGroupBox>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QSpinBox> #include <QSpinBox>
#include <QToolButton> #include <QToolButton>
@ -11,12 +12,16 @@
#include <sys.h> #include <sys.h>
#include "DesktopServices.h"
#include "FileSystem.h" #include "FileSystem.h"
#include "JavaCommon.h" #include "JavaCommon.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"
#include "ui/java/InstallJavaDialog.h"
#include "ui/widgets/VersionSelectWidget.h" #include "ui/widgets/VersionSelectWidget.h"
#include "Application.h" #include "Application.h"
@ -38,6 +43,9 @@ JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent)
connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaBrowseBtn_clicked); connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaBrowseBtn_clicked);
connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited); connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited);
connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked); connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked);
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaSettingsWidget::javaDownloadBtn_clicked);
}
} }
void JavaSettingsWidget::setupUi() void JavaSettingsWidget::setupUi()
@ -120,6 +128,38 @@ void JavaSettingsWidget::setupUi()
m_verticalLayout->addWidget(m_memoryGroupBox); m_verticalLayout->addWidget(m_memoryGroupBox);
m_horizontalBtnLayout = new QHBoxLayout();
m_horizontalBtnLayout->setObjectName(QStringLiteral("horizontalBtnLayout"));
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
m_javaDownloadBtn = new QPushButton(tr("Download Java"), this);
m_horizontalBtnLayout->addWidget(m_javaDownloadBtn);
}
m_verticalLayout->addLayout(m_horizontalBtnLayout);
m_autoJavaGroupBox = new QGroupBox(this);
m_autoJavaGroupBox->setObjectName(QStringLiteral("autoJavaGroupBox"));
m_veriticalJavaLayout = new QVBoxLayout(m_autoJavaGroupBox);
m_veriticalJavaLayout->setObjectName(QStringLiteral("veriticalJavaLayout"));
m_autodetectJavaCheckBox = new QCheckBox(m_autoJavaGroupBox);
m_autodetectJavaCheckBox->setObjectName("autodetectJavaCheckBox");
m_veriticalJavaLayout->addWidget(m_autodetectJavaCheckBox);
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
m_autodownloadCheckBox = new QCheckBox(m_autoJavaGroupBox);
m_autodownloadCheckBox->setObjectName("autodownloadCheckBox");
m_autodownloadCheckBox->setEnabled(false);
m_veriticalJavaLayout->addWidget(m_autodownloadCheckBox);
connect(m_autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] {
m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked());
if (!m_autodetectJavaCheckBox->isChecked())
m_autodownloadCheckBox->setChecked(false);
});
}
m_verticalLayout->addWidget(m_autoJavaGroupBox);
retranslate(); retranslate();
} }
@ -137,6 +177,19 @@ void JavaSettingsWidget::initialize()
m_maxMemSpinBox->setValue(observedMaxMemory); m_maxMemSpinBox->setValue(observedMaxMemory);
m_permGenSpinBox->setValue(observedPermGenMemory); m_permGenSpinBox->setValue(observedPermGenMemory);
updateThresholds(); updateThresholds();
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
auto button =
CustomMessageBox::selectable(this, tr("Automatic Java Download"),
tr("%1 can automatically download the correct Java version for each version of Minecraft..\n"
"Do you want to enable Java auto-download?\n")
.arg(BuildConfig.LAUNCHER_DISPLAYNAME),
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)
->exec();
auto checked = button == QMessageBox::Yes;
m_autodetectJavaCheckBox->setChecked(checked);
m_autodownloadCheckBox->setChecked(checked);
}
} }
void JavaSettingsWidget::refresh() void JavaSettingsWidget::refresh()
@ -153,21 +206,53 @@ JavaSettingsWidget::ValidationStatus JavaSettingsWidget::validate()
switch (javaStatus) { switch (javaStatus) {
default: default:
case JavaStatus::NotSet: case JavaStatus::NotSet:
/* fallthrough */
case JavaStatus::DoesNotExist: case JavaStatus::DoesNotExist:
/* fallthrough */
case JavaStatus::DoesNotStart: case JavaStatus::DoesNotStart:
/* fallthrough */
case JavaStatus::ReturnedInvalidData: { case JavaStatus::ReturnedInvalidData: {
int button = CustomMessageBox::selectable(this, tr("No Java version selected"), if (!(BuildConfig.JAVA_DOWNLOADER_ENABLED && m_autodownloadCheckBox->isChecked())) { // the java will not be autodownloaded
tr("You didn't select a Java version or selected something that doesn't work.\n" int button = QMessageBox::No;
if (m_result.mojangPlatform == "32" && maxHeapSize() > 2048) {
button = CustomMessageBox::selectable(
this, tr("32-bit Java detected"),
tr("You selected a 32-bit installation of Java, but allocated more than 2048MiB as maximum memory.\n"
"%1 will not be able to start Minecraft.\n" "%1 will not be able to start Minecraft.\n"
"Do you wish to proceed without any Java?" "Do you wish to proceed?"
"\n\n" "\n\n"
"You can change the Java version in the settings later.\n") "You can change the Java version in the settings later.\n")
.arg(BuildConfig.LAUNCHER_DISPLAYNAME), .arg(BuildConfig.LAUNCHER_DISPLAYNAME),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton) QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No | QMessageBox::Help, QMessageBox::NoButton)
->exec(); ->exec();
} else {
button = CustomMessageBox::selectable(this, tr("No Java version selected"),
tr("You either didn't select a Java version or selected one that does not work.\n"
"%1 will not be able to start Minecraft.\n"
"Do you wish to proceed without a functional version of Java?"
"\n\n"
"You can change the Java version in the settings later.\n")
.arg(BuildConfig.LAUNCHER_DISPLAYNAME),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No | QMessageBox::Help,
QMessageBox::NoButton)
->exec();
}
switch (button) {
case QMessageBox::Yes:
return ValidationStatus::JavaBad;
case QMessageBox::Help:
DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("java-wizard")));
/* fallthrough */
case QMessageBox::No:
/* fallthrough */
default:
return ValidationStatus::Bad;
}
if (button == QMessageBox::No) { if (button == QMessageBox::No) {
return ValidationStatus::Bad; return ValidationStatus::Bad;
} }
}
return ValidationStatus::JavaBad; return ValidationStatus::JavaBad;
} break; } break;
case JavaStatus::Pending: { case JavaStatus::Pending: {
@ -250,21 +335,22 @@ void JavaSettingsWidget::javaVersionSelected(BaseVersion::Ptr version)
void JavaSettingsWidget::on_javaBrowseBtn_clicked() void JavaSettingsWidget::on_javaBrowseBtn_clicked()
{ {
QString filter; auto filter = QString("Java (%1)").arg(JavaUtils::javaExecutable);
#if defined Q_OS_WIN32 auto raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable"), QString(), filter);
filter = "Java (javaw.exe)";
#else
filter = "Java (java)";
#endif
QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable"), QString(), filter);
if (raw_path.isEmpty()) { if (raw_path.isEmpty()) {
return; return;
} }
QString cooked_path = FS::NormalizePath(raw_path); auto cooked_path = FS::NormalizePath(raw_path);
m_javaPathTextBox->setText(cooked_path); m_javaPathTextBox->setText(cooked_path);
checkJavaPath(cooked_path); checkJavaPath(cooked_path);
} }
void JavaSettingsWidget::javaDownloadBtn_clicked()
{
auto jdialog = new Java::InstallDialog({}, nullptr, this);
jdialog->exec();
}
void JavaSettingsWidget::on_javaStatusBtn_clicked() void JavaSettingsWidget::on_javaStatusBtn_clicked()
{ {
QString text; QString text;
@ -359,30 +445,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(const 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;
} }
@ -403,6 +484,11 @@ void JavaSettingsWidget::retranslate()
m_minMemSpinBox->setToolTip(tr("The amount of memory Minecraft is started with.")); m_minMemSpinBox->setToolTip(tr("The amount of memory Minecraft is started with."));
m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes.")); m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes."));
m_javaBrowseBtn->setText(tr("Browse")); m_javaBrowseBtn->setText(tr("Browse"));
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
m_autodownloadCheckBox->setText(tr("Auto-download Mojang Java"));
}
m_autodetectJavaCheckBox->setText(tr("Autodetect Java version"));
m_autoJavaGroupBox->setTitle(tr("Autodetect Java"));
} }
void JavaSettingsWidget::updateThresholds() void JavaSettingsWidget::updateThresholds()
@ -418,6 +504,9 @@ void JavaSettingsWidget::updateThresholds()
} else if (observedMaxMemory < observedMinMemory) { } else if (observedMaxMemory < observedMinMemory) {
iconName = "status-yellow"; iconName = "status-yellow";
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value")); m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
} else if (observedMaxMemory > 2048 && m_result.is_64bit) {
iconName = "status-bad";
m_labelMaxMemIcon->setToolTip(tr("You are exceeding the maximum allocation supported by 32-bit installations of Java."));
} else { } else {
iconName = "status-good"; iconName = "status-good";
m_labelMaxMemIcon->setToolTip(""); m_labelMaxMemIcon->setToolTip("");
@ -430,3 +519,13 @@ void JavaSettingsWidget::updateThresholds()
m_labelMaxMemIcon->setPixmap(pix); m_labelMaxMemIcon->setPixmap(pix);
} }
} }
bool JavaSettingsWidget::autoDownloadJava() const
{
return m_autodownloadCheckBox && m_autodownloadCheckBox->isChecked();
}
bool JavaSettingsWidget::autoDetectJava() const
{
return m_autodetectJavaCheckBox->isChecked();
}

View File

@ -4,6 +4,7 @@
#include <BaseVersion.h> #include <BaseVersion.h>
#include <QObjectPtr.h> #include <QObjectPtr.h>
#include <java/JavaChecker.h> #include <java/JavaChecker.h>
#include <qcheckbox.h>
#include <QIcon> #include <QIcon>
class QLineEdit; class QLineEdit;
@ -25,7 +26,7 @@ class JavaSettingsWidget : public QWidget {
public: public:
explicit JavaSettingsWidget(QWidget* parent); explicit JavaSettingsWidget(QWidget* parent);
virtual ~JavaSettingsWidget() {}; virtual ~JavaSettingsWidget() = default;
enum class JavaStatus { NotSet, Pending, Good, DoesNotExist, DoesNotStart, ReturnedInvalidData } javaStatus = JavaStatus::NotSet; enum class JavaStatus { NotSet, Pending, Good, DoesNotExist, DoesNotStart, ReturnedInvalidData } javaStatus = JavaStatus::NotSet;
@ -41,6 +42,8 @@ class JavaSettingsWidget : public QWidget {
int minHeapSize() const; int minHeapSize() const;
int maxHeapSize() const; int maxHeapSize() const;
QString javaPath() const; QString javaPath() const;
bool autoDetectJava() const;
bool autoDownloadJava() const;
void updateThresholds(); void updateThresholds();
@ -50,7 +53,8 @@ class JavaSettingsWidget : public QWidget {
void javaVersionSelected(BaseVersion::Ptr version); void javaVersionSelected(BaseVersion::Ptr version);
void on_javaBrowseBtn_clicked(); void on_javaBrowseBtn_clicked();
void on_javaStatusBtn_clicked(); void on_javaStatusBtn_clicked();
void checkFinished(JavaCheckResult result); void javaDownloadBtn_clicked();
void checkFinished(const JavaChecker::Result& result);
protected: /* methods */ protected: /* methods */
void checkJavaPathOnEdit(const QString& path); void checkJavaPathOnEdit(const QString& path);
@ -76,15 +80,23 @@ class JavaSettingsWidget : public QWidget {
QSpinBox* m_minMemSpinBox = nullptr; QSpinBox* m_minMemSpinBox = nullptr;
QLabel* m_labelPermGen = nullptr; QLabel* m_labelPermGen = nullptr;
QSpinBox* m_permGenSpinBox = nullptr; QSpinBox* m_permGenSpinBox = nullptr;
QHBoxLayout* m_horizontalBtnLayout = nullptr;
QPushButton* m_javaDownloadBtn = nullptr;
QIcon goodIcon; QIcon goodIcon;
QIcon yellowIcon; QIcon yellowIcon;
QIcon badIcon; QIcon badIcon;
QGroupBox* m_autoJavaGroupBox = nullptr;
QVBoxLayout* m_veriticalJavaLayout = nullptr;
QCheckBox* m_autodetectJavaCheckBox = nullptr;
QCheckBox* m_autodownloadCheckBox = nullptr;
unsigned int observedMinMemory = 0; unsigned int observedMinMemory = 0;
unsigned int observedMaxMemory = 0; unsigned int observedMaxMemory = 0;
unsigned int observedPermGenMemory = 0; unsigned int observedPermGenMemory = 0;
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;
}; };