mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-04-30 06:34:27 +02:00
Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into metadata2
This commit is contained in:
commit
2a86ceb5d6
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@v3.0.2
|
||||
uses: korthout/backport-action@v3.1.0
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
pull_description: |-
|
||||
|
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@ -79,7 +79,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.1"
|
||||
qt_version: "6.7.2"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: windows-2022
|
||||
@ -90,7 +90,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: "win64_msvc2019_arm64"
|
||||
qt_version: "6.7.1"
|
||||
qt_version: "6.7.2"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: macos-12
|
||||
@ -99,7 +99,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.1"
|
||||
qt_version: "6.7.2"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: macos-12
|
||||
@ -160,7 +160,7 @@ jobs:
|
||||
|
||||
- name: Setup ccache
|
||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
with:
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||
|
||||
@ -266,23 +266,23 @@ jobs:
|
||||
- name: Configure CMake (macOS)
|
||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||
|
||||
- name: Configure CMake (macOS-Legacy)
|
||||
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||
|
||||
- name: Configure CMake (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
- name: Configure CMake (Windows MSVC)
|
||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||
if ("${{ env.CCACHE_VAR }}")
|
||||
{
|
||||
@ -297,7 +297,7 @@ jobs:
|
||||
- name: Configure CMake (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DENABLE_JAVA_DOWNLOADER=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
##
|
||||
# BUILD
|
||||
|
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v22
|
||||
- uses: DeterminateSystems/update-flake-lock@v23
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
|
@ -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_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
|
||||
if(UNIX AND APPLE)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||
|
@ -81,6 +81,9 @@ Config::Config()
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
|
||||
#cmakedefine01 ENABLE_JAVA_DOWNLOADER
|
||||
JAVA_DOWNLOADER_ENABLED = ENABLE_JAVA_DOWNLOADER;
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
GIT_TAG = "@Launcher_GIT_TAG@";
|
||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||
|
@ -67,6 +67,7 @@ class Config {
|
||||
QString VERSION_CHANNEL;
|
||||
|
||||
bool UPDATER_ENABLED = false;
|
||||
bool JAVA_DOWNLOADER_ENABLED = false;
|
||||
|
||||
/// A short string identifying this build's platform or distribution.
|
||||
QString BUILD_PLATFORM;
|
||||
|
18
flake.lock
generated
18
flake.lock
generated
@ -23,11 +23,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719994518,
|
||||
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
|
||||
"lastModified": 1722555600,
|
||||
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
|
||||
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -75,11 +75,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1720768451,
|
||||
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
|
||||
"lastModified": 1723637854,
|
||||
"narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
|
||||
"rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -103,11 +103,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720524665,
|
||||
"narHash": "sha256-ni/87oHPZm6Gv0ECYxr1f6uxB0UKBWJ6HvS7lwLU6oY=",
|
||||
"lastModified": 1723803910,
|
||||
"narHash": "sha256-yezvUuFiEnCFbGuwj/bQcqg7RykIEqudOy/RBrId0pc=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "8d6a17d0cdf411c55f12602624df6368ad86fac1",
|
||||
"rev": "bfef0ada09e2c8ac55bbcd0831bd0c9d42e651ba",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -44,10 +44,10 @@
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "DataMigrationTask.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "net/PasteUpload.h"
|
||||
#include "pathmatcher/MultiMatcher.h"
|
||||
#include "pathmatcher/SimplePrefixMatcher.h"
|
||||
#include "settings/INIFile.h"
|
||||
#include "tools/GenericProfiler.h"
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/MainWindow.h"
|
||||
@ -106,7 +106,7 @@
|
||||
#include "icons/IconList.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
|
||||
#include "java/JavaUtils.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
|
||||
#include "updater/ExternalUpdater.h"
|
||||
|
||||
@ -126,6 +126,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys.h>
|
||||
#include "SysInfo.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <dlfcn.h>
|
||||
@ -151,6 +152,7 @@
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include "WindowsConsole.h"
|
||||
#endif
|
||||
|
||||
@ -236,6 +238,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" },
|
||||
{ { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" },
|
||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
|
||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||
@ -250,6 +253,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
m_instanceIdToLaunch = parser.value("launch");
|
||||
m_serverToJoin = parser.value("server");
|
||||
m_worldToJoin = parser.value("world");
|
||||
m_profileToUse = parser.value("profile");
|
||||
m_liveCheck = parser.isSet("alive");
|
||||
|
||||
@ -265,7 +269,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
|
||||
if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
|
||||
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
@ -385,6 +389,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
launch.args["server"] = m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
launch.args["world"] = m_worldToJoin;
|
||||
}
|
||||
if (!m_profileToUse.isEmpty()) {
|
||||
launch.args["profile"] = m_profileToUse;
|
||||
@ -523,6 +529,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
qDebug() << "Address of server to join :" << m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
qDebug() << "Name of the world to join :" << m_worldToJoin;
|
||||
}
|
||||
qDebug() << "<> Paths set.";
|
||||
}
|
||||
@ -596,6 +604,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
m_settings->registerSetting("DownloadsDirWatchRecursive", false);
|
||||
m_settings->registerSetting("SkinsDir", "skins");
|
||||
m_settings->registerSetting("JavaDir", "java");
|
||||
|
||||
// Editors
|
||||
m_settings->registerSetting("JsonEditor", QString());
|
||||
@ -624,7 +633,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
// Memory
|
||||
m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512);
|
||||
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem());
|
||||
m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::suitableMaxMem());
|
||||
m_settings->registerSetting("PermGen", 128);
|
||||
|
||||
// Java Settings
|
||||
@ -638,6 +647,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("JvmArgs", "");
|
||||
m_settings->registerSetting("IgnoreJavaCompatibility", 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
|
||||
m_settings->registerSetting("OnlineFixes", false);
|
||||
@ -867,6 +879,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->addBase("java", QDir("cache/java").absolutePath());
|
||||
m_metacache->Load();
|
||||
qDebug() << "<> Cache initialized.";
|
||||
}
|
||||
@ -1157,14 +1170,17 @@ void Application::performMainStartupAction()
|
||||
if (!m_instanceIdToLaunch.isEmpty()) {
|
||||
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
||||
if (inst) {
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr;
|
||||
MinecraftTarget::Ptr targetToJoin = nullptr;
|
||||
MinecraftAccountPtr accountToUse = nullptr;
|
||||
|
||||
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
|
||||
if (!m_serverToJoin.isEmpty()) {
|
||||
// FIXME: validate the server string
|
||||
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin)));
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_serverToJoin, false)));
|
||||
qDebug() << " Launching with server" << m_serverToJoin;
|
||||
} else if (!m_worldToJoin.isEmpty()) {
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_worldToJoin, true)));
|
||||
qDebug() << " Launching with world" << m_worldToJoin;
|
||||
}
|
||||
|
||||
if (!m_profileToUse.isEmpty()) {
|
||||
@ -1175,7 +1191,7 @@ void Application::performMainStartupAction()
|
||||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, serverToJoin, accountToUse);
|
||||
launch(inst, true, false, targetToJoin, accountToUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1265,6 +1281,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||
} else if (command == "launch") {
|
||||
QString id = received.args["id"];
|
||||
QString server = received.args["server"];
|
||||
QString world = received.args["world"];
|
||||
QString profile = received.args["profile"];
|
||||
|
||||
InstancePtr instance;
|
||||
@ -1279,11 +1296,12 @@ void Application::messageReceived(const QByteArray& message)
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftServerTargetPtr serverObject = nullptr;
|
||||
MinecraftTarget::Ptr serverObject = nullptr;
|
||||
if (!server.isEmpty()) {
|
||||
serverObject = std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(server));
|
||||
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(server, false));
|
||||
} else if (!world.isEmpty()) {
|
||||
serverObject = std::make_shared<MinecraftTarget>(MinecraftTarget::parse(world, true));
|
||||
}
|
||||
|
||||
MinecraftAccountPtr accountObject;
|
||||
if (!profile.isEmpty()) {
|
||||
accountObject = accounts()->getAccountByProfileName(profile);
|
||||
@ -1332,11 +1350,7 @@ bool Application::openJsonEditor(const QString& filename)
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::launch(InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
MinecraftServerTargetPtr serverToJoin,
|
||||
MinecraftAccountPtr accountToUse)
|
||||
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
|
||||
{
|
||||
if (m_updateRunning) {
|
||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||
@ -1354,7 +1368,7 @@ bool Application::launch(InstancePtr instance,
|
||||
controller->setOnline(online);
|
||||
controller->setDemo(demo);
|
||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||
controller->setServerToJoin(serverToJoin);
|
||||
controller->setTargetToJoin(targetToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
if (window) {
|
||||
controller->setParentWidget(window);
|
||||
@ -1733,20 +1747,6 @@ QString Application::getUserAgentUncached()
|
||||
return BuildConfig.USER_AGENT_UNCACHED;
|
||||
}
|
||||
|
||||
int Application::suitableMaxMem()
|
||||
{
|
||||
float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte;
|
||||
int maxMemoryAlloc;
|
||||
|
||||
// If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB
|
||||
if (totalRAM < (4096 * 1.5))
|
||||
maxMemoryAlloc = (int)(totalRAM / 1.5);
|
||||
else
|
||||
maxMemoryAlloc = 4096;
|
||||
|
||||
return maxMemoryAlloc;
|
||||
}
|
||||
|
||||
bool Application::handleDataMigration(const QString& currentData,
|
||||
const QString& oldData,
|
||||
const QString& name,
|
||||
@ -1853,3 +1853,7 @@ QUrl Application::normalizeImportUrl(QString const& url)
|
||||
return QUrl::fromUserInput(url);
|
||||
}
|
||||
}
|
||||
const QString Application::javaPath()
|
||||
{
|
||||
return m_settings->get("JavaDir").toString();
|
||||
}
|
||||
|
@ -47,7 +47,7 @@
|
||||
|
||||
#include <BaseInstance.h>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class LaunchController;
|
||||
class LocalPeer;
|
||||
@ -161,6 +161,9 @@ class Application : public QApplication {
|
||||
/// the data path the application is using
|
||||
const QString& dataRoot() { return m_dataPath; }
|
||||
|
||||
/// the java installed path the application is using
|
||||
const QString javaPath();
|
||||
|
||||
bool isPortable() { return m_portable; }
|
||||
|
||||
const Capabilities capabilities() { return m_capabilities; }
|
||||
@ -179,8 +182,6 @@ class Application : public QApplication {
|
||||
|
||||
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
bool updaterEnabled();
|
||||
QString updaterBinaryName();
|
||||
|
||||
@ -202,7 +203,7 @@ class Application : public QApplication {
|
||||
bool launch(InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftTarget::Ptr targetToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
bool kill(InstancePtr instance);
|
||||
void closeCurrentWindow();
|
||||
@ -290,6 +291,7 @@ class Application : public QApplication {
|
||||
QString m_detectedOpenALPath;
|
||||
QString m_instanceIdToLaunch;
|
||||
QString m_serverToJoin;
|
||||
QString m_worldToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_liveCheck = false;
|
||||
QList<QUrl> m_urlsToImport;
|
||||
|
@ -56,7 +56,7 @@
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "RuntimeContext.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
@ -184,7 +184,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||
|
||||
/// returns the current launch task (if any)
|
||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||
@ -256,7 +256,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
/**
|
||||
* 'print' a verbose description of the instance into a QStringList
|
||||
*/
|
||||
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) = 0;
|
||||
|
||||
Status currentStatus() const;
|
||||
|
||||
|
@ -78,6 +78,14 @@ QVariant BaseVersionList::data(const QModelIndex& index, int role) const
|
||||
case TypeRole:
|
||||
return version->typeString();
|
||||
|
||||
case JavaMajorRole: {
|
||||
auto major = version->name();
|
||||
if (major.startsWith("java")) {
|
||||
major = "Java " + major.mid(4);
|
||||
}
|
||||
return major;
|
||||
}
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -110,6 +118,8 @@ QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||
roles.insert(TypeRole, "type");
|
||||
roles.insert(BranchRole, "branch");
|
||||
roles.insert(PathRole, "path");
|
||||
roles.insert(ArchitectureRole, "architecture");
|
||||
roles.insert(JavaNameRole, "javaName");
|
||||
roles.insert(CPUArchitectureRole, "architecture");
|
||||
roles.insert(JavaMajorRole, "javaMajor");
|
||||
return roles;
|
||||
}
|
||||
|
@ -48,7 +48,9 @@ class BaseVersionList : public QAbstractListModel {
|
||||
TypeRole,
|
||||
BranchRole,
|
||||
PathRole,
|
||||
ArchitectureRole,
|
||||
JavaNameRole,
|
||||
JavaMajorRole,
|
||||
CPUArchitectureRole,
|
||||
SortRole
|
||||
};
|
||||
using RoleList = QList<int>;
|
||||
|
@ -24,6 +24,8 @@ set(CORE_SOURCES
|
||||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
Untar.h
|
||||
Untar.cpp
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
QVariantUtils.h
|
||||
@ -262,8 +264,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/launch/ExtractNatives.h
|
||||
minecraft/launch/LauncherPartLaunch.cpp
|
||||
minecraft/launch/LauncherPartLaunch.h
|
||||
minecraft/launch/MinecraftServerTarget.cpp
|
||||
minecraft/launch/MinecraftServerTarget.h
|
||||
minecraft/launch/MinecraftTarget.cpp
|
||||
minecraft/launch/MinecraftTarget.h
|
||||
minecraft/launch/PrintInstanceInfo.cpp
|
||||
minecraft/launch/PrintInstanceInfo.h
|
||||
minecraft/launch/ReconstructAssets.cpp
|
||||
@ -272,6 +274,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/launch/ScanModFolders.h
|
||||
minecraft/launch/VerifyJavaInstall.cpp
|
||||
minecraft/launch/VerifyJavaInstall.h
|
||||
minecraft/launch/AutoInstallJava.cpp
|
||||
minecraft/launch/AutoInstallJava.h
|
||||
|
||||
minecraft/GradleSpecifier.h
|
||||
minecraft/MinecraftInstance.cpp
|
||||
@ -417,8 +421,6 @@ set(SETTINGS_SOURCES
|
||||
set(JAVA_SOURCES
|
||||
java/JavaChecker.h
|
||||
java/JavaChecker.cpp
|
||||
java/JavaCheckerJob.h
|
||||
java/JavaCheckerJob.cpp
|
||||
java/JavaInstall.h
|
||||
java/JavaInstall.cpp
|
||||
java/JavaInstallList.h
|
||||
@ -427,6 +429,18 @@ set(JAVA_SOURCES
|
||||
java/JavaUtils.cpp
|
||||
java/JavaVersion.h
|
||||
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
|
||||
@ -746,6 +760,8 @@ SET(LAUNCHER_SOURCES
|
||||
DataMigrationTask.cpp
|
||||
ApplicationMessage.h
|
||||
ApplicationMessage.cpp
|
||||
SysInfo.h
|
||||
SysInfo.cpp
|
||||
|
||||
# GUI - general utilities
|
||||
DesktopServices.h
|
||||
@ -784,8 +800,6 @@ SET(LAUNCHER_SOURCES
|
||||
# GUI - windows
|
||||
ui/GuiUtil.h
|
||||
ui/GuiUtil.cpp
|
||||
ui/ColorCache.h
|
||||
ui/ColorCache.cpp
|
||||
ui/MainWindow.h
|
||||
ui/MainWindow.cpp
|
||||
ui/InstanceWindow.h
|
||||
|
@ -276,6 +276,9 @@ bool ensureFolderPathExists(const QFileInfo folderPath)
|
||||
{
|
||||
QDir dir;
|
||||
QString ensuredPath = folderPath.filePath();
|
||||
if (folderPath.exists())
|
||||
return true;
|
||||
|
||||
bool success = dir.mkpath(ensuredPath);
|
||||
return success;
|
||||
}
|
||||
|
@ -1,16 +1,12 @@
|
||||
#include "Filter.h"
|
||||
|
||||
Filter::~Filter() {}
|
||||
|
||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
|
||||
ContainsFilter::~ContainsFilter() {}
|
||||
bool ContainsFilter::accepts(const QString& value)
|
||||
{
|
||||
return value.contains(pattern);
|
||||
}
|
||||
|
||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
|
||||
ExactFilter::~ExactFilter() {}
|
||||
bool ExactFilter::accepts(const QString& value)
|
||||
{
|
||||
return value == pattern;
|
||||
@ -27,10 +23,15 @@ RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||
pattern.setPattern(regexp);
|
||||
pattern.optimize();
|
||||
}
|
||||
RegexpFilter::~RegexpFilter() {}
|
||||
bool RegexpFilter::accepts(const QString& value)
|
||||
{
|
||||
auto match = pattern.match(value);
|
||||
bool matched = match.hasMatch();
|
||||
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);
|
||||
}
|
@ -5,14 +5,14 @@
|
||||
|
||||
class Filter {
|
||||
public:
|
||||
virtual ~Filter();
|
||||
virtual ~Filter() = default;
|
||||
virtual bool accepts(const QString& value) = 0;
|
||||
};
|
||||
|
||||
class ContainsFilter : public Filter {
|
||||
public:
|
||||
ContainsFilter(const QString& pattern);
|
||||
virtual ~ContainsFilter();
|
||||
virtual ~ContainsFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
@ -22,7 +22,7 @@ class ContainsFilter : public Filter {
|
||||
class ExactFilter : public Filter {
|
||||
public:
|
||||
ExactFilter(const QString& pattern);
|
||||
virtual ~ExactFilter();
|
||||
virtual ~ExactFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
@ -32,7 +32,7 @@ class ExactFilter : public Filter {
|
||||
class ExactIfPresentFilter : public Filter {
|
||||
public:
|
||||
ExactIfPresentFilter(const QString& pattern);
|
||||
~ExactIfPresentFilter() override = default;
|
||||
virtual ~ExactIfPresentFilter() override = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
@ -42,10 +42,20 @@ class ExactIfPresentFilter : public Filter {
|
||||
class RegexpFilter : public Filter {
|
||||
public:
|
||||
RegexpFilter(const QString& regexp, bool invert);
|
||||
virtual ~RegexpFilter();
|
||||
virtual ~RegexpFilter() = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QRegularExpression pattern;
|
||||
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;
|
||||
};
|
||||
|
@ -22,7 +22,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||
public:
|
||||
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
||||
|
||||
virtual ~InstancePageProvider() {};
|
||||
virtual ~InstancePageProvider() = default;
|
||||
virtual QList<BasePage*> getPages() override
|
||||
{
|
||||
QList<BasePage*> values;
|
||||
@ -39,7 +39,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
||||
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
||||
values.append(new NotesPage(onesix.get()));
|
||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||
values.append(new WorldListPage(onesix, onesix->worldList()));
|
||||
values.append(new ServersPage(onesix));
|
||||
// values.append(new GameOptionsPage(onesix.get()));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||
|
@ -63,7 +63,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaChecker::Result& result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr(
|
||||
@ -79,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result)
|
||||
{
|
||||
auto htmlError = result.errorLog;
|
||||
QString text;
|
||||
@ -89,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result)
|
||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr(
|
||||
@ -116,34 +116,26 @@ void JavaCommon::TestCheck::run()
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0, this));
|
||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
||||
checker->m_path = m_path;
|
||||
checker->performCheck();
|
||||
checker->start();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||
void JavaCommon::TestCheck::checkFinished(const JavaChecker::Result& result)
|
||||
{
|
||||
if (result.validity != JavaCheckResult::Validity::Valid) {
|
||||
if (result.validity != JavaChecker::Result::Validity::Valid) {
|
||||
javaBinaryWasBad(m_parent, result);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0, this));
|
||||
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
||||
checker->m_path = m_path;
|
||||
checker->m_args = m_args;
|
||||
checker->m_minMem = m_minMem;
|
||||
checker->m_maxMem = m_maxMem;
|
||||
if (result.javaVersion.requiresPermGen()) {
|
||||
checker->m_permGen = m_permGen;
|
||||
}
|
||||
checker->performCheck();
|
||||
checker->start();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
||||
void JavaCommon::TestCheck::checkFinishedWithArgs(const JavaChecker::Result& result)
|
||||
{
|
||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
||||
if (result.validity == JavaChecker::Result::Validity::Valid) {
|
||||
javaWasOk(m_parent, result);
|
||||
emit finished();
|
||||
return;
|
||||
|
@ -10,11 +10,11 @@ namespace JavaCommon {
|
||||
bool checkJVMArgs(QString args, QWidget* parent);
|
||||
|
||||
// Show a dialog saying that the Java binary was usable
|
||||
void javaWasOk(QWidget* parent, const JavaCheckResult& result);
|
||||
void javaWasOk(QWidget* parent, const JavaChecker::Result& result);
|
||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||
void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result);
|
||||
void javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result);
|
||||
// Show a dialog saying that the Java binary was not usable
|
||||
void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result);
|
||||
void javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result);
|
||||
// Show a dialog if we couldn't find Java Checker
|
||||
void javaCheckNotFound(QWidget* parent);
|
||||
|
||||
@ -32,11 +32,11 @@ class TestCheck : public QObject {
|
||||
void finished();
|
||||
|
||||
private slots:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinishedWithArgs(JavaCheckResult result);
|
||||
void checkFinished(const JavaChecker::Result& result);
|
||||
void checkFinishedWithArgs(const JavaChecker::Result& result);
|
||||
|
||||
private:
|
||||
std::shared_ptr<JavaChecker> checker;
|
||||
JavaChecker::Ptr checker;
|
||||
QWidget* m_parent = nullptr;
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
|
@ -324,7 +324,7 @@ void LaunchController::launchInstance()
|
||||
return;
|
||||
}
|
||||
|
||||
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
|
||||
m_launcher = m_instance->createLaunchTask(m_session, m_targetToJoin);
|
||||
if (!m_launcher) {
|
||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||
return;
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class InstanceWindow;
|
||||
class LaunchController : public Task {
|
||||
@ -48,7 +48,7 @@ class LaunchController : public Task {
|
||||
void executeTask() override;
|
||||
|
||||
LaunchController(QObject* parent = nullptr);
|
||||
virtual ~LaunchController() {};
|
||||
virtual ~LaunchController() = default;
|
||||
|
||||
void setInstance(InstancePtr instance) { m_instance = instance; }
|
||||
|
||||
@ -62,7 +62,7 @@ class LaunchController : public Task {
|
||||
|
||||
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
|
||||
|
||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
|
||||
void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); }
|
||||
|
||||
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
|
||||
|
||||
@ -94,5 +94,5 @@ class LaunchController : public Task {
|
||||
MinecraftAccountPtr m_accountToUse = nullptr;
|
||||
AuthSessionPtr m_session;
|
||||
shared_qobject_ptr<LaunchTask> m_launcher;
|
||||
MinecraftServerTargetPtr m_serverToJoin;
|
||||
MinecraftTarget::Ptr m_targetToJoin;
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -536,6 +536,10 @@ bool ExportToZipTask::abort()
|
||||
|
||||
void ExtractZipTask::executeTask()
|
||||
{
|
||||
if (!m_input->isOpen() && !m_input->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied zip file."));
|
||||
return;
|
||||
}
|
||||
m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); });
|
||||
connect(&m_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExtractZipTask::finish);
|
||||
m_zip_watcher.setFuture(m_zip_future);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* 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 {
|
||||
public:
|
||||
ExtractZipTask(QString input, QDir outputDir, QString subdirectory = "")
|
||||
: ExtractZipTask(std::make_shared<QuaZip>(input), outputDir, subdirectory)
|
||||
{}
|
||||
ExtractZipTask(std::shared_ptr<QuaZip> input, QDir outputDir, QString subdirectory = "")
|
||||
: m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory)
|
||||
{}
|
||||
|
@ -40,8 +40,8 @@ namespace MangoHud {
|
||||
|
||||
QString getLibraryString()
|
||||
{
|
||||
/*
|
||||
* Check for vulkan layers in this order:
|
||||
/**
|
||||
* Guess MangoHud install location by searching for vulkan layers in this order:
|
||||
*
|
||||
* $VK_LAYER_PATH
|
||||
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
|
||||
@ -49,8 +49,9 @@ QString getLibraryString()
|
||||
* /etc
|
||||
* $XDG_CONFIG_DIRS (/etc/xdg)
|
||||
* $XDG_CONFIG_HOME (~/.config)
|
||||
*
|
||||
* @returns Absolute path of libMangoHud.so if found and empty QString otherwise.
|
||||
*/
|
||||
|
||||
QStringList vkLayerList;
|
||||
{
|
||||
QString home = QDir::homePath();
|
||||
@ -85,7 +86,7 @@ QString getLibraryString()
|
||||
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
|
||||
}
|
||||
|
||||
for (QString vkLayer : vkLayerList) {
|
||||
for (const QString& vkLayer : vkLayerList) {
|
||||
// prefer to use architecture specific vulkan layers
|
||||
QString currentArch = QSysInfo::currentCpuArchitecture();
|
||||
|
||||
@ -95,8 +96,8 @@ QString getLibraryString()
|
||||
|
||||
QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" };
|
||||
|
||||
QString filePath = "";
|
||||
for (QString manifestName : manifestNames) {
|
||||
QString filePath{};
|
||||
for (const QString& manifestName : manifestNames) {
|
||||
QString tryPath = FS::PathCombine(vkLayer, manifestName);
|
||||
if (QFile::exists(tryPath)) {
|
||||
filePath = tryPath;
|
||||
@ -111,10 +112,23 @@ QString getLibraryString()
|
||||
auto conf = Json::requireDocument(filePath, vkLayer);
|
||||
auto confObject = Json::requireObject(conf, vkLayer);
|
||||
auto layer = Json::ensureObject(confObject, "layer");
|
||||
return Json::ensureString(layer, "library_path");
|
||||
QString libraryName = Json::ensureString(layer, "library_path");
|
||||
|
||||
#ifdef __GLIBC__
|
||||
// Check whether mangohud is usable on a glibc based system
|
||||
if (!libraryName.isEmpty()) {
|
||||
QString libraryPath = findLibrary(libraryName);
|
||||
if (!libraryPath.isEmpty()) {
|
||||
return libraryPath;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Without glibc return recorded shared library as-is.
|
||||
return libraryName;
|
||||
#endif
|
||||
}
|
||||
|
||||
return QString();
|
||||
return {};
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName)
|
||||
|
@ -46,13 +46,13 @@ class NullInstance : public BaseInstance {
|
||||
{
|
||||
setVersionBroken(true);
|
||||
}
|
||||
virtual ~NullInstance() {};
|
||||
virtual ~NullInstance() = default;
|
||||
void saveNow() override {}
|
||||
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
|
||||
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
|
||||
QSet<QString> traits() const override { return {}; };
|
||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||
@ -64,7 +64,7 @@ class NullInstance : public BaseInstance {
|
||||
bool canEdit() const override { return false; }
|
||||
bool canLaunch() const override { return false; }
|
||||
void populateLaunchMenu(QMenu* menu) override {}
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override
|
||||
{
|
||||
QStringList out;
|
||||
out << "Null instance - placeholder.";
|
||||
|
99
launcher/SysInfo.cpp
Normal file
99
launcher/SysInfo.cpp
Normal 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
8
launcher/SysInfo.h
Normal 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
260
launcher/Untar.cpp
Normal 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
46
launcher/Untar.h
Normal 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);
|
||||
}
|
@ -114,10 +114,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
||||
return tr("Branch");
|
||||
case Type:
|
||||
return tr("Type");
|
||||
case Architecture:
|
||||
case CPUArchitecture:
|
||||
return tr("Architecture");
|
||||
case Path:
|
||||
return tr("Path");
|
||||
case JavaName:
|
||||
return tr("Java Name");
|
||||
case JavaMajor:
|
||||
return tr("Major Version");
|
||||
case Time:
|
||||
return tr("Released");
|
||||
}
|
||||
@ -131,10 +135,14 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
||||
return tr("The version's branch");
|
||||
case Type:
|
||||
return tr("The version's type");
|
||||
case Architecture:
|
||||
case CPUArchitecture:
|
||||
return tr("CPU Architecture");
|
||||
case Path:
|
||||
return tr("Filesystem path to this version");
|
||||
case JavaName:
|
||||
return tr("The alternative name of the java version");
|
||||
case JavaMajor:
|
||||
return tr("The java major version");
|
||||
case Time:
|
||||
return tr("Release date of this version");
|
||||
}
|
||||
@ -165,10 +173,14 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
|
||||
case Type:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
|
||||
case Architecture:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole);
|
||||
case CPUArchitecture:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole);
|
||||
case Path:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
|
||||
case JavaName:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole);
|
||||
case JavaMajor:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole);
|
||||
case Time:
|
||||
return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate();
|
||||
default:
|
||||
@ -308,12 +320,18 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
||||
m_columns.push_back(ParentVersion);
|
||||
}
|
||||
*/
|
||||
if (roles.contains(BaseVersionList::ArchitectureRole)) {
|
||||
m_columns.push_back(Architecture);
|
||||
if (roles.contains(BaseVersionList::CPUArchitectureRole)) {
|
||||
m_columns.push_back(CPUArchitecture);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::PathRole)) {
|
||||
m_columns.push_back(Path);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::JavaNameRole)) {
|
||||
m_columns.push_back(JavaName);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::JavaMajorRole)) {
|
||||
m_columns.push_back(JavaMajor);
|
||||
}
|
||||
if (roles.contains(Meta::VersionList::TimeRole)) {
|
||||
m_columns.push_back(Time);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class VersionFilterModel;
|
||||
class VersionProxyModel : public QAbstractProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Column { Name, ParentVersion, Branch, Type, Architecture, Path, Time };
|
||||
enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor };
|
||||
using FilterMap = QHash<BaseVersionList::ModelRoles, std::shared_ptr<Filter>>;
|
||||
|
||||
public:
|
||||
|
@ -40,14 +40,15 @@
|
||||
#include <QMap>
|
||||
#include <QProcess>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Commandline.h"
|
||||
#include "FileSystem.h"
|
||||
#include "JavaUtils.h"
|
||||
#include "java/JavaUtils.h"
|
||||
|
||||
JavaChecker::JavaChecker(QObject* parent) : QObject(parent) {}
|
||||
JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id, QObject* parent)
|
||||
: Task(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id)
|
||||
{}
|
||||
|
||||
void JavaChecker::performCheck()
|
||||
void JavaChecker::executeTask()
|
||||
{
|
||||
QString checkerJar = JavaUtils::getJavaCheckPath();
|
||||
|
||||
@ -72,7 +73,7 @@ void JavaChecker::performCheck()
|
||||
if (m_maxMem != 0) {
|
||||
args << QString("-Xmx%1m").arg(m_maxMem);
|
||||
}
|
||||
if (m_permGen != 64) {
|
||||
if (m_permGen != 64 && m_permGen != 0) {
|
||||
args << QString("-XX:PermSize=%1m").arg(m_permGen);
|
||||
}
|
||||
|
||||
@ -115,11 +116,10 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
QProcessPtr _process = process;
|
||||
process.reset();
|
||||
|
||||
JavaCheckResult result;
|
||||
{
|
||||
result.path = m_path;
|
||||
result.id = m_id;
|
||||
}
|
||||
Result result = {
|
||||
m_path,
|
||||
m_id,
|
||||
};
|
||||
result.errorLog = m_stderr;
|
||||
result.outLog = m_stdout;
|
||||
qDebug() << "STDOUT" << m_stdout;
|
||||
@ -127,8 +127,9 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
qDebug() << "Java checker finished with status" << status << "exit code" << exitcode;
|
||||
|
||||
if (status == QProcess::CrashExit || exitcode == 1) {
|
||||
result.validity = JavaCheckResult::Validity::Errored;
|
||||
result.validity = Result::Validity::Errored;
|
||||
emit checkFinished(result);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
result.validity = JavaCheckResult::Validity::ReturnedInvalidData;
|
||||
result.validity = Result::Validity::ReturnedInvalidData;
|
||||
emit checkFinished(result);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -171,7 +173,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
auto java_vendor = results["java.vendor"];
|
||||
bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64";
|
||||
|
||||
result.validity = JavaCheckResult::Validity::Valid;
|
||||
result.validity = Result::Validity::Valid;
|
||||
result.is_64bit = is_64;
|
||||
result.mojangPlatform = is_64 ? "64" : "32";
|
||||
result.realPlatform = os_arch;
|
||||
@ -179,6 +181,7 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
|
||||
result.javaVendor = java_vendor;
|
||||
qDebug() << "Java checker succeeded.";
|
||||
emit checkFinished(result);
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void JavaChecker::error(QProcess::ProcessError err)
|
||||
@ -190,15 +193,9 @@ void JavaChecker::error(QProcess::ProcessError err)
|
||||
qDebug() << "Native environment:";
|
||||
qDebug() << QProcessEnvironment::systemEnvironment().toStringList();
|
||||
killTimer.stop();
|
||||
JavaCheckResult result;
|
||||
{
|
||||
result.path = m_path;
|
||||
result.id = m_id;
|
||||
}
|
||||
|
||||
emit checkFinished(result);
|
||||
return;
|
||||
emit checkFinished({ m_path, m_id });
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void JavaChecker::timeout()
|
||||
|
@ -3,14 +3,19 @@
|
||||
#include <QTimer>
|
||||
#include <memory>
|
||||
|
||||
#include "QObjectPtr.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;
|
||||
int id;
|
||||
QString mojangPlatform;
|
||||
QString realPlatform;
|
||||
JavaVersion javaVersion;
|
||||
@ -18,34 +23,31 @@ struct JavaCheckResult {
|
||||
QString outLog;
|
||||
QString errorLog;
|
||||
bool is_64bit = false;
|
||||
int id;
|
||||
enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored;
|
||||
};
|
||||
};
|
||||
|
||||
using QProcessPtr = shared_qobject_ptr<QProcess>;
|
||||
using JavaCheckerPtr = shared_qobject_ptr<JavaChecker>;
|
||||
class JavaChecker : public QObject {
|
||||
Q_OBJECT
|
||||
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;
|
||||
explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0, QObject* parent = 0);
|
||||
|
||||
signals:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinished(const Result& result);
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
QProcessPtr process;
|
||||
QTimer killTimer;
|
||||
QString m_stdout;
|
||||
QString m_stderr;
|
||||
public slots:
|
||||
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
int m_id = 0;
|
||||
|
||||
private slots:
|
||||
void timeout();
|
||||
void finished(int exitcode, QProcess::ExitStatus);
|
||||
void error(QProcess::ProcessError);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -40,6 +40,7 @@ struct JavaInstall : public BaseVersion {
|
||||
QString arch;
|
||||
QString path;
|
||||
bool recommended = false;
|
||||
bool is_64bit = false;
|
||||
};
|
||||
|
||||
using JavaInstallPtr = std::shared_ptr<JavaInstall>;
|
||||
|
@ -38,13 +38,17 @@
|
||||
#include <QtXml>
|
||||
|
||||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
|
||||
#include "java/JavaCheckerJob.h"
|
||||
#include "Application.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
|
||||
JavaInstallList::JavaInstallList(QObject* parent) : BaseVersionList(parent) {}
|
||||
JavaInstallList::JavaInstallList(QObject* parent, bool onlyManagedVersions)
|
||||
: BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions)
|
||||
{}
|
||||
|
||||
Task::Ptr JavaInstallList::getLoadTask()
|
||||
{
|
||||
@ -55,7 +59,7 @@ Task::Ptr JavaInstallList::getLoadTask()
|
||||
Task::Ptr JavaInstallList::getCurrentTask()
|
||||
{
|
||||
if (m_status == Status::InProgress) {
|
||||
return m_loadTask;
|
||||
return m_load_task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -64,8 +68,8 @@ void JavaInstallList::load()
|
||||
{
|
||||
if (m_status != Status::InProgress) {
|
||||
m_status = Status::InProgress;
|
||||
m_loadTask.reset(new JavaListLoadTask(this));
|
||||
m_loadTask->start();
|
||||
m_load_task.reset(new JavaListLoadTask(this, m_only_managed_versions));
|
||||
m_load_task->start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +110,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
|
||||
return version->recommended;
|
||||
case PathRole:
|
||||
return version->path;
|
||||
case ArchitectureRole:
|
||||
case CPUArchitectureRole:
|
||||
return version->arch;
|
||||
default:
|
||||
return QVariant();
|
||||
@ -115,7 +119,7 @@ QVariant JavaInstallList::data(const QModelIndex& index, int role) const
|
||||
|
||||
BaseVersionList::RoleList JavaInstallList::providesRoles() const
|
||||
{
|
||||
return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole };
|
||||
return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, CPUArchitectureRole };
|
||||
}
|
||||
|
||||
void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
|
||||
@ -129,7 +133,7 @@ void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)
|
||||
}
|
||||
endResetModel();
|
||||
m_status = Status::Done;
|
||||
m_loadTask.reset();
|
||||
m_load_task.reset();
|
||||
}
|
||||
|
||||
bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right)
|
||||
@ -146,35 +150,30 @@ void JavaInstallList::sortVersions()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist) : Task()
|
||||
JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions) : Task(), m_only_managed_versions(onlyManagedVersions)
|
||||
{
|
||||
m_list = vlist;
|
||||
m_currentRecommended = NULL;
|
||||
m_current_recommended = NULL;
|
||||
}
|
||||
|
||||
JavaListLoadTask::~JavaListLoadTask() {}
|
||||
|
||||
void JavaListLoadTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Detecting Java installations..."));
|
||||
|
||||
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::progress, this, &Task::setProgress);
|
||||
|
||||
qDebug() << "Probing the following Java paths: ";
|
||||
int id = 0;
|
||||
for (QString candidate : candidate_paths) {
|
||||
qDebug() << " " << candidate;
|
||||
|
||||
auto candidate_checker = new JavaChecker();
|
||||
candidate_checker->m_path = candidate;
|
||||
candidate_checker->m_id = id;
|
||||
m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
|
||||
|
||||
auto checker = new JavaChecker(candidate, "", 0, 0, 0, id, this);
|
||||
connect(checker, &JavaChecker::checkFinished, [this](const JavaChecker::Result& result) { m_results << result; });
|
||||
job->addTask(Task::Ptr(checker));
|
||||
id++;
|
||||
}
|
||||
|
||||
@ -184,16 +183,17 @@ void JavaListLoadTask::executeTask()
|
||||
void JavaListLoadTask::javaCheckerFinished()
|
||||
{
|
||||
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:";
|
||||
for (JavaCheckResult result : results) {
|
||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
||||
for (auto result : m_results) {
|
||||
if (result.validity == JavaChecker::Result::Validity::Valid) {
|
||||
JavaInstallPtr javaVersion(new JavaInstall());
|
||||
|
||||
javaVersion->id = result.javaVersion;
|
||||
javaVersion->arch = result.realPlatform;
|
||||
javaVersion->path = result.path;
|
||||
javaVersion->is_64bit = result.is_64bit;
|
||||
candidates.append(javaVersion);
|
||||
|
||||
qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path;
|
||||
|
@ -19,9 +19,9 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "BaseVersionList.h"
|
||||
#include "java/JavaChecker.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "JavaCheckerJob.h"
|
||||
#include "JavaInstall.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
@ -33,9 +33,9 @@ class JavaInstallList : public BaseVersionList {
|
||||
enum class Status { NotDone, InProgress, Done };
|
||||
|
||||
public:
|
||||
explicit JavaInstallList(QObject* parent = 0);
|
||||
explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false);
|
||||
|
||||
Task::Ptr getLoadTask() override;
|
||||
[[nodiscard]] Task::Ptr getLoadTask() override;
|
||||
bool isLoaded() override;
|
||||
const BaseVersion::Ptr at(int i) const override;
|
||||
int count() const override;
|
||||
@ -53,23 +53,27 @@ class JavaInstallList : public BaseVersionList {
|
||||
|
||||
protected:
|
||||
Status m_status = Status::NotDone;
|
||||
shared_qobject_ptr<JavaListLoadTask> m_loadTask;
|
||||
shared_qobject_ptr<JavaListLoadTask> m_load_task;
|
||||
QList<BaseVersion::Ptr> m_vlist;
|
||||
bool m_only_managed_versions;
|
||||
};
|
||||
|
||||
class JavaListLoadTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit JavaListLoadTask(JavaInstallList* vlist);
|
||||
virtual ~JavaListLoadTask();
|
||||
explicit JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions = false);
|
||||
virtual ~JavaListLoadTask() = default;
|
||||
|
||||
protected:
|
||||
void executeTask() override;
|
||||
public slots:
|
||||
void javaCheckerFinished();
|
||||
|
||||
protected:
|
||||
shared_qobject_ptr<JavaCheckerJob> m_job;
|
||||
Task::Ptr m_job;
|
||||
JavaInstallList* m_list;
|
||||
JavaInstall* m_currentRecommended;
|
||||
JavaInstall* m_current_recommended;
|
||||
QList<JavaChecker::Result> m_results;
|
||||
bool m_only_managed_versions;
|
||||
};
|
||||
|
128
launcher/java/JavaMetadata.cpp
Normal file
128
launcher/java/JavaMetadata.cpp
Normal 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
|
64
launcher/java/JavaMetadata.h
Normal file
64
launcher/java/JavaMetadata.h
Normal 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
|
@ -182,8 +182,9 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
|
||||
else if (keyType == KEY_WOW64_32KEY)
|
||||
archType = "32";
|
||||
|
||||
for (HKEY baseRegistry : { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }) {
|
||||
HKEY jreKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) ==
|
||||
if (RegOpenKeyExW(baseRegistry, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) ==
|
||||
ERROR_SUCCESS) {
|
||||
// Read the current type version from the registry.
|
||||
// This will be used to find any key that contains the JavaHome value.
|
||||
@ -205,7 +206,7 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
|
||||
QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix;
|
||||
|
||||
HKEY newKey;
|
||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
|
||||
if (RegOpenKeyExW(baseRegistry, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) ==
|
||||
ERROR_SUCCESS) {
|
||||
// Read the JavaHome value to find where Java is installed.
|
||||
DWORD valueSz = 0;
|
||||
@ -233,6 +234,7 @@ QList<JavaInstallPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
|
||||
|
||||
RegCloseKey(jreKey);
|
||||
}
|
||||
}
|
||||
|
||||
return javas;
|
||||
}
|
||||
@ -345,6 +347,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
|
||||
candidates.append(getMinecraftJavaBundle());
|
||||
candidates.append(getPrismJavaBundle());
|
||||
candidates = addJavasFromEnv(candidates);
|
||||
candidates.removeDuplicates();
|
||||
return candidates;
|
||||
@ -389,6 +392,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
}
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
@ -452,6 +456,7 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDirs(FS::PathCombine(home, ".gradle/jdks"));
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
javas.removeDuplicates();
|
||||
return javas;
|
||||
@ -465,6 +470,8 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
javas.append(this->GetDefaultJava()->path);
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas.append(getPrismJavaBundle());
|
||||
javas.removeDuplicates();
|
||||
return addJavasFromEnv(javas);
|
||||
}
|
||||
#endif
|
||||
@ -476,12 +483,10 @@ QString JavaUtils::getJavaCheckPath()
|
||||
|
||||
QStringList getMinecraftJavaBundle()
|
||||
{
|
||||
QString executable = "java";
|
||||
QStringList processpaths;
|
||||
#if defined(Q_OS_OSX)
|
||||
processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime"));
|
||||
#elif defined(Q_OS_WIN32)
|
||||
executable += "w.exe";
|
||||
|
||||
auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", "");
|
||||
processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime");
|
||||
@ -506,7 +511,7 @@ QStringList getMinecraftJavaBundle()
|
||||
auto binFound = false;
|
||||
for (auto& entry : entries) {
|
||||
if (entry.baseName() == "bin") {
|
||||
javas.append(FS::PathCombine(entry.canonicalFilePath(), executable));
|
||||
javas.append(FS::PathCombine(entry.canonicalFilePath(), JavaUtils::javaExecutable));
|
||||
binFound = true;
|
||||
break;
|
||||
}
|
||||
@ -519,3 +524,33 @@ QStringList getMinecraftJavaBundle()
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -15,10 +15,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QProcess>
|
||||
#include <QStringList>
|
||||
|
||||
#include "JavaChecker.h"
|
||||
#include "JavaInstallList.h"
|
||||
#include "java/JavaInstall.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
@ -27,6 +26,7 @@
|
||||
QString stripVariableEntries(QString name, QString target, QString remove);
|
||||
QProcessEnvironment CleanEnviroment();
|
||||
QStringList getMinecraftJavaBundle();
|
||||
QStringList getPrismJavaBundle();
|
||||
|
||||
class JavaUtils : public QObject {
|
||||
Q_OBJECT
|
||||
@ -42,4 +42,5 @@ class JavaUtils : public QObject {
|
||||
#endif
|
||||
|
||||
static QString getJavaCheckPath();
|
||||
static const QString javaExecutable;
|
||||
};
|
||||
|
@ -43,12 +43,12 @@ QString JavaVersion::toString() const
|
||||
return m_string;
|
||||
}
|
||||
|
||||
bool JavaVersion::requiresPermGen()
|
||||
bool JavaVersion::requiresPermGen() const
|
||||
{
|
||||
return !m_parseable || m_major < 8;
|
||||
}
|
||||
|
||||
bool JavaVersion::isModular()
|
||||
bool JavaVersion::isModular() const
|
||||
{
|
||||
return m_parseable && m_major >= 9;
|
||||
}
|
||||
@ -59,12 +59,6 @@ bool JavaVersion::operator<(const JavaVersion& rhs)
|
||||
auto major = 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)
|
||||
return true;
|
||||
if (major > rmajor)
|
||||
@ -109,3 +103,24 @@ bool JavaVersion::operator>(const JavaVersion& rhs)
|
||||
{
|
||||
return (!operator<(rhs)) && (!operator==(rhs));
|
||||
}
|
||||
|
||||
JavaVersion::JavaVersion(int major, int minor, int security, int build, QString name)
|
||||
: m_major(major), m_minor(minor), m_security(security), m_name(name), m_parseable(true)
|
||||
{
|
||||
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(".");
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class JavaVersion {
|
||||
public:
|
||||
JavaVersion() {}
|
||||
JavaVersion(const QString& rhs);
|
||||
JavaVersion(int major, int minor, int security, int build = 0, QString name = "");
|
||||
|
||||
JavaVersion& operator=(const QString& rhs);
|
||||
|
||||
@ -23,21 +24,24 @@ class JavaVersion {
|
||||
bool operator==(const JavaVersion& rhs);
|
||||
bool operator>(const JavaVersion& rhs);
|
||||
|
||||
bool requiresPermGen();
|
||||
bool requiresPermGen() const;
|
||||
|
||||
bool isModular();
|
||||
bool isModular() const;
|
||||
|
||||
QString toString() const;
|
||||
|
||||
int major() { return m_major; }
|
||||
int minor() { return m_minor; }
|
||||
int security() { return m_security; }
|
||||
int major() const { return m_major; }
|
||||
int minor() const { return m_minor; }
|
||||
int security() const { return m_security; }
|
||||
QString build() const { return m_prerelease; }
|
||||
QString name() const { return m_name; }
|
||||
|
||||
private:
|
||||
QString m_string;
|
||||
int m_major = 0;
|
||||
int m_minor = 0;
|
||||
int m_security = 0;
|
||||
QString m_name = "";
|
||||
bool m_parseable = false;
|
||||
QString m_prerelease;
|
||||
};
|
||||
|
141
launcher/java/download/ArchiveDownloadTask.cpp
Normal file
141
launcher/java/download/ArchiveDownloadTask.cpp
Normal 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
|
45
launcher/java/download/ArchiveDownloadTask.h
Normal file
45
launcher/java/download/ArchiveDownloadTask.h
Normal 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
|
138
launcher/java/download/ManifestDownloadTask.cpp
Normal file
138
launcher/java/download/ManifestDownloadTask.cpp
Normal 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
|
46
launcher/java/download/ManifestDownloadTask.h
Normal file
46
launcher/java/download/ManifestDownloadTask.h
Normal 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
|
@ -37,6 +37,7 @@
|
||||
#include <FileSystem.h>
|
||||
#include <launch/LaunchTask.h>
|
||||
#include <sys.h>
|
||||
#include <QCryptographicHash>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include "java/JavaUtils.h"
|
||||
@ -45,20 +46,23 @@ void CheckJava::executeTask()
|
||||
{
|
||||
auto instance = m_parent->instance();
|
||||
auto settings = instance->settings();
|
||||
m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString());
|
||||
|
||||
QString javaPathSetting = settings->get("JavaPath").toString();
|
||||
m_javaPath = FS::ResolveExecutable(javaPathSetting);
|
||||
|
||||
bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool();
|
||||
|
||||
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
|
||||
if (realJavaPath.isEmpty()) {
|
||||
if (perInstance) {
|
||||
emit logLine(QString("The java binary \"%1\" couldn't be found. Please fix the java path "
|
||||
emit logLine(QString("The Java binary \"%1\" couldn't be found. Please fix the Java path "
|
||||
"override in the instance's settings or disable it.")
|
||||
.arg(m_javaPath),
|
||||
.arg(javaPathSetting),
|
||||
MessageLevel::Warning);
|
||||
} else {
|
||||
emit logLine(QString("The java binary \"%1\" couldn't be found. Please set up java in "
|
||||
emit logLine(QString("The Java binary \"%1\" couldn't be found. Please set up Java in "
|
||||
"the settings.")
|
||||
.arg(m_javaPath),
|
||||
.arg(javaPathSetting),
|
||||
MessageLevel::Warning);
|
||||
}
|
||||
emitFailed(QString("Java path is not valid."));
|
||||
@ -90,11 +94,10 @@ void CheckJava::executeTask()
|
||||
// if timestamps are not the same, or something is missing, check!
|
||||
if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 ||
|
||||
storedRealArchitecture.size() == 0 || storedVendor.size() == 0) {
|
||||
m_JavaChecker.reset(new JavaChecker);
|
||||
m_JavaChecker.reset(new JavaChecker(realJavaPath, "", 0, 0, 0, 0, this));
|
||||
emit logLine(QString("Checking Java version..."), MessageLevel::Launcher);
|
||||
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
|
||||
m_JavaChecker->m_path = realJavaPath;
|
||||
m_JavaChecker->performCheck();
|
||||
m_JavaChecker->start();
|
||||
return;
|
||||
} else {
|
||||
auto verString = instance->settings()->get("JavaVersion").toString();
|
||||
@ -106,10 +109,10 @@ void CheckJava::executeTask()
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
void CheckJava::checkJavaFinished(const JavaChecker::Result& result)
|
||||
{
|
||||
switch (result.validity) {
|
||||
case JavaCheckResult::Validity::Errored: {
|
||||
case JavaChecker::Result::Validity::Errored: {
|
||||
// Error message displayed if java can't start
|
||||
emit logLine(QString("Could not start java:"), MessageLevel::Error);
|
||||
emit logLines(result.errorLog.split('\n'), MessageLevel::Error);
|
||||
@ -117,14 +120,14 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
|
||||
emitFailed(QString("Could not start java!"));
|
||||
return;
|
||||
}
|
||||
case JavaCheckResult::Validity::ReturnedInvalidData: {
|
||||
case JavaChecker::Result::Validity::ReturnedInvalidData: {
|
||||
emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error);
|
||||
emit logLines(result.outLog.split('\n'), MessageLevel::Warning);
|
||||
emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher);
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
case JavaCheckResult::Validity::Valid: {
|
||||
case JavaChecker::Result::Validity::Valid: {
|
||||
auto instance = m_parent->instance();
|
||||
printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor);
|
||||
instance->settings()->set("JavaVersion", result.javaVersion.toString());
|
||||
|
@ -28,7 +28,7 @@ class CheckJava : public LaunchStep {
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
private slots:
|
||||
void checkJavaFinished(JavaCheckResult result);
|
||||
void checkJavaFinished(const JavaChecker::Result& result);
|
||||
|
||||
private:
|
||||
void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor);
|
||||
@ -37,5 +37,5 @@ class CheckJava : public LaunchStep {
|
||||
private:
|
||||
QString m_javaPath;
|
||||
QString m_javaSignature;
|
||||
JavaCheckerPtr m_JavaChecker;
|
||||
JavaChecker::Ptr m_JavaChecker;
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ void LookupServerAddress::setLookupAddress(const QString& lookupAddress)
|
||||
m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress));
|
||||
}
|
||||
|
||||
void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output)
|
||||
void LookupServerAddress::setOutputAddressPtr(MinecraftTarget::Ptr output)
|
||||
{
|
||||
m_output = std::move(output);
|
||||
}
|
||||
|
@ -19,20 +19,20 @@
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <QDnsLookup>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
class LookupServerAddress : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LookupServerAddress(LaunchTask* parent);
|
||||
virtual ~LookupServerAddress() {};
|
||||
virtual ~LookupServerAddress() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
virtual bool canAbort() const { return true; }
|
||||
|
||||
void setLookupAddress(const QString& lookupAddress);
|
||||
void setOutputAddressPtr(MinecraftServerTargetPtr output);
|
||||
void setOutputAddressPtr(MinecraftTarget::Ptr output);
|
||||
|
||||
private slots:
|
||||
void on_dnsLookupFinished();
|
||||
@ -42,5 +42,5 @@ class LookupServerAddress : public LaunchStep {
|
||||
|
||||
QDnsLookup* m_dnsLookup;
|
||||
QString m_lookupAddress;
|
||||
MinecraftServerTargetPtr m_output;
|
||||
MinecraftTarget::Ptr m_output;
|
||||
};
|
||||
|
@ -15,19 +15,26 @@
|
||||
|
||||
#include "BaseEntity.h"
|
||||
|
||||
#include "Exception.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "net/Mode.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Meta {
|
||||
|
||||
class ParsingValidator : public Net::Validator {
|
||||
public: /* con/des */
|
||||
ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity) {};
|
||||
virtual ~ParsingValidator() {};
|
||||
ParsingValidator(BaseEntity* entity) : m_entity(entity) {};
|
||||
virtual ~ParsingValidator() = default;
|
||||
|
||||
public: /* methods */
|
||||
bool init(QNetworkRequest&) override
|
||||
@ -61,92 +68,131 @@ class ParsingValidator : public Net::Validator {
|
||||
|
||||
private: /* data */
|
||||
QByteArray m_data;
|
||||
Meta::BaseEntity* m_entity;
|
||||
BaseEntity* m_entity;
|
||||
};
|
||||
|
||||
Meta::BaseEntity::~BaseEntity() {}
|
||||
|
||||
QUrl Meta::BaseEntity::url() const
|
||||
QUrl BaseEntity::url() const
|
||||
{
|
||||
auto s = APPLICATION->settings();
|
||||
QString metaOverride = s->get("MetaURLOverride").toString();
|
||||
if (metaOverride.isEmpty()) {
|
||||
return QUrl(BuildConfig.META_URL).resolved(localFilename());
|
||||
} else {
|
||||
return QUrl(metaOverride).resolved(localFilename());
|
||||
}
|
||||
return QUrl(metaOverride).resolved(localFilename());
|
||||
}
|
||||
|
||||
bool Meta::BaseEntity::loadLocalFile()
|
||||
Task::Ptr BaseEntity::loadTask(Net::Mode mode)
|
||||
{
|
||||
const QString fname = QDir("meta").absoluteFilePath(localFilename());
|
||||
if (!QFile::exists(fname)) {
|
||||
return false;
|
||||
if (m_task && m_task->isRunning()) {
|
||||
return m_task;
|
||||
}
|
||||
// TODO: check if the file has the expected checksum
|
||||
m_task.reset(new BaseEntityLoadTask(this, mode));
|
||||
return m_task;
|
||||
}
|
||||
|
||||
bool BaseEntity::isLoaded() const
|
||||
{
|
||||
// consider it loaded only if the main hash is either empty and was remote loadded or the hashes match and was loaded
|
||||
return m_sha256.isEmpty() ? m_load_status == LoadStatus::Remote : m_load_status != LoadStatus::NotLoaded && m_sha256 == m_file_sha256;
|
||||
}
|
||||
|
||||
void BaseEntity::setSha256(QString sha256)
|
||||
{
|
||||
m_sha256 = sha256;
|
||||
}
|
||||
|
||||
BaseEntity::LoadStatus BaseEntity::status() const
|
||||
{
|
||||
return m_load_status;
|
||||
}
|
||||
|
||||
BaseEntityLoadTask::BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode) : m_entity(parent), m_mode(mode) {}
|
||||
|
||||
void BaseEntityLoadTask::executeTask()
|
||||
{
|
||||
const QString fname = QDir("meta").absoluteFilePath(m_entity->localFilename());
|
||||
auto hashMatches = false;
|
||||
// the file exists on disk try to load it
|
||||
if (QFile::exists(fname)) {
|
||||
try {
|
||||
auto doc = Json::requireDocument(fname, fname);
|
||||
QByteArray fileData;
|
||||
// read local file if nothing is loaded yet
|
||||
if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded || m_entity->m_file_sha256.isEmpty()) {
|
||||
setStatus(tr("Loading local file"));
|
||||
fileData = FS::read(fname);
|
||||
m_entity->m_file_sha256 = Hashing::hash(fileData, Hashing::Algorithm::Sha256);
|
||||
}
|
||||
|
||||
// on online the hash needs to match
|
||||
hashMatches = m_entity->m_sha256 == m_entity->m_file_sha256;
|
||||
if (m_mode == Net::Mode::Online && !m_entity->m_sha256.isEmpty() && !hashMatches) {
|
||||
throw Exception("mismatched checksum");
|
||||
}
|
||||
|
||||
// load local file
|
||||
if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded) {
|
||||
auto doc = Json::requireDocument(fileData, fname);
|
||||
auto obj = Json::requireObject(doc, fname);
|
||||
parse(obj);
|
||||
return true;
|
||||
m_entity->parse(obj);
|
||||
m_entity->m_load_status = BaseEntity::LoadStatus::Local;
|
||||
}
|
||||
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
|
||||
// just make sure it's gone and we never consider it again.
|
||||
return !FS::deletePath(fname);
|
||||
}
|
||||
}
|
||||
|
||||
void Meta::BaseEntity::load(Net::Mode loadType)
|
||||
{
|
||||
// load local file if nothing is loaded yet
|
||||
if (!isLoaded()) {
|
||||
if (loadLocalFile()) {
|
||||
m_loadStatus = LoadStatus::Local;
|
||||
FS::deletePath(fname);
|
||||
m_entity->m_load_status = BaseEntity::LoadStatus::NotLoaded;
|
||||
}
|
||||
}
|
||||
// if we need remote update, run the update task
|
||||
if (loadType == Net::Mode::Offline || !shouldStartRemoteUpdate()) {
|
||||
auto wasLoadedOffline = m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && m_mode == Net::Mode::Offline;
|
||||
// if has is not present allways fetch from remote(e.g. the main index file), else only fetch if hash doesn't match
|
||||
auto wasLoadedRemote = m_entity->m_sha256.isEmpty() ? m_entity->m_load_status == BaseEntity::LoadStatus::Remote : hashMatches;
|
||||
if (wasLoadedOffline || wasLoadedRemote) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
m_updateTask.reset(new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()), APPLICATION->network()));
|
||||
auto url = this->url();
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename());
|
||||
m_task.reset(new NetJob(QObject::tr("Download of meta file %1").arg(m_entity->localFilename()), APPLICATION->network()));
|
||||
auto url = m_entity->url();
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("meta", m_entity->localFilename());
|
||||
entry->setStale(true);
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||
/*
|
||||
* The validator parses the file and loads it into the object.
|
||||
* If that fails, the file is not written to storage.
|
||||
*/
|
||||
dl->addValidator(new ParsingValidator(this));
|
||||
m_updateTask->addNetAction(dl);
|
||||
m_updateStatus = UpdateStatus::InProgress;
|
||||
QObject::connect(m_updateTask.get(), &NetJob::succeeded, [&]() {
|
||||
m_loadStatus = LoadStatus::Remote;
|
||||
m_updateStatus = UpdateStatus::Succeeded;
|
||||
m_updateTask.reset();
|
||||
if (!m_entity->m_sha256.isEmpty())
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_entity->m_sha256));
|
||||
dl->addValidator(new ParsingValidator(m_entity));
|
||||
m_task->addNetAction(dl);
|
||||
m_task->setAskRetry(false);
|
||||
connect(m_task.get(), &Task::failed, this, &BaseEntityLoadTask::emitFailed);
|
||||
connect(m_task.get(), &Task::succeeded, this, &BaseEntityLoadTask::emitSucceeded);
|
||||
connect(m_task.get(), &Task::succeeded, this, [this]() {
|
||||
m_entity->m_load_status = BaseEntity::LoadStatus::Remote;
|
||||
m_entity->m_file_sha256 = m_entity->m_sha256;
|
||||
});
|
||||
QObject::connect(m_updateTask.get(), &NetJob::failed, [&]() {
|
||||
m_updateStatus = UpdateStatus::Failed;
|
||||
m_updateTask.reset();
|
||||
});
|
||||
m_updateTask->start();
|
||||
|
||||
connect(m_task.get(), &Task::progress, this, &Task::setProgress);
|
||||
connect(m_task.get(), &Task::stepProgress, this, &BaseEntityLoadTask::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::status, this, &Task::setStatus);
|
||||
connect(m_task.get(), &Task::details, this, &Task::setDetails);
|
||||
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
bool Meta::BaseEntity::isLoaded() const
|
||||
bool BaseEntityLoadTask::canAbort() const
|
||||
{
|
||||
return m_loadStatus > LoadStatus::NotLoaded;
|
||||
return m_task ? m_task->canAbort() : false;
|
||||
}
|
||||
|
||||
bool Meta::BaseEntity::shouldStartRemoteUpdate() const
|
||||
bool BaseEntityLoadTask::abort()
|
||||
{
|
||||
// TODO: version-locks and offline mode?
|
||||
return m_updateStatus != UpdateStatus::InProgress;
|
||||
}
|
||||
|
||||
Task::Ptr Meta::BaseEntity::getCurrentTask()
|
||||
{
|
||||
if (m_updateStatus == UpdateStatus::InProgress) {
|
||||
return m_updateTask;
|
||||
if (m_task) {
|
||||
Task::abort();
|
||||
return m_task->abort();
|
||||
}
|
||||
return nullptr;
|
||||
return Task::abort();
|
||||
}
|
||||
|
||||
} // namespace Meta
|
||||
|
@ -17,38 +17,57 @@
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "net/Mode.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
namespace Meta {
|
||||
class BaseEntityLoadTask;
|
||||
class BaseEntity {
|
||||
friend BaseEntityLoadTask;
|
||||
|
||||
public: /* types */
|
||||
using Ptr = std::shared_ptr<BaseEntity>;
|
||||
enum class LoadStatus { NotLoaded, Local, Remote };
|
||||
enum class UpdateStatus { NotDone, InProgress, Failed, Succeeded };
|
||||
|
||||
public:
|
||||
virtual ~BaseEntity();
|
||||
|
||||
virtual void parse(const QJsonObject& obj) = 0;
|
||||
virtual ~BaseEntity() = default;
|
||||
|
||||
virtual QString localFilename() const = 0;
|
||||
virtual QUrl url() const;
|
||||
|
||||
bool isLoaded() const;
|
||||
bool shouldStartRemoteUpdate() const;
|
||||
LoadStatus status() const;
|
||||
|
||||
void load(Net::Mode loadType);
|
||||
Task::Ptr getCurrentTask();
|
||||
/* for parsers */
|
||||
void setSha256(QString sha256);
|
||||
|
||||
protected: /* methods */
|
||||
bool loadLocalFile();
|
||||
virtual void parse(const QJsonObject& obj) = 0;
|
||||
[[nodiscard]] Task::Ptr loadTask(Net::Mode loadType = Net::Mode::Online);
|
||||
|
||||
protected:
|
||||
QString m_sha256; // the expected sha256
|
||||
QString m_file_sha256; // the file sha256
|
||||
|
||||
private:
|
||||
LoadStatus m_loadStatus = LoadStatus::NotLoaded;
|
||||
UpdateStatus m_updateStatus = UpdateStatus::NotDone;
|
||||
NetJob::Ptr m_updateTask;
|
||||
LoadStatus m_load_status = LoadStatus::NotLoaded;
|
||||
Task::Ptr m_task;
|
||||
};
|
||||
|
||||
class BaseEntityLoadTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode);
|
||||
~BaseEntityLoadTask() override = default;
|
||||
|
||||
virtual void executeTask() override;
|
||||
virtual bool canAbort() const override;
|
||||
virtual bool abort() override;
|
||||
|
||||
private:
|
||||
BaseEntity* m_entity;
|
||||
Net::Mode m_mode;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
} // namespace Meta
|
||||
|
@ -16,7 +16,10 @@
|
||||
#include "Index.h"
|
||||
|
||||
#include "JsonFormat.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "VersionList.h"
|
||||
#include "meta/BaseEntity.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
namespace Meta {
|
||||
Index::Index(QObject* parent) : QAbstractListModel(parent) {}
|
||||
@ -51,14 +54,17 @@ QVariant Index::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int Index::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : m_lists.size();
|
||||
}
|
||||
|
||||
int Index::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : 1;
|
||||
}
|
||||
|
||||
QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) {
|
||||
@ -79,6 +85,7 @@ VersionList::Ptr Index::get(const QString& uid)
|
||||
if (!out) {
|
||||
out = std::make_shared<VersionList>(uid);
|
||||
m_uids[uid] = out;
|
||||
m_lists.append(out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -96,7 +103,7 @@ void Index::parse(const QJsonObject& obj)
|
||||
|
||||
void Index::merge(const std::shared_ptr<Index>& other)
|
||||
{
|
||||
const QVector<VersionList::Ptr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
|
||||
const QVector<VersionList::Ptr> lists = other->m_lists;
|
||||
// initial load, no need to merge
|
||||
if (m_lists.isEmpty()) {
|
||||
beginResetModel();
|
||||
@ -123,7 +130,33 @@ void Index::merge(const std::shared_ptr<Index>& other)
|
||||
|
||||
void Index::connectVersionList(const int row, const VersionList::Ptr& list)
|
||||
{
|
||||
connect(list.get(), &VersionList::nameChanged, this,
|
||||
[this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole); });
|
||||
connect(list.get(), &VersionList::nameChanged, this, [this, row] { emit dataChanged(index(row), index(row), { Qt::DisplayRole }); });
|
||||
}
|
||||
|
||||
Task::Ptr Index::loadVersion(const QString& uid, const QString& version, Net::Mode mode, bool force)
|
||||
{
|
||||
if (mode == Net::Mode::Offline) {
|
||||
return get(uid, version)->loadTask(mode);
|
||||
}
|
||||
|
||||
auto versionList = get(uid);
|
||||
auto loadTask = makeShared<SequentialTask>(
|
||||
this, tr("Load meta for %1:%2", "This is for the task name that loads the meta index.").arg(uid, version));
|
||||
if (status() != BaseEntity::LoadStatus::Remote || force) {
|
||||
loadTask->addTask(this->loadTask(mode));
|
||||
}
|
||||
loadTask->addTask(versionList->loadTask(mode));
|
||||
loadTask->addTask(versionList->getVersion(version)->loadTask(mode));
|
||||
return loadTask;
|
||||
}
|
||||
|
||||
Version::Ptr Index::getLoadedVersion(const QString& uid, const QString& version)
|
||||
{
|
||||
QEventLoop ev;
|
||||
auto task = loadVersion(uid, version);
|
||||
QObject::connect(task.get(), &Task::finished, &ev, &QEventLoop::quit);
|
||||
task->start();
|
||||
ev.exec();
|
||||
return get(uid, version);
|
||||
}
|
||||
} // namespace Meta
|
||||
|
@ -16,10 +16,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <memory>
|
||||
|
||||
#include "BaseEntity.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
class Task;
|
||||
|
||||
@ -30,6 +30,7 @@ class Index : public QAbstractListModel, public BaseEntity {
|
||||
public:
|
||||
explicit Index(QObject* parent = nullptr);
|
||||
explicit Index(const QVector<VersionList::Ptr>& lists, QObject* parent = nullptr);
|
||||
virtual ~Index() = default;
|
||||
|
||||
enum { UidRole = Qt::UserRole, NameRole, ListPtrRole };
|
||||
|
||||
@ -47,8 +48,15 @@ class Index : public QAbstractListModel, public BaseEntity {
|
||||
|
||||
QVector<VersionList::Ptr> lists() const { return m_lists; }
|
||||
|
||||
Task::Ptr loadVersion(const QString& uid, const QString& version = {}, Net::Mode mode = Net::Mode::Online, bool force = false);
|
||||
|
||||
// this blocks until the version is loaded
|
||||
Version::Ptr getLoadedVersion(const QString& uid, const QString& version);
|
||||
|
||||
public: // for usage by parsers only
|
||||
void merge(const std::shared_ptr<Index>& other);
|
||||
|
||||
protected:
|
||||
void parse(const QJsonObject& obj) override;
|
||||
|
||||
private:
|
||||
|
@ -41,6 +41,7 @@ static std::shared_ptr<Index> parseIndexInternal(const QJsonObject& obj)
|
||||
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) {
|
||||
VersionList::Ptr list = std::make_shared<VersionList>(requireString(obj, "uid"));
|
||||
list->setName(ensureString(obj, "name", QString()));
|
||||
list->setSha256(ensureString(obj, "sha256", QString()));
|
||||
return list;
|
||||
});
|
||||
return std::make_shared<Index>(lists);
|
||||
@ -58,6 +59,9 @@ static Version::Ptr parseCommonVersion(const QString& uid, const QJsonObject& ob
|
||||
parseRequires(obj, &reqs, "requires");
|
||||
parseRequires(obj, &conflicts, "conflicts");
|
||||
version->setRequires(reqs, conflicts);
|
||||
if (auto sha256 = ensureString(obj, "sha256", QString()); !sha256.isEmpty()) {
|
||||
version->setSha256(sha256);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <memory>
|
||||
|
||||
#include <set>
|
||||
#include "Exception.h"
|
||||
#include "meta/BaseEntity.h"
|
||||
|
||||
namespace Meta {
|
||||
class Index;
|
||||
|
@ -18,12 +18,9 @@
|
||||
#include <QDateTime>
|
||||
|
||||
#include "JsonFormat.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
Meta::Version::Version(const QString& uid, const QString& version) : BaseVersion(), m_uid(uid), m_version(version) {}
|
||||
|
||||
Meta::Version::~Version() {}
|
||||
|
||||
QString Meta::Version::descriptor()
|
||||
{
|
||||
return m_version;
|
||||
@ -71,6 +68,9 @@ void Meta::Version::mergeFromList(const Meta::Version::Ptr& other)
|
||||
if (m_volatile != other->m_volatile) {
|
||||
setVolatile(other->m_volatile);
|
||||
}
|
||||
if (!other->m_sha256.isEmpty()) {
|
||||
m_sha256 = other->m_sha256;
|
||||
}
|
||||
}
|
||||
|
||||
void Meta::Version::merge(const Version::Ptr& other)
|
||||
|
@ -38,7 +38,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
|
||||
using Ptr = std::shared_ptr<Version>;
|
||||
|
||||
explicit Version(const QString& uid, const QString& version);
|
||||
virtual ~Version();
|
||||
virtual ~Version() = default;
|
||||
|
||||
QString descriptor() override;
|
||||
QString name() override;
|
||||
@ -52,7 +52,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
|
||||
const Meta::RequireSet& requiredSet() const { return m_requires; }
|
||||
VersionFilePtr data() const { return m_data; }
|
||||
bool isRecommended() const { return m_recommended; }
|
||||
bool isLoaded() const { return m_data != nullptr; }
|
||||
bool isLoaded() const { return m_data != nullptr && BaseEntity::isLoaded(); }
|
||||
|
||||
void merge(const Version::Ptr& other);
|
||||
void mergeFromList(const Version::Ptr& other);
|
||||
|
@ -17,8 +17,13 @@
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Index.h"
|
||||
#include "JsonFormat.h"
|
||||
#include "Version.h"
|
||||
#include "meta/BaseEntity.h"
|
||||
#include "net/Mode.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
namespace Meta {
|
||||
VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList(parent), m_uid(uid)
|
||||
@ -28,8 +33,11 @@ VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList(
|
||||
|
||||
Task::Ptr VersionList::getLoadTask()
|
||||
{
|
||||
load(Net::Mode::Online);
|
||||
return getCurrentTask();
|
||||
auto loadTask =
|
||||
makeShared<SequentialTask>(this, tr("Load meta for %1", "This is for the task name that loads the meta index.").arg(m_uid));
|
||||
loadTask->addTask(APPLICATION->metadataIndex()->loadTask(Net::Mode::Online));
|
||||
loadTask->addTask(this->loadTask(Net::Mode::Online));
|
||||
return loadTask;
|
||||
}
|
||||
|
||||
bool VersionList::isLoaded()
|
||||
@ -92,6 +100,13 @@ QVariant VersionList::data(const QModelIndex& index, int role) const
|
||||
return QVariant::fromValue(version);
|
||||
case RecommendedRole:
|
||||
return version->isRecommended();
|
||||
case JavaMajorRole: {
|
||||
auto major = version->version();
|
||||
if (major.startsWith("java")) {
|
||||
major = "Java " + major.mid(4);
|
||||
}
|
||||
return major;
|
||||
}
|
||||
// FIXME: this should be determined in whatever view/proxy is used...
|
||||
// case LatestRole: return version == getLatestStable();
|
||||
default:
|
||||
@ -101,10 +116,14 @@ QVariant VersionList::data(const QModelIndex& index, int role) const
|
||||
|
||||
BaseVersionList::RoleList VersionList::providesRoles() const
|
||||
{
|
||||
return { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole,
|
||||
TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole };
|
||||
return m_provided_roles;
|
||||
}
|
||||
|
||||
void VersionList::setProvidedRoles(RoleList roles)
|
||||
{
|
||||
m_provided_roles = roles;
|
||||
};
|
||||
|
||||
QHash<int, QByteArray> VersionList::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles = BaseVersionList::roleNames();
|
||||
@ -131,6 +150,8 @@ Version::Ptr VersionList::getVersion(const QString& version)
|
||||
if (!out) {
|
||||
out = std::make_shared<Version>(m_uid, version);
|
||||
m_lookup[version] = out;
|
||||
setupAddedVersion(m_versions.size(), out);
|
||||
m_versions.append(out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -191,6 +212,9 @@ void VersionList::mergeFromIndex(const VersionList::Ptr& other)
|
||||
if (m_name != other->m_name) {
|
||||
setName(other->m_name);
|
||||
}
|
||||
if (!other->m_sha256.isEmpty()) {
|
||||
m_sha256 = other->m_sha256;
|
||||
}
|
||||
}
|
||||
|
||||
void VersionList::merge(const VersionList::Ptr& other)
|
||||
@ -198,23 +222,27 @@ void VersionList::merge(const VersionList::Ptr& other)
|
||||
if (m_name != other->m_name) {
|
||||
setName(other->m_name);
|
||||
}
|
||||
if (!other->m_sha256.isEmpty()) {
|
||||
m_sha256 = other->m_sha256;
|
||||
}
|
||||
|
||||
// TODO: do not reset the whole model. maybe?
|
||||
beginResetModel();
|
||||
m_versions.clear();
|
||||
if (other->m_versions.isEmpty()) {
|
||||
qWarning() << "Empty list loaded ...";
|
||||
}
|
||||
for (const Version::Ptr& version : other->m_versions) {
|
||||
for (auto version : other->m_versions) {
|
||||
// we already have the version. merge the contents
|
||||
if (m_lookup.contains(version->version())) {
|
||||
m_lookup.value(version->version())->mergeFromList(version);
|
||||
auto existing = m_lookup.value(version->version());
|
||||
existing->mergeFromList(version);
|
||||
version = existing;
|
||||
} else {
|
||||
m_lookup.insert(version->uid(), version);
|
||||
}
|
||||
m_lookup.insert(version->version(), version);
|
||||
// connect it.
|
||||
setupAddedVersion(m_versions.size(), version);
|
||||
m_versions.append(version);
|
||||
}
|
||||
m_recommended = getBetterVersion(m_recommended, version);
|
||||
}
|
||||
endResetModel();
|
||||
@ -222,14 +250,15 @@ void VersionList::merge(const VersionList::Ptr& other)
|
||||
|
||||
void VersionList::setupAddedVersion(const int row, const Version::Ptr& version)
|
||||
{
|
||||
// FIXME: do not disconnect from everythin, disconnect only the lambdas here
|
||||
version->disconnect();
|
||||
disconnect(version.get(), &Version::requiresChanged, this, nullptr);
|
||||
disconnect(version.get(), &Version::timeChanged, this, nullptr);
|
||||
disconnect(version.get(), &Version::typeChanged, this, nullptr);
|
||||
|
||||
connect(version.get(), &Version::requiresChanged, this,
|
||||
[this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
|
||||
connect(version.get(), &Version::timeChanged, this,
|
||||
[this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
|
||||
connect(version.get(), &Version::typeChanged, this,
|
||||
[this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
|
||||
[this, row]() { emit dataChanged(index(row), index(row), { TimeRole, SortRole }); });
|
||||
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TypeRole }); });
|
||||
}
|
||||
|
||||
BaseVersion::Ptr VersionList::getRecommended() const
|
||||
@ -237,4 +266,14 @@ BaseVersion::Ptr VersionList::getRecommended() const
|
||||
return m_recommended;
|
||||
}
|
||||
|
||||
void VersionList::waitToLoad()
|
||||
{
|
||||
if (isLoaded())
|
||||
return;
|
||||
QEventLoop ev;
|
||||
auto task = getLoadTask();
|
||||
QObject::connect(task.get(), &Task::finished, &ev, &QEventLoop::quit);
|
||||
task->start();
|
||||
ev.exec();
|
||||
}
|
||||
} // namespace Meta
|
||||
|
@ -30,13 +30,14 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||
public:
|
||||
explicit VersionList(const QString& uid, QObject* parent = nullptr);
|
||||
virtual ~VersionList() = default;
|
||||
|
||||
using Ptr = std::shared_ptr<VersionList>;
|
||||
|
||||
enum Roles { UidRole = Qt::UserRole + 100, TimeRole, RequiresRole, VersionPtrRole };
|
||||
|
||||
Task::Ptr getLoadTask() override;
|
||||
bool isLoaded() override;
|
||||
[[nodiscard]] Task::Ptr getLoadTask() override;
|
||||
const BaseVersion::Ptr at(int i) const override;
|
||||
int count() const override;
|
||||
void sortVersions() override;
|
||||
@ -47,6 +48,8 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
RoleList providesRoles() const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void setProvidedRoles(RoleList roles);
|
||||
|
||||
QString localFilename() const override;
|
||||
|
||||
QString uid() const { return m_uid; }
|
||||
@ -58,6 +61,9 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
|
||||
QVector<Version::Ptr> versions() const { return m_versions; }
|
||||
|
||||
// this blocks until the version list is loaded
|
||||
void waitToLoad();
|
||||
|
||||
public: // for usage only by parsers
|
||||
void setName(const QString& name);
|
||||
void setVersions(const QVector<Version::Ptr>& versions);
|
||||
@ -79,6 +85,9 @@ class VersionList : public BaseVersionList, public BaseEntity {
|
||||
|
||||
Version::Ptr m_recommended;
|
||||
|
||||
RoleList m_provided_roles = { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole,
|
||||
TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole };
|
||||
|
||||
void setupAddedVersion(int row, const Version::Ptr& version);
|
||||
};
|
||||
} // namespace Meta
|
||||
|
@ -283,8 +283,7 @@ Net::NetRequest::Ptr AssetObject::getDownloadAction()
|
||||
if ((!objectFile.isFile()) || (objectFile.size() != size)) {
|
||||
auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath());
|
||||
if (hash.size()) {
|
||||
auto rawHash = QByteArray::fromHex(hash.toLatin1());
|
||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
||||
objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, hash));
|
||||
}
|
||||
objectDL->setProgress(objectDL->getProgress(), size);
|
||||
return objectDL;
|
||||
|
@ -56,18 +56,6 @@ Component::Component(PackProfile* parent, const QString& uid)
|
||||
m_uid = uid;
|
||||
}
|
||||
|
||||
Component::Component(PackProfile* parent, std::shared_ptr<Meta::Version> version)
|
||||
{
|
||||
assert(parent);
|
||||
m_parent = parent;
|
||||
|
||||
m_metaVersion = version;
|
||||
m_uid = version->uid();
|
||||
m_version = m_cachedVersion = version->version();
|
||||
m_cachedName = version->name();
|
||||
m_loaded = version->isLoaded();
|
||||
}
|
||||
|
||||
Component::Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file)
|
||||
{
|
||||
assert(parent);
|
||||
@ -102,9 +90,6 @@ void Component::applyTo(LaunchProfile* profile)
|
||||
std::shared_ptr<class VersionFile> Component::getVersionFile() const
|
||||
{
|
||||
if (m_metaVersion) {
|
||||
if (!m_metaVersion->isLoaded()) {
|
||||
m_metaVersion->load(Net::Mode::Online);
|
||||
}
|
||||
return m_metaVersion->data();
|
||||
} else {
|
||||
return m_file;
|
||||
@ -131,29 +116,35 @@ int Component::getOrder()
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Component::setOrder(int order)
|
||||
{
|
||||
m_orderOverride = true;
|
||||
m_order = order;
|
||||
}
|
||||
|
||||
QString Component::getID()
|
||||
{
|
||||
return m_uid;
|
||||
}
|
||||
|
||||
QString Component::getName()
|
||||
{
|
||||
if (!m_cachedName.isEmpty())
|
||||
return m_cachedName;
|
||||
return m_uid;
|
||||
}
|
||||
|
||||
QString Component::getVersion()
|
||||
{
|
||||
return m_cachedVersion;
|
||||
}
|
||||
|
||||
QString Component::getFilename()
|
||||
{
|
||||
return m_parent->patchFilePathForUid(m_uid);
|
||||
}
|
||||
|
||||
QDateTime Component::getReleaseDateTime()
|
||||
{
|
||||
if (m_metaVersion) {
|
||||
@ -198,17 +189,14 @@ bool Component::isCustom()
|
||||
|
||||
bool Component::isCustomizable()
|
||||
{
|
||||
if (m_metaVersion) {
|
||||
if (getVersionFile()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return m_metaVersion && getVersionFile();
|
||||
}
|
||||
|
||||
bool Component::isRemovable()
|
||||
{
|
||||
return !m_important;
|
||||
}
|
||||
|
||||
bool Component::isRevertible()
|
||||
{
|
||||
if (isCustom()) {
|
||||
@ -218,18 +206,18 @@ bool Component::isRevertible()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Component::isMoveable()
|
||||
{
|
||||
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Component::isVersionChangeable()
|
||||
{
|
||||
auto list = getVersionList();
|
||||
if (list) {
|
||||
if (!list->isLoaded()) {
|
||||
list->load(Net::Mode::Online);
|
||||
}
|
||||
list->waitToLoad();
|
||||
return list->count() != 0;
|
||||
}
|
||||
return false;
|
||||
|
@ -22,7 +22,6 @@ class Component : public QObject, public ProblemProvider {
|
||||
Component(PackProfile* parent, const QString& uid);
|
||||
|
||||
// DEPRECATED: remove these constructors?
|
||||
Component(PackProfile* parent, std::shared_ptr<Meta::Version> version);
|
||||
Component(PackProfile* parent, const QString& uid, std::shared_ptr<VersionFile> file);
|
||||
|
||||
virtual ~Component() {}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
/*
|
||||
* This is responsible for loading the components of a component list AND resolving dependency issues between them
|
||||
@ -93,9 +94,9 @@ static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net
|
||||
component->m_loaded = true;
|
||||
result = LoadResult::LoadedLocal;
|
||||
} else {
|
||||
metaVersion->load(netmode);
|
||||
loadTask = metaVersion->getCurrentTask();
|
||||
if (loadTask)
|
||||
loadTask = APPLICATION->metadataIndex()->loadVersion(component->m_uid, component->m_version, netmode);
|
||||
loadTask->start();
|
||||
if (netmode == Net::Mode::Online)
|
||||
result = LoadResult::RequiresRemote;
|
||||
else if (metaVersion->isLoaded())
|
||||
result = LoadResult::LoadedLocal;
|
||||
@ -133,21 +134,6 @@ static LoadResult loadPackProfile(ComponentPtr component, Task::Ptr& loadTask, N
|
||||
}
|
||||
*/
|
||||
|
||||
static LoadResult loadIndex(Task::Ptr& loadTask, Net::Mode netmode)
|
||||
{
|
||||
// FIXME: DECIDE. do we want to run the update task anyway?
|
||||
if (APPLICATION->metadataIndex()->isLoaded()) {
|
||||
qDebug() << "Index is already loaded";
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
APPLICATION->metadataIndex()->load(netmode);
|
||||
loadTask = APPLICATION->metadataIndex()->getCurrentTask();
|
||||
if (loadTask) {
|
||||
return LoadResult::RequiresRemote;
|
||||
}
|
||||
// FIXME: this is assuming the load succeeded... did it really?
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ComponentUpdateTask::loadComponents()
|
||||
@ -156,23 +142,7 @@ void ComponentUpdateTask::loadComponents()
|
||||
size_t taskIndex = 0;
|
||||
size_t componentIndex = 0;
|
||||
d->remoteLoadSuccessful = true;
|
||||
// load the main index (it is needed to determine if components can revert)
|
||||
{
|
||||
// FIXME: tear out as a method? or lambda?
|
||||
Task::Ptr indexLoadTask;
|
||||
auto singleResult = loadIndex(indexLoadTask, d->netmode);
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if (indexLoadTask) {
|
||||
qDebug() << "Remote loading is being run for metadata index";
|
||||
RemoteLoadStatus status;
|
||||
status.type = RemoteLoadStatus::Type::Index;
|
||||
d->remoteLoadStatusList.append(status);
|
||||
connect(indexLoadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(indexLoadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(indexLoadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
taskIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// load all the components OR their lists...
|
||||
for (auto component : d->m_list->d->components) {
|
||||
Task::Ptr loadTask;
|
||||
@ -206,12 +176,13 @@ void ComponentUpdateTask::loadComponents()
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if (loadTask) {
|
||||
qDebug() << "Remote loading is being run for" << component->getName();
|
||||
connect(loadTask.get(), &Task::succeeded, [=]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(loadTask.get(), &Task::failed, [=](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(loadTask.get(), &Task::aborted, [=]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
connect(loadTask.get(), &Task::succeeded, this, [this, taskIndex]() { remoteLoadSucceeded(taskIndex); });
|
||||
connect(loadTask.get(), &Task::failed, this, [this, taskIndex](const QString& error) { remoteLoadFailed(taskIndex, error); });
|
||||
connect(loadTask.get(), &Task::aborted, this, [this, taskIndex]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
|
||||
RemoteLoadStatus status;
|
||||
status.type = loadType;
|
||||
status.PackProfileIndex = componentIndex;
|
||||
status.task = loadTask;
|
||||
d->remoteLoadStatusList.append(status);
|
||||
taskIndex++;
|
||||
}
|
||||
@ -518,7 +489,14 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
|
||||
void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
{
|
||||
if (static_cast<size_t>(d->remoteLoadStatusList.size()) < taskIndex) {
|
||||
qWarning() << "Got task index outside of results" << taskIndex;
|
||||
return;
|
||||
}
|
||||
auto& taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::failed, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr);
|
||||
if (taskSlot.finished) {
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
@ -538,7 +516,14 @@ void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
|
||||
void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
|
||||
{
|
||||
if (static_cast<size_t>(d->remoteLoadStatusList.size()) < taskIndex) {
|
||||
qWarning() << "Got task index outside of results" << taskIndex;
|
||||
return;
|
||||
}
|
||||
auto& taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::failed, this, nullptr);
|
||||
disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr);
|
||||
if (taskSlot.finished) {
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QString>
|
||||
#include <cstddef>
|
||||
#include "net/Mode.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class PackProfile;
|
||||
|
||||
@ -13,6 +14,7 @@ struct RemoteLoadStatus {
|
||||
bool finished = false;
|
||||
bool succeeded = false;
|
||||
QString error;
|
||||
Task::Ptr task;
|
||||
};
|
||||
|
||||
struct ComponentUpdateTaskData {
|
||||
|
@ -164,6 +164,11 @@ void LaunchProfile::applyCompatibleJavaMajors(QList<int>& javaMajor)
|
||||
{
|
||||
m_compatibleJavaMajors.append(javaMajor);
|
||||
}
|
||||
void LaunchProfile::applyCompatibleJavaName(QString javaName)
|
||||
{
|
||||
if (!javaName.isEmpty())
|
||||
m_compatibleJavaName = javaName;
|
||||
}
|
||||
|
||||
void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext)
|
||||
{
|
||||
@ -334,6 +339,11 @@ const QList<int>& LaunchProfile::getCompatibleJavaMajors() const
|
||||
return m_compatibleJavaMajors;
|
||||
}
|
||||
|
||||
const QString LaunchProfile::getCompatibleJavaName() const
|
||||
{
|
||||
return m_compatibleJavaName;
|
||||
}
|
||||
|
||||
void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext,
|
||||
QStringList& jars,
|
||||
QStringList& nativeJars,
|
||||
|
@ -59,6 +59,7 @@ class LaunchProfile : public ProblemProvider {
|
||||
void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext);
|
||||
void applyAgent(AgentPtr agent, const RuntimeContext& runtimeContext);
|
||||
void applyCompatibleJavaMajors(QList<int>& javaMajor);
|
||||
void applyCompatibleJavaName(QString javaName);
|
||||
void applyMainJar(LibraryPtr jar);
|
||||
void applyProblemSeverity(ProblemSeverity severity);
|
||||
/// clear the profile
|
||||
@ -80,6 +81,7 @@ class LaunchProfile : public ProblemProvider {
|
||||
const QList<LibraryPtr>& getMavenFiles() const;
|
||||
const QList<AgentPtr>& getAgents() const;
|
||||
const QList<int>& getCompatibleJavaMajors() const;
|
||||
const QString getCompatibleJavaName() const;
|
||||
const LibraryPtr getMainJar() const;
|
||||
void getLibraryFiles(const RuntimeContext& runtimeContext,
|
||||
QStringList& jars,
|
||||
@ -150,5 +152,7 @@ class LaunchProfile : public ProblemProvider {
|
||||
/// compatible java major versions
|
||||
QList<int> m_compatibleJavaMajors;
|
||||
|
||||
QString m_compatibleJavaName;
|
||||
|
||||
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
|
||||
};
|
||||
|
@ -147,9 +147,8 @@ QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeC
|
||||
options |= Net::Download::Option::MakeEternal;
|
||||
|
||||
if (sha1.size()) {
|
||||
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry, options);
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, sha1));
|
||||
qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url;
|
||||
out.append(dl);
|
||||
} else {
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include "MinecraftInstance.h"
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/launch/AutoInstallJava.h"
|
||||
#include "minecraft/launch/CreateGameFolders.h"
|
||||
#include "minecraft/launch/ExtractNatives.h"
|
||||
#include "minecraft/launch/PrintInstanceInfo.h"
|
||||
@ -134,25 +136,21 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
return;
|
||||
|
||||
// Java Settings
|
||||
auto javaOverride = m_settings->registerSetting("OverrideJava", false);
|
||||
auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
|
||||
auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false);
|
||||
|
||||
// combinations
|
||||
auto javaOrLocation = std::make_shared<OrSetting>("JavaOrLocationOverride", javaOverride, locationOverride);
|
||||
auto javaOrArgs = std::make_shared<OrSetting>("JavaOrArgsOverride", javaOverride, argsOverride);
|
||||
m_settings->registerSetting("AutomaticJava", false);
|
||||
|
||||
if (auto global_settings = globalSettings()) {
|
||||
m_settings->registerOverride(global_settings->getSetting("JavaPath"), javaOrLocation);
|
||||
m_settings->registerOverride(global_settings->getSetting("JvmArgs"), javaOrArgs);
|
||||
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), javaOrLocation);
|
||||
m_settings->registerOverride(global_settings->getSetting("JavaPath"), locationOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("JvmArgs"), argsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), locationOverride);
|
||||
|
||||
// special!
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), javaOrLocation);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), locationOverride);
|
||||
m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), locationOverride);
|
||||
|
||||
// Window Size
|
||||
auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
|
||||
@ -196,8 +194,9 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
}
|
||||
|
||||
// Join server on launch, this does not have a global override
|
||||
m_settings->registerSetting("JoinServerOnLaunch", false);
|
||||
m_settings->registerSetting({ "JoinServerOnLaunch", "JoinOnLaunch" }, false);
|
||||
m_settings->registerSetting("JoinServerOnLaunchAddress", "");
|
||||
m_settings->registerSetting("JoinWorldOnLaunch", "");
|
||||
|
||||
// Use account for instance, this does not have a global override
|
||||
m_settings->registerSetting("UseAccountForInstance", false);
|
||||
@ -523,8 +522,7 @@ QStringList MinecraftInstance::javaArguments()
|
||||
|
||||
if (javaVersion.isModular() && shouldApplyOnlineFixes())
|
||||
// allow reflective access to java.net - required by the skin fix
|
||||
args << "--add-opens"
|
||||
<< "java.base/java.net=ALL-UNNAMED";
|
||||
args << "--add-opens" << "java.base/java.net=ALL-UNNAMED";
|
||||
|
||||
return args;
|
||||
}
|
||||
@ -608,7 +606,7 @@ QProcessEnvironment MinecraftInstance::createLaunchEnvironment()
|
||||
// dlsym variant is only needed for OpenGL and not included in the vulkan layer
|
||||
appendLib("libMangoHud_dlsym.so");
|
||||
appendLib("libMangoHud_opengl.so");
|
||||
appendLib(mangoHudLib.fileName());
|
||||
preloadList << mangoHudLibString;
|
||||
}
|
||||
|
||||
env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":")));
|
||||
@ -656,7 +654,7 @@ static QString replaceTokensIn(QString text, QMap<QString, QString> with)
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const
|
||||
QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) const
|
||||
{
|
||||
auto profile = m_components->getProfile();
|
||||
QString args_pattern = profile->getMinecraftArguments();
|
||||
@ -664,12 +662,16 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
|
||||
args_pattern += " --tweakClass " + tweaker;
|
||||
}
|
||||
|
||||
if (serverToJoin && !serverToJoin->address.isEmpty()) {
|
||||
if (targetToJoin) {
|
||||
if (!targetToJoin->address.isEmpty()) {
|
||||
if (profile->hasTrait("feature:is_quick_play_multiplayer")) {
|
||||
args_pattern += " --quickPlayMultiplayer " + serverToJoin->address + ':' + QString::number(serverToJoin->port);
|
||||
args_pattern += " --quickPlayMultiplayer " + targetToJoin->address + ':' + QString::number(targetToJoin->port);
|
||||
} else {
|
||||
args_pattern += " --server " + serverToJoin->address;
|
||||
args_pattern += " --port " + QString::number(serverToJoin->port);
|
||||
args_pattern += " --server " + targetToJoin->address;
|
||||
args_pattern += " --port " + QString::number(targetToJoin->port);
|
||||
}
|
||||
} else if (!targetToJoin->world.isEmpty() && profile->hasTrait("feature:is_quick_play_singleplayer")) {
|
||||
args_pattern += " --quickPlaySingleplayer " + targetToJoin->world;
|
||||
}
|
||||
}
|
||||
|
||||
@ -713,7 +715,7 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine
|
||||
return parts;
|
||||
}
|
||||
|
||||
QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
{
|
||||
QString launchScript;
|
||||
|
||||
@ -732,9 +734,13 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
launchScript += "appletClass " + appletClass + "\n";
|
||||
}
|
||||
|
||||
if (serverToJoin && !serverToJoin->address.isEmpty()) {
|
||||
launchScript += "serverAddress " + serverToJoin->address + "\n";
|
||||
launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n";
|
||||
if (targetToJoin) {
|
||||
if (!targetToJoin->address.isEmpty()) {
|
||||
launchScript += "serverAddress " + targetToJoin->address + "\n";
|
||||
launchScript += "serverPort " + QString::number(targetToJoin->port) + "\n";
|
||||
} else if (!targetToJoin->world.isEmpty()) {
|
||||
launchScript += "worldName " + targetToJoin->world + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// generic minecraft params
|
||||
@ -787,16 +793,15 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
|
||||
return launchScript;
|
||||
}
|
||||
|
||||
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
{
|
||||
QStringList out;
|
||||
out << "Main Class:"
|
||||
<< " " + getMainClass() << "";
|
||||
out << "Native path:"
|
||||
<< " " + getNativePath() << "";
|
||||
out << "Main Class:" << " " + getMainClass() << "";
|
||||
out << "Native path:" << " " + getNativePath() << "";
|
||||
|
||||
auto profile = m_components->getProfile();
|
||||
|
||||
// traits
|
||||
auto alltraits = traits();
|
||||
if (alltraits.size()) {
|
||||
out << "Traits:";
|
||||
@ -806,6 +811,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "";
|
||||
}
|
||||
|
||||
// native libraries
|
||||
auto settings = this->settings();
|
||||
bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
|
||||
bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
|
||||
@ -841,6 +847,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "";
|
||||
}
|
||||
|
||||
// mods and core mods
|
||||
auto printModList = [&](const QString& label, ModFolderModel& model) {
|
||||
if (model.size()) {
|
||||
out << QString("%1:").arg(label);
|
||||
@ -869,6 +876,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
printModList("Mods", *(loaderModList().get()));
|
||||
printModList("Core Mods", *(coreModList().get()));
|
||||
|
||||
// jar mods
|
||||
auto& jarMods = profile->getJarMods();
|
||||
if (jarMods.size()) {
|
||||
out << "Jar Mods:";
|
||||
@ -884,11 +892,13 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr
|
||||
out << "";
|
||||
}
|
||||
|
||||
auto params = processMinecraftArgs(nullptr, serverToJoin);
|
||||
// minecraft arguments
|
||||
auto params = processMinecraftArgs(nullptr, targetToJoin);
|
||||
out << "Params:";
|
||||
out << " " + params.join(' ');
|
||||
out << "";
|
||||
|
||||
// window size
|
||||
QString windowParams;
|
||||
if (settings->get("LaunchMaximized").toBool()) {
|
||||
out << "Window size: max (if available)";
|
||||
@ -1034,7 +1044,7 @@ Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
{
|
||||
updateRuntimeContext();
|
||||
// FIXME: get rid of shared_from_this ...
|
||||
@ -1048,26 +1058,28 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
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)
|
||||
{
|
||||
process->appendStep(makeShared<CreateGameFolders>(pptr));
|
||||
}
|
||||
|
||||
if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool()) {
|
||||
if (!targetToJoin && settings()->get("JoinOnLaunch").toBool()) {
|
||||
QString fullAddress = settings()->get("JoinServerOnLaunchAddress").toString();
|
||||
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress)));
|
||||
if (!fullAddress.isEmpty()) {
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(fullAddress, false)));
|
||||
} else {
|
||||
QString world = settings()->get("JoinWorldOnLaunch").toString();
|
||||
if (!world.isEmpty()) {
|
||||
targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(world, true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverToJoin && serverToJoin->port == 25565) {
|
||||
if (targetToJoin && targetToJoin->port == 25565) {
|
||||
// Resolve server address to join on launch
|
||||
auto step = makeShared<LookupServerAddress>(pptr);
|
||||
step->setLookupAddress(serverToJoin->address);
|
||||
step->setOutputAddressPtr(serverToJoin);
|
||||
step->setLookupAddress(targetToJoin->address);
|
||||
step->setOutputAddressPtr(targetToJoin);
|
||||
process->appendStep(step);
|
||||
}
|
||||
|
||||
@ -1088,6 +1100,12 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
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
|
||||
{
|
||||
process->appendStep(makeShared<ModMinecraftJar>(pptr));
|
||||
@ -1100,7 +1118,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
|
||||
// print some instance info here...
|
||||
{
|
||||
process->appendStep(makeShared<PrintInstanceInfo>(pptr, session, serverToJoin));
|
||||
process->appendStep(makeShared<PrintInstanceInfo>(pptr, session, targetToJoin));
|
||||
}
|
||||
|
||||
// extract native jars if needed
|
||||
@ -1123,7 +1141,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
|
||||
auto step = makeShared<LauncherPartLaunch>(pptr);
|
||||
step->setWorkingDirectory(gameRoot());
|
||||
step->setAuthSession(session);
|
||||
step->setServerToJoin(serverToJoin);
|
||||
step->setTargetToJoin(targetToJoin);
|
||||
process->appendStep(step);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
|
||||
class ModFolderModel;
|
||||
@ -56,7 +56,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
|
||||
virtual ~MinecraftInstance() {};
|
||||
virtual ~MinecraftInstance() = default;
|
||||
virtual void saveNow() override;
|
||||
|
||||
void loadSpecificSettings() override;
|
||||
@ -121,11 +121,11 @@ class MinecraftInstance : public BaseInstance {
|
||||
|
||||
////// Launch stuff //////
|
||||
Task::Ptr createUpdateTask(Net::Mode mode) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) override;
|
||||
QStringList extraArguments() override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override;
|
||||
QList<Mod*> getJarMods() const;
|
||||
QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
|
||||
QString createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin);
|
||||
/// get arguments passed to java
|
||||
QStringList javaArguments();
|
||||
QString getLauncher();
|
||||
@ -155,7 +155,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
virtual QString getMainClass() const;
|
||||
|
||||
// FIXME: remove
|
||||
virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const;
|
||||
virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) const;
|
||||
|
||||
virtual JavaVersion getJavaVersion();
|
||||
|
||||
|
@ -16,32 +16,22 @@
|
||||
#include "MinecraftUpdate.h"
|
||||
#include "MinecraftInstance.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <FileSystem.h>
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/Library.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "update/AssetUpdateTask.h"
|
||||
#include "update/FMLLibrariesTask.h"
|
||||
#include "update/FoldersTask.h"
|
||||
#include "update/LibrariesTask.h"
|
||||
|
||||
#include <meta/Index.h>
|
||||
#include <meta/Version.h>
|
||||
|
||||
MinecraftUpdate::MinecraftUpdate(MinecraftInstance* inst, QObject* parent) : Task(parent), m_inst(inst) {}
|
||||
MinecraftUpdate::MinecraftUpdate(MinecraftInstance* inst, QObject* parent) : SequentialTask(parent), m_inst(inst) {}
|
||||
|
||||
void MinecraftUpdate::executeTask()
|
||||
{
|
||||
m_tasks.clear();
|
||||
m_queue.clear();
|
||||
// create folders
|
||||
{
|
||||
m_tasks.append(makeShared<FoldersTask>(m_inst));
|
||||
addTask(makeShared<FoldersTask>(m_inst));
|
||||
}
|
||||
|
||||
// add metadata update task if necessary
|
||||
@ -50,121 +40,24 @@ void MinecraftUpdate::executeTask()
|
||||
components->reload(Net::Mode::Online);
|
||||
auto task = components->getCurrentTask();
|
||||
if (task) {
|
||||
m_tasks.append(task);
|
||||
addTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
// libraries download
|
||||
{
|
||||
m_tasks.append(makeShared<LibrariesTask>(m_inst));
|
||||
addTask(makeShared<LibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// FML libraries download and copy into the instance
|
||||
{
|
||||
m_tasks.append(makeShared<FMLLibrariesTask>(m_inst));
|
||||
addTask(makeShared<FMLLibrariesTask>(m_inst));
|
||||
}
|
||||
|
||||
// assets update
|
||||
{
|
||||
m_tasks.append(makeShared<AssetUpdateTask>(m_inst));
|
||||
addTask(makeShared<AssetUpdateTask>(m_inst));
|
||||
}
|
||||
|
||||
if (!m_preFailure.isEmpty()) {
|
||||
emitFailed(m_preFailure);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
void MinecraftUpdate::next()
|
||||
{
|
||||
if (m_abort) {
|
||||
emitFailed(tr("Aborted by user."));
|
||||
return;
|
||||
}
|
||||
if (m_failed_out_of_order) {
|
||||
emitFailed(m_fail_reason);
|
||||
return;
|
||||
}
|
||||
m_currentTask++;
|
||||
if (m_currentTask > 0) {
|
||||
auto task = m_tasks[m_currentTask - 1];
|
||||
disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
|
||||
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
|
||||
disconnect(task.get(), &Task::aborted, this, &Task::abort);
|
||||
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
|
||||
disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress);
|
||||
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
|
||||
disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
|
||||
}
|
||||
if (m_currentTask == m_tasks.size()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto task = m_tasks[m_currentTask];
|
||||
// if the task is already finished by the time we look at it, skip it
|
||||
if (task->isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
|
||||
next();
|
||||
}
|
||||
connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
|
||||
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
|
||||
connect(task.get(), &Task::aborted, this, &Task::abort);
|
||||
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
|
||||
connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propagateStepProgress);
|
||||
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
|
||||
connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
|
||||
// if the task is already running, do not start it again
|
||||
if (!task->isRunning()) {
|
||||
task->start();
|
||||
}
|
||||
}
|
||||
|
||||
void MinecraftUpdate::subtaskSucceeded()
|
||||
{
|
||||
if (isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
|
||||
return;
|
||||
}
|
||||
auto senderTask = QObject::sender();
|
||||
auto currentTask = m_tasks[m_currentTask].get();
|
||||
if (senderTask != currentTask) {
|
||||
qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order.";
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
void MinecraftUpdate::subtaskFailed(QString error)
|
||||
{
|
||||
if (isFinished()) {
|
||||
qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
|
||||
return;
|
||||
}
|
||||
auto senderTask = QObject::sender();
|
||||
auto currentTask = m_tasks[m_currentTask].get();
|
||||
if (senderTask != currentTask) {
|
||||
qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order.";
|
||||
m_failed_out_of_order = true;
|
||||
m_fail_reason = error;
|
||||
return;
|
||||
}
|
||||
emitFailed(error);
|
||||
}
|
||||
|
||||
bool MinecraftUpdate::abort()
|
||||
{
|
||||
if (!m_abort) {
|
||||
m_abort = true;
|
||||
auto task = m_tasks[m_currentTask];
|
||||
if (task->canAbort()) {
|
||||
return task->abort();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinecraftUpdate::canAbort() const
|
||||
{
|
||||
return true;
|
||||
SequentialTask::executeTask();
|
||||
}
|
||||
|
@ -15,43 +15,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
#include <quazip/quazip.h>
|
||||
#include "minecraft/VersionFilterData.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class MinecraftVersion;
|
||||
class MinecraftInstance;
|
||||
|
||||
// FIXME: This looks very similar to a SequentialTask. Maybe we can reduce code duplications? :^)
|
||||
|
||||
class MinecraftUpdate : public Task {
|
||||
// this needs to be a task because components->reload does stuff that may block
|
||||
class MinecraftUpdate : public SequentialTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftUpdate(MinecraftInstance* inst, QObject* parent = 0);
|
||||
virtual ~MinecraftUpdate() {};
|
||||
virtual ~MinecraftUpdate() = default;
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
|
||||
private slots:
|
||||
bool abort() override;
|
||||
void subtaskSucceeded();
|
||||
void subtaskFailed(QString error);
|
||||
|
||||
private:
|
||||
void next();
|
||||
|
||||
private:
|
||||
MinecraftInstance* m_inst = nullptr;
|
||||
QList<Task::Ptr> m_tasks;
|
||||
QString m_preFailure;
|
||||
int m_currentTask = -1;
|
||||
bool m_abort = false;
|
||||
bool m_failed_out_of_order = false;
|
||||
QString m_fail_reason;
|
||||
};
|
||||
|
@ -185,6 +185,9 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFi
|
||||
out->compatibleJavaMajors.append(requireInteger(compatible));
|
||||
}
|
||||
}
|
||||
if (in.contains("compatibleJavaName")) {
|
||||
out->compatibleJavaName = requireString(in.value("compatibleJavaName"));
|
||||
}
|
||||
|
||||
if (in.contains("downloads")) {
|
||||
auto downloadsObj = requireObject(in, "downloads");
|
||||
@ -259,6 +262,9 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
|
||||
}
|
||||
out.insert("compatibleJavaMajors", compatibleJavaMajorsOut);
|
||||
}
|
||||
if (!in->compatibleJavaName.isEmpty()) {
|
||||
writeString(out, "compatibleJavaName", in->compatibleJavaName);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch)
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include <Json.h>
|
||||
#include <minecraft/MojangVersionFormat.h>
|
||||
#include <QList>
|
||||
#include "java/JavaMetadata.h"
|
||||
#include "minecraft/Agent.h"
|
||||
#include "minecraft/ParseUtils.h"
|
||||
|
||||
@ -255,6 +257,13 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc
|
||||
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 */
|
||||
if (root.contains("tweakers")) {
|
||||
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
|
||||
|
@ -73,6 +73,7 @@ void VersionFile::applyTo(LaunchProfile* profile, const RuntimeContext& runtimeC
|
||||
profile->applyMods(mods);
|
||||
profile->applyTraits(traits);
|
||||
profile->applyCompatibleJavaMajors(compatibleJavaMajors);
|
||||
profile->applyCompatibleJavaName(compatibleJavaName);
|
||||
|
||||
for (auto library : libraries) {
|
||||
profile->applyLibrary(library, runtimeContext);
|
||||
|
@ -36,6 +36,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@ -45,6 +47,7 @@
|
||||
#include "Agent.h"
|
||||
#include "Library.h"
|
||||
#include "ProblemProvider.h"
|
||||
#include "java/JavaMetadata.h"
|
||||
#include "minecraft/Rule.h"
|
||||
|
||||
class PackProfile;
|
||||
@ -98,6 +101,9 @@ class VersionFile : public ProblemContainer {
|
||||
/// Mojang: list of compatible java majors
|
||||
QList<int> compatibleJavaMajors;
|
||||
|
||||
/// Mojang: the name of recommended java version
|
||||
QString compatibleJavaName;
|
||||
|
||||
/// Mojang: type of the Minecraft version
|
||||
QString type;
|
||||
|
||||
@ -149,6 +155,8 @@ class VersionFile : public ProblemContainer {
|
||||
/// is volatile -- may be removed as soon as it is no longer needed by something else
|
||||
bool m_volatile = false;
|
||||
|
||||
QList<Java::MetadataPtr> runtimes;
|
||||
|
||||
public:
|
||||
// Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more.
|
||||
QMap<QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads;
|
||||
|
@ -74,6 +74,8 @@ void MSADeviceCodeStep::perform()
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAutorizationFinished);
|
||||
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
|
242
launcher/minecraft/launch/AutoInstallJava.cpp
Normal file
242
launcher/minecraft/launch/AutoInstallJava.cpp
Normal 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;
|
||||
}
|
68
launcher/minecraft/launch/AutoInstallJava.h
Normal file
68
launcher/minecraft/launch/AutoInstallJava.h
Normal 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;
|
||||
};
|
@ -90,7 +90,7 @@ void LauncherPartLaunch::executeTask()
|
||||
}
|
||||
}
|
||||
|
||||
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
|
||||
m_launchScript = minecraftInstance->createLaunchScript(m_session, m_targetToJoin);
|
||||
QStringList args = minecraftInstance->javaArguments();
|
||||
QString allArgs = args.join(", ");
|
||||
emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher);
|
||||
|
@ -19,13 +19,13 @@
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <minecraft/auth/AuthSession.h>
|
||||
|
||||
#include "MinecraftServerTarget.h"
|
||||
#include "MinecraftTarget.h"
|
||||
|
||||
class LauncherPartLaunch : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LauncherPartLaunch(LaunchTask* parent);
|
||||
virtual ~LauncherPartLaunch() {};
|
||||
virtual ~LauncherPartLaunch() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
@ -34,7 +34,7 @@ class LauncherPartLaunch : public LaunchStep {
|
||||
void setWorkingDirectory(const QString& wd);
|
||||
void setAuthSession(AuthSessionPtr session) { m_session = session; }
|
||||
|
||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
|
||||
void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); }
|
||||
|
||||
private slots:
|
||||
void on_state(LoggedProcess::State state);
|
||||
@ -44,7 +44,7 @@ class LauncherPartLaunch : public LaunchStep {
|
||||
QString m_command;
|
||||
AuthSessionPtr m_session;
|
||||
QString m_launchScript;
|
||||
MinecraftServerTargetPtr m_serverToJoin;
|
||||
MinecraftTarget::Ptr m_targetToJoin;
|
||||
|
||||
bool mayProceed = false;
|
||||
};
|
||||
|
@ -13,13 +13,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MinecraftServerTarget.h"
|
||||
#include "MinecraftTarget.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
// FIXME: the way this is written, it can't ever do any sort of validation and can accept total junk
|
||||
MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress)
|
||||
MinecraftTarget MinecraftTarget::parse(const QString& fullAddress, bool useWorld)
|
||||
{
|
||||
if (useWorld) {
|
||||
MinecraftTarget target;
|
||||
target.world = fullAddress;
|
||||
return target;
|
||||
}
|
||||
QStringList split = fullAddress.split(":");
|
||||
|
||||
// The logic below replicates the exact logic minecraft uses for parsing server addresses.
|
||||
@ -56,5 +61,5 @@ MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress)
|
||||
}
|
||||
}
|
||||
|
||||
return MinecraftServerTarget{ realAddress, realPort };
|
||||
return MinecraftTarget{ realAddress, realPort };
|
||||
}
|
@ -19,11 +19,11 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct MinecraftServerTarget {
|
||||
struct MinecraftTarget {
|
||||
QString address;
|
||||
quint16 port;
|
||||
|
||||
static MinecraftServerTarget parse(const QString& fullAddress);
|
||||
QString world;
|
||||
static MinecraftTarget parse(const QString& fullAddress, bool useWorld);
|
||||
using Ptr = std::shared_ptr<MinecraftTarget>;
|
||||
};
|
||||
|
||||
using MinecraftServerTargetPtr = std::shared_ptr<MinecraftServerTarget>;
|
@ -129,6 +129,6 @@ void PrintInstanceInfo::executeTask()
|
||||
#endif
|
||||
|
||||
logLines(log, MessageLevel::Launcher);
|
||||
logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::Launcher);
|
||||
logLines(instance->verboseDescription(m_session, m_targetToJoin), MessageLevel::Launcher);
|
||||
emitSucceeded();
|
||||
}
|
||||
|
@ -16,22 +16,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <memory>
|
||||
#include "minecraft/auth/AuthSession.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
||||
// FIXME: temporary wrapper for existing task.
|
||||
class PrintInstanceInfo : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
: LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {};
|
||||
virtual ~PrintInstanceInfo() {};
|
||||
explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
|
||||
: LaunchStep(parent), m_session(session), m_targetToJoin(targetToJoin) {};
|
||||
virtual ~PrintInstanceInfo() = default;
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
||||
private:
|
||||
AuthSessionPtr m_session;
|
||||
MinecraftServerTargetPtr m_serverToJoin;
|
||||
MinecraftTarget::Ptr m_targetToJoin;
|
||||
};
|
||||
|
@ -34,7 +34,12 @@
|
||||
*/
|
||||
|
||||
#include "VerifyJavaInstall.h"
|
||||
#include <memory>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "java/JavaInstall.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaVersion.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
@ -46,6 +51,15 @@ void VerifyJavaInstall::executeTask()
|
||||
auto settings = instance->settings();
|
||||
auto storedVersion = settings->get("JavaVersion").toString();
|
||||
auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool();
|
||||
auto javaArchitecture = settings->get("JavaArchitecture").toString();
|
||||
auto maxMemAlloc = settings->get("MaxMemAlloc").toInt();
|
||||
|
||||
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();
|
||||
|
||||
|
@ -32,8 +32,7 @@ void AssetUpdateTask::executeTask()
|
||||
auto hexSha1 = assets->sha1.toLatin1();
|
||||
qDebug() << "Asset index SHA1:" << hexSha1;
|
||||
auto dl = Net::ApiDownload::makeCached(indexUrl, entry);
|
||||
auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, assets->sha1));
|
||||
job->addNetAction(dl);
|
||||
|
||||
downloadJob.reset(job);
|
||||
|
@ -343,9 +343,7 @@ QString PackInstallTask::getVersionForLoader(QString uid)
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (!vlist->isLoaded()) {
|
||||
vlist->load(Net::Mode::Online);
|
||||
}
|
||||
vlist->waitToLoad();
|
||||
|
||||
if (m_version.loader.recommended || m_version.loader.latest) {
|
||||
for (int i = 0; i < vlist->versions().size(); i++) {
|
||||
@ -638,8 +636,7 @@ void PackInstallTask::installConfigs()
|
||||
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||
if (!m_version.configs.sha1.isEmpty()) {
|
||||
auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, m_version.configs.sha1));
|
||||
}
|
||||
jobPtr->addNetAction(dl);
|
||||
archivePath = entry->getFullPath();
|
||||
@ -758,8 +755,7 @@ void PackInstallTask::downloadMods()
|
||||
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||
if (!mod.md5.isEmpty()) {
|
||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5));
|
||||
}
|
||||
jobPtr->addNetAction(dl);
|
||||
} else if (mod.type == ModType::Decomp) {
|
||||
@ -769,8 +765,7 @@ void PackInstallTask::downloadMods()
|
||||
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||
if (!mod.md5.isEmpty()) {
|
||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5));
|
||||
}
|
||||
jobPtr->addNetAction(dl);
|
||||
} else {
|
||||
@ -783,8 +778,7 @@ void PackInstallTask::downloadMods()
|
||||
|
||||
auto dl = Net::ApiDownload::makeCached(url, entry);
|
||||
if (!mod.md5.isEmpty()) {
|
||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5));
|
||||
}
|
||||
jobPtr->addNetAction(dl);
|
||||
|
||||
@ -1075,36 +1069,7 @@ void PackInstallTask::install()
|
||||
|
||||
static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version)
|
||||
{
|
||||
auto vlist = APPLICATION->metadataIndex()->get(uid);
|
||||
if (!vlist)
|
||||
return {};
|
||||
|
||||
if (!vlist->isLoaded()) {
|
||||
QEventLoop loadVersionLoop;
|
||||
auto task = vlist->getLoadTask();
|
||||
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||
if (!task->isRunning())
|
||||
task->start();
|
||||
|
||||
loadVersionLoop.exec();
|
||||
}
|
||||
|
||||
auto ver = vlist->getVersion(version);
|
||||
if (!ver)
|
||||
return {};
|
||||
|
||||
if (!ver->isLoaded()) {
|
||||
QEventLoop loadVersionLoop;
|
||||
ver->load(Net::Mode::Online);
|
||||
auto task = ver->getCurrentTask();
|
||||
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||
if (!task->isRunning())
|
||||
task->start();
|
||||
|
||||
loadVersionLoop.exec();
|
||||
}
|
||||
|
||||
return ver;
|
||||
return APPLICATION->metadataIndex()->getLoadedVersion(uid, version);
|
||||
}
|
||||
|
||||
} // namespace ATLauncher
|
||||
|
@ -246,7 +246,7 @@ void FlamePackExportTask::makeApiRequest()
|
||||
pendingHashes.clear();
|
||||
getProjectsInfo();
|
||||
});
|
||||
connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::getProjectsInfo);
|
||||
connect(task.get(), &Task::failed, this, &FlamePackExportTask::getProjectsInfo);
|
||||
task->start();
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "ModrinthPackExportTask.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
@ -28,6 +29,7 @@
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
|
||||
const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" });
|
||||
@ -154,8 +156,8 @@ void ModrinthPackExportTask::makeApiRequest()
|
||||
setStatus(tr("Finding versions for hashes..."));
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
task = api.currentVersions(pendingHashes.values(), "sha512", response);
|
||||
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
|
||||
connect(task.get(), &NetJob::failed, this, &ModrinthPackExportTask::emitFailed);
|
||||
connect(task.get(), &Task::succeeded, [this, response]() { parseApiResponse(response); });
|
||||
connect(task.get(), &Task::failed, this, &ModrinthPackExportTask::emitFailed);
|
||||
task->start();
|
||||
}
|
||||
}
|
||||
|
@ -114,8 +114,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
|
||||
|
||||
auto dl = Net::ApiDownload::makeFile(mod.url, path);
|
||||
if (!mod.md5.isEmpty()) {
|
||||
auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5));
|
||||
}
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
|
||||
|
@ -43,6 +43,9 @@
|
||||
namespace Net {
|
||||
class ChecksumValidator : public Validator {
|
||||
public:
|
||||
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QString expectedHex)
|
||||
: Net::ChecksumValidator(algorithm, QByteArray::fromHex(expectedHex.toLatin1()))
|
||||
{}
|
||||
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
|
||||
: m_checksum(algorithm), m_expected(expected) {};
|
||||
virtual ~ChecksumValidator() = default;
|
||||
|
@ -353,5 +353,11 @@
|
||||
<file>scalable/instances/neoforged.svg</file>
|
||||
<file>128x128/instances/forge.png</file> <!-- LGPL3 Forge Development LLC -->
|
||||
<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>
|
||||
</RCC>
|
||||
|
180
launcher/resources/multimc/scalable/adoptium.svg
Normal file
180
launcher/resources/multimc/scalable/adoptium.svg
Normal 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 |
17
launcher/resources/multimc/scalable/azul.svg
Normal file
17
launcher/resources/multimc/scalable/azul.svg
Normal 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 |
55
launcher/resources/multimc/scalable/mojang.svg
Normal file
55
launcher/resources/multimc/scalable/mojang.svg
Normal 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" /> <desc
|
||||
id="desc1">Created with Fabric.js 3.6.3</desc> <defs
|
||||
id="defs1"> </defs> <g
|
||||
transform="matrix(2 0 0 2 399.75 399.75)"
|
||||
id="g4"> <g
|
||||
style=""
|
||||
id="g3"> <g
|
||||
transform="matrix(3.37 0 0 3.37 0 0)"
|
||||
id="g1"> <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" /> </g> <g
|
||||
transform="matrix(3.78 0 0 3.78 0.01 0)"
|
||||
id="g2"> <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" /> </g> </g> </g> </svg>
|
After Width: | Height: | Size: 3.8 KiB |
@ -592,8 +592,7 @@ void TranslationsModel::downloadTranslation(QString key)
|
||||
entry->setStale(true);
|
||||
|
||||
auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + lang->file_name), entry);
|
||||
auto rawHash = QByteArray::fromHex(lang->file_sha1.toLatin1());
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawHash));
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->file_sha1));
|
||||
dl->setProgress(dl->getProgress(), lang->file_size);
|
||||
|
||||
d->m_dl_job.reset(new NetJob("Translation for " + key, APPLICATION->network()));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user