Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into netjob_retry
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
commit
09a10a916b
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
|
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -125,7 +125,7 @@ jobs:
|
||||
# PREPARE
|
||||
##
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
@ -164,7 +164,7 @@ jobs:
|
||||
|
||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v3.3.1
|
||||
uses: actions/cache@v3.3.2
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||
@ -620,7 +620,7 @@ jobs:
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
|
2
.github/workflows/trigger_release.yml
vendored
2
.github/workflows/trigger_release.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
path: 'PrismLauncher-source'
|
||||
|
6
.github/workflows/update-flake.yml
vendored
6
.github/workflows/update-flake.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v19
|
||||
- uses: DeterminateSystems/update-flake-lock@v20
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
|
@ -216,6 +216,18 @@ 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")
|
||||
|
||||
# Native libraries
|
||||
if(UNIX AND APPLE)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library")
|
||||
elseif(UNIX)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library")
|
||||
elseif(WIN32)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library")
|
||||
endif()
|
||||
|
||||
# API Keys
|
||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||
# of these platforms, please change these API keys beforehand.
|
||||
|
@ -110,6 +110,9 @@ Config::Config()
|
||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
|
||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||
|
@ -134,6 +134,9 @@ class Config {
|
||||
*/
|
||||
QString META_URL;
|
||||
|
||||
QString GLFW_LIBRARY_NAME;
|
||||
QString OPENAL_LIBRARY_NAME;
|
||||
|
||||
QString BUG_TRACKER_URL;
|
||||
QString TRANSLATIONS_URL;
|
||||
QString MATRIX_URL;
|
||||
|
@ -75,7 +75,6 @@ function(
|
||||
set(CLANG_WARNINGS
|
||||
-Wall
|
||||
-Wextra # reasonable and standard
|
||||
-Wextra-semi # Warn about semicolon after in-class function definition.
|
||||
-Wshadow # warn the user if a variable declaration shadows one from a parent context
|
||||
-Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
|
||||
# catch hard to track down memory errors
|
||||
|
40
flake.lock
generated
40
flake.lock
generated
@ -21,11 +21,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1690933134,
|
||||
"narHash": "sha256-ab989mN63fQZBFrkk4Q8bYxQCktuHmBIBqUG1jl6/FQ=",
|
||||
"lastModified": 1693611461,
|
||||
"narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "59cf3f1447cfc75087e7273b04b31e689a8599fb",
|
||||
"rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -89,13 +89,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1694857738,
|
||||
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1691853136,
|
||||
"narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=",
|
||||
"lastModified": 1695318763,
|
||||
"narHash": "sha256-FHVPDRP2AfvsxAdc+AsgFJevMz5VBmnZglFUMlxBkcY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f0451844bbdf545f696f029d1448de4906c7f753",
|
||||
"rev": "e12483116b3b51a185a33a272bf351e357ba9a99",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -108,11 +123,11 @@
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1690881714,
|
||||
"narHash": "sha256-h/nXluEqdiQHs1oSgkOOWF+j8gcJMWhwnZ9PFabN6q0=",
|
||||
"lastModified": 1693471703,
|
||||
"narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9e1960bc196baf6881340d53dccb203a951745a2",
|
||||
"rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -138,11 +153,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1691747570,
|
||||
"narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=",
|
||||
"lastModified": 1694364351,
|
||||
"narHash": "sha256-oadhSCqopYXxURwIA6/Anpe5IAG11q2LhvTJNP5zE6o=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a",
|
||||
"rev": "4f883a76282bc28eb952570afc3d8a1bf6f481d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -156,6 +171,7 @@
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
|
25
flake.nix
25
flake.nix
@ -4,6 +4,7 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
pre-commit-hooks = {
|
||||
url = "github:cachix/pre-commit-hooks.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@ -20,8 +21,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs:
|
||||
inputs.flake-parts.lib.mkFlake
|
||||
{inherit inputs;}
|
||||
{imports = [./nix];};
|
||||
outputs = {
|
||||
flake-parts,
|
||||
pre-commit-hooks,
|
||||
...
|
||||
} @ inputs:
|
||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||
imports = [
|
||||
pre-commit-hooks.flakeModule
|
||||
|
||||
./nix/dev.nix
|
||||
./nix/distribution.nix
|
||||
];
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
* Copyright (C) 2022 Tayou <git@tayou.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||
* Copyright (C) 2023 seth <getchoo at tuta dot io>
|
||||
*
|
||||
* 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
|
||||
@ -504,6 +503,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
m_settings->registerSetting("MenuBarInsteadOfToolBar", false);
|
||||
|
||||
m_settings->registerSetting("NumberOfConcurrentTasks", 10);
|
||||
m_settings->registerSetting("NumberOfConcurrentDownloads", 6);
|
||||
|
||||
QString defaultMonospace;
|
||||
int defaultSize = 11;
|
||||
#ifdef Q_OS_WIN32
|
||||
@ -580,12 +582,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||
|
||||
// Mod loader settings
|
||||
m_settings->registerSetting("DisableQuiltBeacon", false);
|
||||
|
||||
// Native library workarounds
|
||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||
m_settings->registerSetting("CustomOpenALPath", "");
|
||||
m_settings->registerSetting("UseNativeGLFW", false);
|
||||
m_settings->registerSetting("CustomGLFWPath", "");
|
||||
|
||||
// Peformance related options
|
||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||
@ -596,6 +597,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("ShowGameTime", true);
|
||||
m_settings->registerSetting("ShowGlobalGameTime", true);
|
||||
m_settings->registerSetting("RecordGameTime", true);
|
||||
m_settings->registerSetting("ShowGameTimeWithoutDays", false);
|
||||
|
||||
// Minecraft mods
|
||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||
@ -845,6 +847,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
|
||||
updateCapabilities();
|
||||
|
||||
detectLibraries();
|
||||
|
||||
if (createSetupWizard()) {
|
||||
return;
|
||||
}
|
||||
@ -964,7 +968,7 @@ void Application::performMainStartupAction()
|
||||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, nullptr, serverToJoin, accountToUse);
|
||||
launch(inst, true, false, serverToJoin, accountToUse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1063,7 +1067,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||
}
|
||||
}
|
||||
|
||||
launch(instance, true, false, nullptr, serverObject, accountObject);
|
||||
launch(instance, true, false, serverObject, accountObject);
|
||||
} else {
|
||||
qWarning() << "Received invalid message" << message;
|
||||
}
|
||||
@ -1104,7 +1108,6 @@ bool Application::openJsonEditor(const QString& filename)
|
||||
bool Application::launch(InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
BaseProfilerFactory* profiler,
|
||||
MinecraftServerTargetPtr serverToJoin,
|
||||
MinecraftAccountPtr accountToUse)
|
||||
{
|
||||
@ -1112,7 +1115,7 @@ bool Application::launch(InstancePtr instance,
|
||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||
} else if (instance->canLaunch()) {
|
||||
auto& extras = m_instanceExtras[instance->id()];
|
||||
auto& window = extras.window;
|
||||
auto window = extras.window;
|
||||
if (window) {
|
||||
if (!window->saveAll()) {
|
||||
return false;
|
||||
@ -1123,7 +1126,7 @@ bool Application::launch(InstancePtr instance,
|
||||
controller->setInstance(instance);
|
||||
controller->setOnline(online);
|
||||
controller->setDemo(demo);
|
||||
controller->setProfiler(profiler);
|
||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||
controller->setServerToJoin(serverToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
if (window) {
|
||||
@ -1415,6 +1418,15 @@ void Application::updateCapabilities()
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::detectLibraries()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME);
|
||||
m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME);
|
||||
qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString Application::getJarPath(QString jarFile)
|
||||
{
|
||||
QStringList potentialPaths = {
|
||||
|
@ -142,6 +142,8 @@ class Application : public QApplication {
|
||||
|
||||
void updateCapabilities();
|
||||
|
||||
void detectLibraries();
|
||||
|
||||
/*!
|
||||
* Finds and returns the full path to a jar file.
|
||||
* Returns a null-string if it could not be found.
|
||||
@ -193,7 +195,6 @@ class Application : public QApplication {
|
||||
bool launch(InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
BaseProfilerFactory* profiler = nullptr,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
bool kill(InstancePtr instance);
|
||||
@ -277,6 +278,8 @@ class Application : public QApplication {
|
||||
SetupWizard* m_setupWizard = nullptr;
|
||||
|
||||
public:
|
||||
QString m_detectedGLFWPath;
|
||||
QString m_detectedOpenALPath;
|
||||
QString m_instanceIdToLaunch;
|
||||
QString m_serverToJoin;
|
||||
QString m_profileToUse;
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -100,6 +101,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
m_settings->registerSetting("ManagedPackName", "");
|
||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||
|
||||
m_settings->registerSetting("Profiler", "");
|
||||
}
|
||||
|
||||
QString BaseInstance::getPreLaunchCommand()
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -38,6 +39,7 @@
|
||||
#include <cassert>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMenu>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QSet>
|
||||
@ -246,6 +248,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
virtual bool canEdit() const = 0;
|
||||
virtual bool canExport() const = 0;
|
||||
|
||||
virtual void populateLaunchMenu(QMenu* menu) = 0;
|
||||
|
||||
bool reloadSettings();
|
||||
|
||||
/**
|
||||
@ -282,6 +286,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
|
||||
void runningStatusChanged(bool running);
|
||||
|
||||
void profilerChanged();
|
||||
|
||||
void statusChanged(Status from, Status to);
|
||||
|
||||
protected slots:
|
||||
|
@ -1137,6 +1137,9 @@ include(CompilerWarnings)
|
||||
|
||||
# Add executable
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
if(BUILD_TESTING)
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST)
|
||||
endif()
|
||||
set_project_warnings(Launcher_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
"${Launcher_CLANG_WARNINGS}"
|
||||
|
@ -267,10 +267,7 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
|
||||
|
||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||
{
|
||||
auto fileName = fileInfo.fileName();
|
||||
auto path = relPath(fileInfo.absoluteFilePath());
|
||||
return std::any_of(m_ignoreFiles.cbegin(), m_ignoreFiles.cend(), [fileName](auto iFileName) { return fileName == iFileName; }) ||
|
||||
m_ignoreFilePaths.covers(path);
|
||||
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -361,22 +362,21 @@ void LaunchController::readyForLaunch()
|
||||
QString error;
|
||||
if (!m_profiler->check(&error)) {
|
||||
m_launcher->abort();
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
||||
emitFailed("Profiler startup failed!");
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
|
||||
return;
|
||||
}
|
||||
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||
|
||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
||||
QMessageBox msg;
|
||||
QMessageBox msg(m_parentWidget);
|
||||
msg.setText(tr("The game launch is delayed until you press the "
|
||||
"button. This is the right time to setup the profiler, as the "
|
||||
"profiler server is running now.\n\n%1")
|
||||
.arg(message));
|
||||
msg.setWindowTitle(tr("Waiting."));
|
||||
msg.setIcon(QMessageBox::Information);
|
||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
||||
msg.setModal(true);
|
||||
msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
|
||||
msg.exec();
|
||||
m_launcher->proceed();
|
||||
});
|
||||
|
@ -16,19 +16,20 @@
|
||||
*/
|
||||
|
||||
#include <MMCTime.h>
|
||||
#include <qobject.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
|
||||
QString Time::prettifyDuration(int64_t duration)
|
||||
QString Time::prettifyDuration(int64_t duration, bool noDays)
|
||||
{
|
||||
int seconds = (int)(duration % 60);
|
||||
duration /= 60;
|
||||
int minutes = (int)(duration % 60);
|
||||
duration /= 60;
|
||||
int hours = (int)(duration % 24);
|
||||
int days = (int)(duration / 24);
|
||||
int hours = (int)(noDays ? duration : (duration % 24));
|
||||
int days = (int)(noDays ? 0 : (duration / 24));
|
||||
if ((hours == 0) && (days == 0)) {
|
||||
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
namespace Time {
|
||||
|
||||
QString prettifyDuration(int64_t duration);
|
||||
QString prettifyDuration(int64_t duration, bool noDays = false);
|
||||
|
||||
/**
|
||||
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
|
||||
|
@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@ -26,6 +27,15 @@
|
||||
#include "Json.h"
|
||||
#include "MangoHud.h"
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define UNDEF_GNU_SOURCE
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#include <linux/limits.h>
|
||||
#endif
|
||||
|
||||
namespace MangoHud {
|
||||
|
||||
QString getLibraryString()
|
||||
@ -106,4 +116,37 @@ QString getLibraryString()
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName)
|
||||
{
|
||||
#ifdef __GLIBC__
|
||||
const char* library = libName.toLocal8Bit().constData();
|
||||
|
||||
void* handle = dlopen(library, RTLD_NOW);
|
||||
if (!handle) {
|
||||
qCritical() << "dlopen() failed:" << dlerror();
|
||||
return {};
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) {
|
||||
qCritical() << "dlinfo() failed:" << dlerror();
|
||||
dlclose(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto fullPath = FS::PathCombine(QString(path), libName);
|
||||
|
||||
dlclose(handle);
|
||||
return fullPath;
|
||||
#else
|
||||
qWarning() << "MangoHud::findLibrary is not implemented on this platform";
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
} // namespace MangoHud
|
||||
|
||||
#ifdef UNDEF_GNU_SOURCE
|
||||
#undef _GNU_SOURCE
|
||||
#undef UNDEF_GNU_SOURCE
|
||||
#endif
|
||||
|
@ -24,4 +24,6 @@
|
||||
namespace MangoHud {
|
||||
|
||||
QString getLibraryString();
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName);
|
||||
} // namespace MangoHud
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -62,6 +63,7 @@ class NullInstance : public BaseInstance {
|
||||
bool canExport() const override { return false; }
|
||||
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 out;
|
||||
|
@ -103,14 +103,8 @@ class Version {
|
||||
|
||||
QString m_fullString;
|
||||
|
||||
[[nodiscard]] inline bool isAppendix() const
|
||||
{
|
||||
return m_stringPart.startsWith('+');
|
||||
}
|
||||
[[nodiscard]] inline bool isPreRelease() const
|
||||
{
|
||||
return m_stringPart.startsWith('-') && m_stringPart.length() > 1;
|
||||
}
|
||||
[[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
|
||||
[[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
|
||||
|
||||
inline bool operator==(const Section& other) const
|
||||
{
|
||||
@ -156,14 +150,8 @@ class Version {
|
||||
return m_fullString < other.m_fullString;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Section& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
inline bool operator>(const Section& other) const
|
||||
{
|
||||
return !(*this < other || *this == other);
|
||||
}
|
||||
inline bool operator!=(const Section& other) const { return !(*this == other); }
|
||||
inline bool operator>(const Section& other) const { return !(*this < other || *this == other); }
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
#include "Component.h"
|
||||
#include "ComponentUpdateTask_p.h"
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include "PackProfile.h"
|
||||
#include "PackProfile_p.h"
|
||||
#include "Version.h"
|
||||
#include "cassert"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/ProfileUtils.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -3,8 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (c) 2023 seth <getchoo at tuta dot io>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -88,6 +87,10 @@
|
||||
#include "minecraft/gameoptions/GameOptions.h"
|
||||
#include "minecraft/update/FoldersTask.h"
|
||||
|
||||
#include "tools/BaseProfiler.h"
|
||||
|
||||
#include <QActionGroup>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "MangoHud.h"
|
||||
#endif
|
||||
@ -166,7 +169,9 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
// Native library workarounds
|
||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("CustomOpenALPath"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
|
||||
|
||||
// Peformance related options
|
||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
||||
@ -179,10 +184,6 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
|
||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
|
||||
|
||||
// Mod loader specific options
|
||||
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
|
||||
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
|
||||
|
||||
m_settings->set("InstanceType", "OneSix");
|
||||
}
|
||||
|
||||
@ -194,6 +195,12 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
m_settings->registerSetting("UseAccountForInstance", false);
|
||||
m_settings->registerSetting("InstanceAccountId", "");
|
||||
|
||||
m_settings->registerSetting("ExportName", "");
|
||||
m_settings->registerSetting("ExportVersion", "1.0.0");
|
||||
m_settings->registerSetting("ExportSummary", "");
|
||||
m_settings->registerSetting("ExportAuthor", "");
|
||||
m_settings->registerSetting("ExportOptionalFiles", true);
|
||||
|
||||
qDebug() << "Instance-type specific settings were loaded!";
|
||||
|
||||
setSpecificSettingsLoaded(true);
|
||||
@ -229,6 +236,50 @@ QSet<QString> MinecraftInstance::traits() const
|
||||
return profile->getTraits();
|
||||
}
|
||||
|
||||
// FIXME: move UI code out of MinecraftInstance
|
||||
void MinecraftInstance::populateLaunchMenu(QMenu* menu)
|
||||
{
|
||||
QAction* normalLaunch = menu->addAction(tr("&Launch"));
|
||||
normalLaunch->setShortcut(QKeySequence::Open);
|
||||
QAction* normalLaunchOffline = menu->addAction(tr("Launch &Offline"));
|
||||
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
|
||||
QAction* normalLaunchDemo = menu->addAction(tr("Launch &Demo"));
|
||||
normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
|
||||
|
||||
normalLaunchDemo->setEnabled(supportsDemo());
|
||||
|
||||
connect(normalLaunch, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this()); });
|
||||
connect(normalLaunchOffline, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, false); });
|
||||
connect(normalLaunchDemo, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, true); });
|
||||
|
||||
QString profilersTitle = tr("Profilers");
|
||||
menu->addSeparator()->setText(profilersTitle);
|
||||
|
||||
auto profilers = new QActionGroup(menu);
|
||||
profilers->setExclusive(true);
|
||||
connect(profilers, &QActionGroup::triggered, [this](QAction* action) {
|
||||
settings()->set("Profiler", action->data());
|
||||
emit profilerChanged();
|
||||
});
|
||||
|
||||
QAction* noProfilerAction = menu->addAction(tr("&No Profiler"));
|
||||
noProfilerAction->setData("");
|
||||
noProfilerAction->setCheckable(true);
|
||||
noProfilerAction->setChecked(true);
|
||||
profilers->addAction(noProfilerAction);
|
||||
|
||||
for (auto profiler = APPLICATION->profilers().begin(); profiler != APPLICATION->profilers().end(); profiler++) {
|
||||
QAction* profilerAction = menu->addAction(profiler.value()->name());
|
||||
profilers->addAction(profilerAction);
|
||||
profilerAction->setData(profiler.key());
|
||||
profilerAction->setCheckable(true);
|
||||
profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key());
|
||||
|
||||
QString error;
|
||||
profilerAction->setEnabled(profiler.value()->check(&error));
|
||||
}
|
||||
}
|
||||
|
||||
QString MinecraftInstance::gameRoot() const
|
||||
{
|
||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
||||
@ -260,7 +311,7 @@ QString MinecraftInstance::getLocalLibraryPath() const
|
||||
bool MinecraftInstance::supportsDemo() const
|
||||
{
|
||||
Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") };
|
||||
// Demo mode was introduced in 1.3.1: https://minecraft.fandom.com/wiki/Demo_mode#History
|
||||
// Demo mode was introduced in 1.3.1: https://minecraft.wiki/w/Demo_mode#History
|
||||
// FIXME: Due to Version constraints atm, this can't handle well non-release versions
|
||||
return instance_ver >= Version("1.3.1");
|
||||
}
|
||||
@ -385,10 +436,31 @@ QStringList MinecraftInstance::extraArguments()
|
||||
}
|
||||
|
||||
{
|
||||
const auto loaders = version->getModLoaders();
|
||||
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
|
||||
list.append("-Dloader.disable_beacon=true");
|
||||
QString openALPath;
|
||||
QString glfwPath;
|
||||
|
||||
if (settings()->get("UseNativeOpenAL").toBool()) {
|
||||
openALPath = APPLICATION->m_detectedOpenALPath;
|
||||
auto customPath = settings()->get("CustomOpenALPath").toString();
|
||||
if (!customPath.isEmpty())
|
||||
openALPath = customPath;
|
||||
}
|
||||
if (settings()->get("UseNativeGLFW").toBool()) {
|
||||
glfwPath = APPLICATION->m_detectedGLFWPath;
|
||||
auto customPath = settings()->get("CustomGLFWPath").toString();
|
||||
if (!customPath.isEmpty())
|
||||
glfwPath = customPath;
|
||||
}
|
||||
|
||||
QFileInfo openALInfo(openALPath);
|
||||
QFileInfo glfwInfo(glfwPath);
|
||||
|
||||
if (!openALPath.isEmpty() && openALInfo.exists())
|
||||
list.append("-Dorg.lwjgl.openal.libname=" + openALInfo.absoluteFilePath());
|
||||
if (!glfwPath.isEmpty() && glfwInfo.exists())
|
||||
list.append("-Dorg.lwjgl.glfw.libname=" + glfwInfo.absoluteFilePath());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -868,13 +940,16 @@ QString MinecraftInstance::getStatusbarDescription()
|
||||
if (m_settings->get("ShowGameTime").toBool()) {
|
||||
if (lastTimePlayed() > 0) {
|
||||
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
||||
description.append(tr(", last played on %1 for %2")
|
||||
description.append(
|
||||
tr(", last played on %1 for %2")
|
||||
.arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
|
||||
.arg(Time::prettifyDuration(lastTimePlayed())));
|
||||
.arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||
}
|
||||
|
||||
if (totalTimePlayed() > 0) {
|
||||
description.append(tr(", total played for %1").arg(Time::prettifyDuration(totalTimePlayed())));
|
||||
description.append(
|
||||
tr(", total played for %1")
|
||||
.arg(Time::prettifyDuration(totalTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||
}
|
||||
}
|
||||
if (hasCrashed()) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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
|
||||
@ -70,6 +70,8 @@ class MinecraftInstance : public BaseInstance {
|
||||
|
||||
bool canExport() const override { return true; }
|
||||
|
||||
void populateLaunchMenu(QMenu* menu) override;
|
||||
|
||||
////// Directories and files //////
|
||||
QString jarModsDir() const;
|
||||
QString resourcePacksDir() const;
|
||||
|
@ -58,14 +58,14 @@
|
||||
#include "ComponentUpdateTask.h"
|
||||
#include "PackProfile.h"
|
||||
#include "PackProfile_p.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
|
||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge },
|
||||
{ "net.fabricmc.fabric-loader", ResourceAPI::Fabric },
|
||||
{ "org.quiltmc.quilt-loader", ResourceAPI::Quilt },
|
||||
{ "com.mumfrey.liteloader", ResourceAPI::LiteLoader } };
|
||||
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
|
||||
{ "net.minecraftforge", ModPlatform::Forge },
|
||||
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
|
||||
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
|
||||
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
|
||||
|
||||
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
|
||||
{
|
||||
@ -989,12 +989,12 @@ void PackProfile::disableInteraction(bool disable)
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
{
|
||||
ResourceAPI::ModLoaderTypes result;
|
||||
ModPlatform::ModLoaderTypes result;
|
||||
bool has_any_loader = false;
|
||||
|
||||
QMapIterator<QString, ResourceAPI::ModLoaderType> i(modloaderMapping);
|
||||
QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
|
||||
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
@ -1008,3 +1008,18 @@ std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getSupportedModLoaders()
|
||||
{
|
||||
auto loadersOpt = getModLoaders();
|
||||
if (!loadersOpt.has_value())
|
||||
return loadersOpt;
|
||||
auto loaders = loadersOpt.value();
|
||||
// TODO: remove this or add version condition once Quilt drops official Fabric support
|
||||
if (loaders & ModPlatform::Quilt)
|
||||
loaders |= ModPlatform::Fabric;
|
||||
// TODO: remove this or add version condition once NeoForge drops official Forge support
|
||||
if (loaders & ModPlatform::NeoForge)
|
||||
loaders |= ModPlatform::Forge;
|
||||
return loaders;
|
||||
}
|
||||
|
@ -44,14 +44,11 @@
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "Component.h"
|
||||
#include "LaunchProfile.h"
|
||||
#include "Library.h"
|
||||
#include "MojangDownloadInfo.h"
|
||||
#include "ProfileUtils.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
@ -146,7 +143,9 @@ class PackProfile : public QAbstractListModel {
|
||||
// todo(merged): is this the best approach
|
||||
void appendComponent(ComponentPtr component);
|
||||
|
||||
std::optional<ResourceAPI::ModLoaderTypes> getModLoaders();
|
||||
std::optional<ModPlatform::ModLoaderTypes> getModLoaders();
|
||||
// this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge)
|
||||
std::optional<ModPlatform::ModLoaderTypes> getSupportedModLoaders();
|
||||
|
||||
private:
|
||||
void scheduleSave();
|
||||
|
@ -415,7 +415,7 @@ Qt::ItemFlags AccountList::flags(const QModelIndex& index) const
|
||||
|
||||
bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role)
|
||||
{
|
||||
if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) {
|
||||
if (idx.row() < 0 || idx.row() >= rowCount(idx.parent()) || !idx.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -423,7 +423,8 @@ bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int rol
|
||||
if (value == Qt::Checked) {
|
||||
MinecraftAccountPtr account = at(idx.row());
|
||||
setDefaultAccount(account);
|
||||
}
|
||||
} else if (m_defaultAccount == at(idx.row()))
|
||||
setDefaultAccount(nullptr);
|
||||
}
|
||||
|
||||
emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));
|
||||
|
@ -39,7 +39,7 @@ static QString replaceSuffix(QString target, const QString& suffix, const QStrin
|
||||
return target + replacement;
|
||||
}
|
||||
|
||||
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW)
|
||||
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack)
|
||||
{
|
||||
QuaZip zip(source);
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
@ -52,12 +52,6 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH
|
||||
do {
|
||||
QString name = zip.getCurrentFileName();
|
||||
auto lowercase = name.toLower();
|
||||
if (nativeGLFW && name.contains("glfw")) {
|
||||
continue;
|
||||
}
|
||||
if (nativeOpenAL && name.contains("openal")) {
|
||||
continue;
|
||||
}
|
||||
if (applyJnilibHack) {
|
||||
name = replaceSuffix(name, ".jnilib", ".dylib");
|
||||
}
|
||||
@ -83,14 +77,12 @@ void ExtractNatives::executeTask()
|
||||
return;
|
||||
}
|
||||
auto settings = minecraftInstance->settings();
|
||||
bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
|
||||
bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
|
||||
|
||||
auto outputPath = minecraftInstance->getNativePath();
|
||||
auto javaVersion = minecraftInstance->getJavaVersion();
|
||||
bool jniHackEnabled = javaVersion.major() >= 8;
|
||||
for (const auto& source : toExtract) {
|
||||
if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW)) {
|
||||
if (!unzipNatives(source, outputPath, jniHackEnabled)) {
|
||||
const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
|
||||
emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
|
||||
emitFailed(tr(reason).arg(source, outputPath));
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "Version.h"
|
||||
|
||||
// Values taken from:
|
||||
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22
|
||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||
{ 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } },
|
||||
{ 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } },
|
||||
|
@ -63,7 +63,7 @@ class DataPack : public Resource {
|
||||
mutable QMutex m_data_lock;
|
||||
|
||||
/* The 'version' of a data pack, as defined in the pack.mcmeta file.
|
||||
* See https://minecraft.fandom.com/wiki/Data_pack#pack.mcmeta
|
||||
* See https://minecraft.wiki/w/Data_pack#pack.mcmeta
|
||||
*/
|
||||
int m_pack_format = 0;
|
||||
|
||||
|
@ -132,15 +132,21 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -
|
||||
if (!preserve_metadata) {
|
||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||
|
||||
destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
return Resource::destroy(attempt_trash);
|
||||
}
|
||||
|
||||
void Mod::destroyMetadata(QDir& index_dir)
|
||||
{
|
||||
if (metadata()) {
|
||||
Metadata::remove(index_dir, metadata()->slug);
|
||||
} else {
|
||||
auto n = name();
|
||||
Metadata::remove(index_dir, n);
|
||||
}
|
||||
}
|
||||
|
||||
return Resource::destroy(attempt_trash);
|
||||
m_local_details.metadata = nullptr;
|
||||
}
|
||||
|
||||
auto Mod::details() const -> const ModDetails&
|
||||
@ -246,7 +252,8 @@ void Mod::setIcon(QImage new_image) const
|
||||
PixmapCache::remove(m_pack_image_cache_key.key);
|
||||
|
||||
// scale the image to avoid flooding the pixmapcache
|
||||
auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||
auto pixmap =
|
||||
QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
|
||||
|
||||
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
|
||||
m_pack_image_cache_key.was_ever_used = true;
|
||||
@ -259,7 +266,7 @@ QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const
|
||||
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
||||
if (size.isNull())
|
||||
return cached_image;
|
||||
return cached_image.scaled(size, mode);
|
||||
return cached_image.scaled(size, mode, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// No valid image we can get
|
||||
|
@ -93,6 +93,8 @@ class Mod : public Resource {
|
||||
|
||||
// Delete all the files of this mod
|
||||
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
||||
// Delete the metadata only
|
||||
void destroyMetadata(QDir& index_dir);
|
||||
|
||||
void finishResolvingWithDetails(ModDetails&& details);
|
||||
|
||||
|
@ -51,8 +51,13 @@
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
|
||||
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
|
||||
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
||||
@ -228,6 +233,25 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderModel::deleteModsMetadata(const QModelIndexList& indexes)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
return true;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0) {
|
||||
continue;
|
||||
}
|
||||
auto m = at(i.row());
|
||||
auto index_dir = indexDir();
|
||||
m->destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderModel::isValid()
|
||||
{
|
||||
return m_dir.exists() && m_dir.isReadable();
|
||||
@ -309,3 +333,47 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
|
||||
|
||||
emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
|
||||
}
|
||||
|
||||
static const FlameAPI flameAPI;
|
||||
bool ModFolderModel::installMod(QString file_path, ModPlatform::IndexedVersion& vers)
|
||||
{
|
||||
if (vers.addonId.isValid()) {
|
||||
ModPlatform::IndexedPack pack{
|
||||
vers.addonId,
|
||||
ModPlatform::ResourceProvider::FLAME,
|
||||
};
|
||||
|
||||
QEventLoop loop;
|
||||
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
auto job = flameAPI.getProject(vers.addonId.toString(), response);
|
||||
|
||||
QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); });
|
||||
QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit);
|
||||
QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qDebug() << *response;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto obj = Json::requireObject(Json::requireObject(doc), "data");
|
||||
FlameMod::loadIndexedPack(pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod info: " << e.cause();
|
||||
}
|
||||
LocalModUpdateTask update_metadata(indexDir(), pack, vers);
|
||||
QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit);
|
||||
update_metadata.start();
|
||||
});
|
||||
|
||||
job->start();
|
||||
|
||||
loop.exec();
|
||||
}
|
||||
return ResourceFolderModel::installResource(file_path);
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class LegacyInstance;
|
||||
class BaseInstance;
|
||||
@ -75,10 +76,12 @@ class ModFolderModel : public ResourceFolderModel {
|
||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||
|
||||
bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); }
|
||||
bool installMod(QString file_path, ModPlatform::IndexedVersion& vers);
|
||||
bool uninstallMod(const QString& filename, bool preserve_metadata = false);
|
||||
|
||||
/// Deletes all the selected mods
|
||||
bool deleteMods(const QModelIndexList& indexes);
|
||||
bool deleteModsMetadata(const QModelIndexList& indexes);
|
||||
|
||||
bool isValid();
|
||||
|
||||
|
@ -33,6 +33,10 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObje
|
||||
|
||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
|
||||
connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); });
|
||||
#ifndef LAUNCHER_TEST
|
||||
// in tests the application macro doesn't work
|
||||
m_helper_thread_task.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
#endif
|
||||
}
|
||||
|
||||
ResourceFolderModel::~ResourceFolderModel()
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
||||
|
||||
// Values taken from:
|
||||
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
|
||||
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
|
||||
@ -50,7 +50,8 @@ void ResourcePack::setImage(QImage new_image) const
|
||||
PixmapCache::instance().remove(m_pack_image_cache_key.key);
|
||||
|
||||
// scale the image to avoid flooding the pixmapcache
|
||||
auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||
auto pixmap =
|
||||
QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
|
||||
|
||||
m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap);
|
||||
m_pack_image_cache_key.was_ever_used = true;
|
||||
@ -68,7 +69,7 @@ QPixmap ResourcePack::image(QSize size, Qt::AspectRatioMode mode) const
|
||||
if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) {
|
||||
if (size.isNull())
|
||||
return cached_image;
|
||||
return cached_image.scaled(size, mode);
|
||||
return cached_image.scaled(size, mode, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// No valid image we can get
|
||||
|
@ -51,7 +51,7 @@ class ResourcePack : public Resource {
|
||||
mutable QMutex m_data_lock;
|
||||
|
||||
/* The 'version' of a resource pack, as defined in the pack.mcmeta file.
|
||||
* See https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
* See https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
*/
|
||||
int m_pack_format = 0;
|
||||
|
||||
|
@ -44,7 +44,8 @@ void TexturePack::setImage(QImage new_image) const
|
||||
PixmapCache::remove(m_pack_image_cache_key.key);
|
||||
|
||||
// scale the image to avoid flooding the pixmapcache
|
||||
auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding));
|
||||
auto pixmap =
|
||||
QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
|
||||
|
||||
m_pack_image_cache_key.key = PixmapCache::insert(pixmap);
|
||||
m_pack_image_cache_key.was_ever_used = true;
|
||||
@ -56,7 +57,7 @@ QPixmap TexturePack::image(QSize size, Qt::AspectRatioMode mode) const
|
||||
if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) {
|
||||
if (size.isNull())
|
||||
return cached_image;
|
||||
return cached_image.scaled(size, mode);
|
||||
return cached_image.scaled(size, mode, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// No valid image we can get
|
||||
|
@ -39,9 +39,9 @@ static Version mcVersion(BaseInstance* inst)
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
|
||||
}
|
||||
|
||||
static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
{
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders().value();
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
|
||||
}
|
||||
|
||||
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||
@ -75,7 +75,7 @@ void GetModDependenciesTask::prepare()
|
||||
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
||||
const ModPlatform::ResourceProvider providerName)
|
||||
{
|
||||
if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) {
|
||||
if (auto isQuilt = m_loaderType & ModPlatform::Quilt; isQuilt || m_loaderType & ModPlatform::Fabric) {
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) {
|
||||
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
|
||||
@ -191,7 +191,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
}
|
||||
pDep->version = provider.mod->loadDependencyVersions(dep, arr);
|
||||
if (!pDep->version.addonId.isValid()) {
|
||||
if (m_loaderType & ResourceAPI::Quilt) { // falback for quilt
|
||||
if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||
[dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; });
|
||||
@ -201,6 +201,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
|
||||
return;
|
||||
}
|
||||
}
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Error while reading mod version empty ";
|
||||
qDebug() << doc;
|
||||
return;
|
||||
@ -250,3 +251,32 @@ void GetModDependenciesTask::removePack(const QVariant addonId)
|
||||
++it;
|
||||
#endif
|
||||
}
|
||||
|
||||
QHash<QString, QStringList> GetModDependenciesTask::getRequiredBy()
|
||||
{
|
||||
QHash<QString, QStringList> rby;
|
||||
auto fullList = m_selected + m_pack_dependencies;
|
||||
for (auto& mod : fullList) {
|
||||
auto addonId = mod->pack->addonId;
|
||||
auto provider = mod->pack->provider;
|
||||
auto version = mod->version.fileId;
|
||||
auto req = QStringList();
|
||||
for (auto& smod : fullList) {
|
||||
if (provider != smod->pack->provider)
|
||||
continue;
|
||||
auto deps = smod->version.dependencies;
|
||||
if (auto dep = std::find_if(deps.begin(), deps.end(),
|
||||
[addonId, provider, version](const ModPlatform::Dependency& d) {
|
||||
return d.type == ModPlatform::DependencyType::REQUIRED &&
|
||||
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
|
||||
? version == d.version
|
||||
: d.addonId == addonId);
|
||||
});
|
||||
dep != deps.end()) {
|
||||
req.append(smod->pack->name);
|
||||
}
|
||||
}
|
||||
rby[addonId.toString()] = req;
|
||||
}
|
||||
return rby;
|
||||
}
|
@ -62,6 +62,7 @@ class GetModDependenciesTask : public SequentialTask {
|
||||
QList<std::shared_ptr<PackDependency>> selected);
|
||||
|
||||
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
|
||||
QHash<QString, QStringList> getRequiredBy();
|
||||
|
||||
protected slots:
|
||||
Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int);
|
||||
@ -80,5 +81,5 @@ class GetModDependenciesTask : public SequentialTask {
|
||||
Provider m_modrinth_provider;
|
||||
|
||||
Version m_version;
|
||||
ResourceAPI::ModLoaderTypes m_loaderType;
|
||||
ModPlatform::ModLoaderTypes m_loaderType;
|
||||
};
|
||||
|
@ -133,7 +133,7 @@ bool processZIP(DataPack& pack, ProcessingLevel level)
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://minecraft.fandom.com/wiki/Data_pack#pack.mcmeta
|
||||
// https://minecraft.wiki/w/Data_pack#pack.mcmeta
|
||||
bool processMCMeta(DataPack& pack, QByteArray&& raw_data)
|
||||
{
|
||||
try {
|
||||
|
@ -178,7 +178,7 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level)
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
||||
bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
|
||||
{
|
||||
try {
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "tasks/Task.h"
|
||||
@ -14,7 +15,7 @@ class CheckUpdateTask : public Task {
|
||||
public:
|
||||
CheckUpdateTask(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){};
|
||||
|
||||
@ -40,6 +41,7 @@ class CheckUpdateTask : public Task {
|
||||
};
|
||||
|
||||
auto getUpdatable() -> std::vector<UpdatableMod>&& { return std::move(m_updatable); }
|
||||
auto getDependencies() -> QList<std::shared_ptr<GetModDependenciesTask::PackDependency>>&& { return std::move(m_deps); }
|
||||
|
||||
public slots:
|
||||
bool abort() override = 0;
|
||||
@ -53,8 +55,9 @@ class CheckUpdateTask : public Task {
|
||||
protected:
|
||||
QList<Mod*>& m_mods;
|
||||
std::list<Version>& m_game_versions;
|
||||
std::optional<ResourceAPI::ModLoaderTypes> m_loaders;
|
||||
std::optional<ModPlatform::ModLoaderTypes> m_loaders;
|
||||
std::shared_ptr<ModFolderModel> m_mods_folder;
|
||||
|
||||
std::vector<UpdatableMod> m_updatable;
|
||||
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> m_deps;
|
||||
};
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <MurmurHash2.h>
|
||||
#include <QDebug>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
|
||||
#include "minecraft/mod/Mod.h"
|
||||
@ -33,7 +34,7 @@ EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::Resource
|
||||
EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr)
|
||||
{
|
||||
m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", 10));
|
||||
m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()));
|
||||
for (auto* mod : mods) {
|
||||
auto hash_task = createNewHash(mod);
|
||||
if (!hash_task)
|
||||
|
@ -83,4 +83,25 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
||||
projectID.toString();
|
||||
}
|
||||
|
||||
auto getModLoaderString(ModLoaderType type) -> const QString
|
||||
{
|
||||
switch (type) {
|
||||
case NeoForge:
|
||||
return "neoforge";
|
||||
case Forge:
|
||||
return "forge";
|
||||
case Cauldron:
|
||||
return "cauldron";
|
||||
case LiteLoader:
|
||||
return "liteloader";
|
||||
case Fabric:
|
||||
return "fabric";
|
||||
case Quilt:
|
||||
return "quilt";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
@ -30,6 +30,9 @@ class QIODevice;
|
||||
|
||||
namespace ModPlatform {
|
||||
|
||||
enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 };
|
||||
Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
|
||||
|
||||
enum class ResourceProvider { MODRINTH, FLAME };
|
||||
|
||||
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
||||
@ -70,7 +73,7 @@ struct IndexedVersion {
|
||||
QString downloadUrl;
|
||||
QString date;
|
||||
QString fileName;
|
||||
QStringList loaders = {};
|
||||
ModLoaderTypes loaders = {};
|
||||
QString hash_type;
|
||||
QString hash;
|
||||
bool is_preferred = true;
|
||||
@ -128,7 +131,6 @@ struct IndexedPack {
|
||||
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
|
||||
}
|
||||
};
|
||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||
|
||||
struct OverrideDep {
|
||||
QString quilt;
|
||||
@ -148,6 +150,14 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
||||
|
||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||
|
||||
auto getModLoaderString(ModLoaderType type) -> const QString;
|
||||
|
||||
constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept
|
||||
{
|
||||
auto x = static_cast<int>(l);
|
||||
return x && !(x & (x - 1));
|
||||
}
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
||||
|
@ -54,9 +54,6 @@ class ResourceAPI {
|
||||
public:
|
||||
virtual ~ResourceAPI() = default;
|
||||
|
||||
enum ModLoaderType { Forge = 1 << 0, Cauldron = 1 << 1, LiteLoader = 1 << 2, Fabric = 1 << 3, Quilt = 1 << 4 };
|
||||
Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
|
||||
|
||||
struct SortingMethod {
|
||||
// The index of the sorting method. Used to allow for arbitrary ordering in the list of methods.
|
||||
// Used by Flame in the API request.
|
||||
@ -74,7 +71,7 @@ class ResourceAPI {
|
||||
|
||||
std::optional<QString> search;
|
||||
std::optional<SortingMethod> sorting;
|
||||
std::optional<ModLoaderTypes> loaders;
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||
std::optional<std::list<Version> > versions;
|
||||
};
|
||||
struct SearchCallbacks {
|
||||
@ -87,7 +84,7 @@ class ResourceAPI {
|
||||
ModPlatform::IndexedPack pack;
|
||||
|
||||
std::optional<std::list<Version> > mcVersions;
|
||||
std::optional<ModLoaderTypes> loaders;
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||
|
||||
VersionSearchArgs(VersionSearchArgs const&) = default;
|
||||
void operator=(VersionSearchArgs other)
|
||||
@ -108,13 +105,15 @@ class ResourceAPI {
|
||||
void operator=(ProjectInfoArgs other) { pack = other.pack; }
|
||||
};
|
||||
struct ProjectInfoCallbacks {
|
||||
std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;
|
||||
std::function<void(QJsonDocument&, const ModPlatform::IndexedPack&)> on_succeed;
|
||||
std::function<void(QString const& reason)> on_fail;
|
||||
std::function<void()> on_abort;
|
||||
};
|
||||
|
||||
struct DependencySearchArgs {
|
||||
ModPlatform::Dependency dependency;
|
||||
Version mcVersion;
|
||||
ModLoaderTypes loader;
|
||||
ModPlatform::ModLoaderTypes loader;
|
||||
};
|
||||
|
||||
struct DependencySearchCallbacks {
|
||||
@ -161,25 +160,6 @@ class ResourceAPI {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static auto getModLoaderString(ModLoaderType type) -> const QString
|
||||
{
|
||||
switch (type) {
|
||||
case Forge:
|
||||
return "forge";
|
||||
case Cauldron:
|
||||
return "cauldron";
|
||||
case LiteLoader:
|
||||
return "liteloader";
|
||||
case Fabric:
|
||||
return "fabric";
|
||||
case Quilt:
|
||||
return "quilt";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]] inline QString debugName() const { return "External resource API"; }
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "FileResolvingTask.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ApiUpload.h"
|
||||
#include "net/Upload.h"
|
||||
@ -102,7 +103,7 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
|
||||
auto output = std::make_shared<QByteArray>();
|
||||
auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output);
|
||||
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; });
|
||||
QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [&out]() { out.resolved = true; });
|
||||
|
||||
m_checkJob->addNetAction(dl);
|
||||
blockedProjects.insert(&out, output);
|
||||
@ -153,7 +154,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
|
||||
// If there's more than one mod loader for this version, we can't know for sure
|
||||
// which file is relative to each loader, so it's best to not use any one and
|
||||
// let the user download it manually.
|
||||
if (file.loaders.size() <= 1) {
|
||||
if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) {
|
||||
out->url = file.downloadUrl;
|
||||
qDebug() << "Found alternative on modrinth " << out->fileName;
|
||||
} else {
|
||||
@ -175,7 +176,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
|
||||
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
|
||||
auto dl = Net::ApiDownload::makeByteArray(url, output);
|
||||
qDebug() << "Fetching url slug for file:" << mod->fileName;
|
||||
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
|
||||
QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [block, index, output]() {
|
||||
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
||||
auto json = QJsonDocument::fromJson(*output);
|
||||
auto base =
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "FlameModIndex.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "Json.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ApiUpload.h"
|
||||
@ -131,19 +130,13 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
|
||||
auto obj = Json::requireObject(doc);
|
||||
auto arr = Json::requireArray(obj, "data");
|
||||
|
||||
QJsonObject latest_file_obj;
|
||||
ModPlatform::IndexedVersion ver_tmp;
|
||||
|
||||
for (auto file : arr) {
|
||||
auto file_obj = Json::requireObject(file);
|
||||
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
|
||||
if (file_tmp.date > ver_tmp.date) {
|
||||
ver_tmp = file_tmp;
|
||||
latest_file_obj = file_obj;
|
||||
}
|
||||
if (file_tmp.date > ver.date && (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders))
|
||||
ver = file_tmp;
|
||||
}
|
||||
|
||||
ver = FlameMod::loadIndexedPackVersion(latest_file_obj);
|
||||
} catch (Json::JsonException& e) {
|
||||
qCritical() << "Failed to parse response from a version request.";
|
||||
qCritical() << e.what();
|
||||
|
@ -24,7 +24,10 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
|
||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||
|
||||
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
|
||||
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
|
||||
{
|
||||
return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt);
|
||||
}
|
||||
|
||||
private:
|
||||
static int getClassId(ModPlatform::ResourceType type)
|
||||
@ -35,22 +38,47 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
return 6;
|
||||
case ModPlatform::ResourceType::RESOURCE_PACK:
|
||||
return 12;
|
||||
case ModPlatform::ResourceType::SHADER_PACK:
|
||||
return 6552;
|
||||
}
|
||||
}
|
||||
|
||||
static int getMappedModLoader(ModLoaderTypes loaders)
|
||||
static int getMappedModLoader(ModPlatform::ModLoaderType loaders)
|
||||
{
|
||||
// https://docs.curseforge.com/?http#tocS_ModLoaderType
|
||||
if (loaders & Forge)
|
||||
switch (loaders) {
|
||||
case ModPlatform::Forge:
|
||||
return 1;
|
||||
if (loaders & Fabric)
|
||||
case ModPlatform::Cauldron:
|
||||
return 2;
|
||||
case ModPlatform::LiteLoader:
|
||||
return 3;
|
||||
case ModPlatform::Fabric:
|
||||
return 4;
|
||||
// TODO: remove this once Quilt drops official Fabric support
|
||||
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
|
||||
return 4; // Quilt would probably be 5
|
||||
case ModPlatform::Quilt:
|
||||
return 5;
|
||||
case ModPlatform::NeoForge:
|
||||
return 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList
|
||||
{
|
||||
QStringList l;
|
||||
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt }) {
|
||||
if (types & loader) {
|
||||
l << QString::number(getMappedModLoader(loader));
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString
|
||||
{
|
||||
return "[" + getModLoaderStrings(types).join(',') + "]";
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
||||
{
|
||||
@ -67,7 +95,7 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
||||
get_arguments.append("sortOrder=desc");
|
||||
if (args.loaders.has_value())
|
||||
get_arguments.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value())));
|
||||
get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value())));
|
||||
get_arguments.append(gameVersionStr);
|
||||
|
||||
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
||||
@ -81,47 +109,27 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
||||
{
|
||||
auto addonId = args.pack.addonId.toString();
|
||||
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(addonId) };
|
||||
QString url = QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000").arg(addonId);
|
||||
|
||||
QStringList get_parameters;
|
||||
if (args.mcVersions.has_value())
|
||||
get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString()));
|
||||
url += QString("&gameVersion=%1").arg(args.mcVersions.value().front().toString());
|
||||
|
||||
if (args.loaders.has_value()) {
|
||||
int mappedModLoader = getMappedModLoader(args.loaders.value());
|
||||
|
||||
if (args.loaders.value() & Quilt) {
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
|
||||
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
|
||||
});
|
||||
if (over != overide.cend()) {
|
||||
mappedModLoader = 5;
|
||||
if (args.loaders.has_value() && ModPlatform::hasSingleModLoaderSelected(args.loaders.value())) {
|
||||
int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loaders.value())));
|
||||
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||
}
|
||||
}
|
||||
|
||||
get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader));
|
||||
}
|
||||
|
||||
return url + get_parameters.join('&');
|
||||
return url;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||
{
|
||||
auto mappedModLoader = getMappedModLoader(args.loader);
|
||||
auto addonId = args.dependency.addonId.toString();
|
||||
if (args.loader & Quilt) {
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
|
||||
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
|
||||
});
|
||||
if (over != overide.cend()) {
|
||||
mappedModLoader = 5;
|
||||
auto url =
|
||||
QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString());
|
||||
if (args.loader && ModPlatform::hasSingleModLoaderSelected(args.loader)) {
|
||||
int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loader)));
|
||||
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||
}
|
||||
}
|
||||
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3")
|
||||
.arg(addonId)
|
||||
.arg(args.mcVersion.toString())
|
||||
.arg(mappedModLoader);
|
||||
return url;
|
||||
};
|
||||
};
|
||||
|
@ -5,13 +5,12 @@
|
||||
#include <MurmurHash2.h>
|
||||
#include <memory>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
|
||||
#include "ResourceDownloadTask.h"
|
||||
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourceFolderModel.h"
|
||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
@ -156,7 +155,6 @@ void FlameCheckUpdate::executeTask()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) {
|
||||
// Fake pack with the necessary info to pass to the download task :)
|
||||
auto pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pack->name = mod->name();
|
||||
@ -167,7 +165,7 @@ void FlameCheckUpdate::executeTask()
|
||||
pack->authors.append({ author });
|
||||
pack->description = mod->description();
|
||||
pack->provider = ModPlatform::ResourceProvider::FLAME;
|
||||
|
||||
if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) {
|
||||
auto old_version = mod->version();
|
||||
if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) {
|
||||
auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt());
|
||||
@ -179,6 +177,7 @@ void FlameCheckUpdate::executeTask()
|
||||
api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()),
|
||||
ModPlatform::ResourceProvider::FLAME, download_task);
|
||||
}
|
||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, latest_ver));
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
|
@ -10,7 +10,7 @@ class FlameCheckUpdate : public CheckUpdateTask {
|
||||
public:
|
||||
FlameCheckUpdate(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||
{}
|
||||
|
@ -284,7 +284,7 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
|
||||
// filter by minecraft version, if the loader depends on a certain version.
|
||||
// not all mod loaders depend on a given Minecraft version, so we won't do this
|
||||
// filtering for those loaders.
|
||||
if (loaderType == "forge") {
|
||||
if (loaderType == "forge" || loaderType == "neoforge") {
|
||||
auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) {
|
||||
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
|
||||
});
|
||||
@ -350,7 +350,11 @@ bool FlameCreationTask::createInstance()
|
||||
|
||||
for (auto& loader : m_pack.minecraft.modLoaders) {
|
||||
auto id = loader.id;
|
||||
if (id.startsWith("forge-")) {
|
||||
if (id.startsWith("neoforge-")) {
|
||||
id.remove("neoforge-");
|
||||
loaderType = "neoforge";
|
||||
loaderUid = "net.neoforged";
|
||||
} else if (id.startsWith("forge-")) {
|
||||
id.remove("forge-");
|
||||
loaderType = "forge";
|
||||
loaderUid = "net.minecraftforge";
|
||||
|
@ -81,6 +81,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
auto loaders = profile->getSupportedModLoaders();
|
||||
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
@ -89,7 +90,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = pack.addonId;
|
||||
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
|
||||
@ -115,6 +117,19 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
||||
|
||||
if (str.contains('.'))
|
||||
file.mcVersion.append(str);
|
||||
auto loader = str.toLower();
|
||||
if (loader == "neoforge")
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
if (loader == "forge")
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
if (loader == "cauldron")
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
if (loader == "liteloader")
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
if (loader == "fabric")
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
if (loader == "quilt")
|
||||
file.loaders |= ModPlatform::Quilt;
|
||||
}
|
||||
|
||||
file.addonId = Json::requireInteger(obj, "modId");
|
||||
@ -173,8 +188,11 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
||||
return file;
|
||||
}
|
||||
|
||||
ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr)
|
||||
ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst)
|
||||
{
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
auto loaders = profile->getSupportedModLoaders();
|
||||
QVector<ModPlatform::IndexedVersion> versions;
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
@ -183,7 +201,8 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = m.addonId;
|
||||
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
versions.append(file);
|
||||
}
|
||||
|
||||
@ -192,5 +211,7 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||
if (versions.size() != 0)
|
||||
return versions.front();
|
||||
return {};
|
||||
}
|
||||
|
@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
const BaseInstance* inst);
|
||||
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion;
|
||||
} // namespace FlameMod
|
@ -28,6 +28,7 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "MMCZip.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
@ -43,12 +44,14 @@ const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" });
|
||||
FlamePackExportTask::FlamePackExportTask(const QString& name,
|
||||
const QString& version,
|
||||
const QString& author,
|
||||
bool optionalFiles,
|
||||
InstancePtr instance,
|
||||
const QString& output,
|
||||
MMCZip::FilterFunction filter)
|
||||
: name(name)
|
||||
, version(version)
|
||||
, author(author)
|
||||
, optionalFiles(optionalFiles)
|
||||
, instance(instance)
|
||||
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
||||
, gameRoot(instance->gameRoot())
|
||||
@ -100,7 +103,8 @@ void FlamePackExportTask::collectHashes()
|
||||
setStatus(tr("Finding file hashes..."));
|
||||
setProgress(1, 5);
|
||||
auto allMods = mcInstance->loaderModList()->allMods();
|
||||
ConcurrentTask::Ptr hashingTask(new ConcurrentTask(this, "MakeHashesTask", 10));
|
||||
ConcurrentTask::Ptr hashingTask(
|
||||
new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()));
|
||||
task.reset(hashingTask);
|
||||
for (const QFileInfo& file : files) {
|
||||
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
|
||||
@ -381,6 +385,7 @@ QByteArray FlamePackExportTask::generateIndex()
|
||||
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
||||
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
||||
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
||||
const ComponentPtr neoforge = profile->getComponent("net.neoforged");
|
||||
|
||||
// convert all available components to mrpack dependencies
|
||||
if (minecraft != nullptr)
|
||||
@ -392,6 +397,8 @@ QByteArray FlamePackExportTask::generateIndex()
|
||||
id = "fabric-" + fabric->getVersion();
|
||||
else if (forge != nullptr)
|
||||
id = "forge-" + forge->getVersion();
|
||||
else if (neoforge != nullptr)
|
||||
id = "neoforge-" + neoforge->getVersion();
|
||||
version["modLoaders"] = QJsonArray();
|
||||
if (!id.isEmpty()) {
|
||||
QJsonObject loader;
|
||||
@ -407,7 +414,7 @@ QByteArray FlamePackExportTask::generateIndex()
|
||||
QJsonObject file;
|
||||
file["projectID"] = mod.addonId;
|
||||
file["fileID"] = mod.version;
|
||||
file["required"] = mod.enabled;
|
||||
file["required"] = mod.enabled || !optionalFiles;
|
||||
files << file;
|
||||
}
|
||||
obj["files"] = files;
|
||||
|
@ -30,6 +30,7 @@ class FlamePackExportTask : public Task {
|
||||
FlamePackExportTask(const QString& name,
|
||||
const QString& version,
|
||||
const QString& author,
|
||||
bool optionalFiles,
|
||||
InstancePtr instance,
|
||||
const QString& output,
|
||||
MMCZip::FilterFunction filter);
|
||||
@ -44,6 +45,7 @@ class FlamePackExportTask : public Task {
|
||||
|
||||
// inputs
|
||||
const QString name, version, author;
|
||||
const bool optionalFiles;
|
||||
const InstancePtr instance;
|
||||
MinecraftInstance* mcInstance;
|
||||
const QDir gameRoot;
|
||||
|
@ -72,7 +72,8 @@ Task::Ptr NetworkResourceAPI::getProjectInfo(ProjectInfoArgs&& args, ProjectInfo
|
||||
|
||||
callbacks.on_succeed(doc, args.pack);
|
||||
});
|
||||
|
||||
QObject::connect(job.get(), &NetJob::failed, [callbacks](QString reason) { callbacks.on_fail(reason); });
|
||||
QObject::connect(job.get(), &NetJob::aborted, [callbacks] { callbacks.on_abort(); });
|
||||
return job;
|
||||
}
|
||||
|
||||
@ -131,7 +132,7 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response));
|
||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
|
||||
QJsonParseError parse_error{};
|
||||
|
@ -59,16 +59,20 @@ Modpack parseDirectory(QString path)
|
||||
auto obj = Json::requireObject(target, "target");
|
||||
auto name = Json::requireString(obj, "name", "name");
|
||||
auto version = Json::requireString(obj, "version", "version");
|
||||
if (name == "forge") {
|
||||
modpack.loaderType = ResourceAPI::Forge;
|
||||
if (name == "neoforge") {
|
||||
modpack.loaderType = ModPlatform::NeoForge;
|
||||
modpack.version = version;
|
||||
break;
|
||||
} else if (name == "forge") {
|
||||
modpack.loaderType = ModPlatform::Forge;
|
||||
modpack.version = version;
|
||||
break;
|
||||
} else if (name == "fabric") {
|
||||
modpack.loaderType = ResourceAPI::Fabric;
|
||||
modpack.loaderType = ModPlatform::Fabric;
|
||||
modpack.version = version;
|
||||
break;
|
||||
} else if (name == "quilt") {
|
||||
modpack.loaderType = ResourceAPI::Quilt;
|
||||
modpack.loaderType = ModPlatform::Quilt;
|
||||
modpack.version = version;
|
||||
break;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ struct Modpack {
|
||||
// not needed for instance creation
|
||||
QVariant jvmArgs;
|
||||
|
||||
std::optional<ResourceAPI::ModLoaderType> loaderType;
|
||||
std::optional<ModPlatform::ModLoaderType> loaderType;
|
||||
QString loaderVersion;
|
||||
|
||||
QIcon icon;
|
||||
|
@ -68,21 +68,25 @@ void PackInstallTask::copySettings()
|
||||
auto modloader = m_pack.loaderType;
|
||||
if (modloader.has_value())
|
||||
switch (modloader.value()) {
|
||||
case ResourceAPI::Forge: {
|
||||
case ModPlatform::NeoForge: {
|
||||
components->setComponentVersion("net.neoforged", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ModPlatform::Forge: {
|
||||
components->setComponentVersion("net.minecraftforge", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ResourceAPI::Fabric: {
|
||||
case ModPlatform::Fabric: {
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ResourceAPI::Quilt: {
|
||||
case ModPlatform::Quilt: {
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ResourceAPI::Cauldron:
|
||||
case ModPlatform::Cauldron:
|
||||
break;
|
||||
case ResourceAPI::LiteLoader:
|
||||
case ModPlatform::LiteLoader:
|
||||
break;
|
||||
}
|
||||
components->saveNow();
|
||||
|
@ -41,7 +41,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
|
||||
Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
||||
QString hash_format,
|
||||
std::optional<std::list<Version>> mcVersions,
|
||||
std::optional<ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<QByteArray> response)
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
|
||||
@ -71,7 +71,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
||||
Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
||||
QString hash_format,
|
||||
std::optional<std::list<Version>> mcVersions,
|
||||
std::optional<ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<QByteArray> response)
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
|
||||
|
@ -19,13 +19,13 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
auto latestVersion(QString hash,
|
||||
QString hash_format,
|
||||
std::optional<std::list<Version>> mcVersions,
|
||||
std::optional<ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||
|
||||
auto latestVersions(const QStringList& hashes,
|
||||
QString hash_format,
|
||||
std::optional<std::list<Version>> mcVersions,
|
||||
std::optional<ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||
|
||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||
@ -35,20 +35,19 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
|
||||
inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; };
|
||||
|
||||
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
||||
static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList
|
||||
{
|
||||
QStringList l;
|
||||
for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
|
||||
for (auto loader :
|
||||
{ ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) {
|
||||
if (types & loader) {
|
||||
l << getModLoaderString(loader);
|
||||
}
|
||||
}
|
||||
if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there
|
||||
l << getModLoaderString(Fabric);
|
||||
return l;
|
||||
}
|
||||
|
||||
static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
|
||||
static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString
|
||||
{
|
||||
QStringList l;
|
||||
for (auto loader : getModLoaderStrings(types)) {
|
||||
@ -141,7 +140,10 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
return s.isEmpty() ? QString() : s;
|
||||
}
|
||||
|
||||
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
|
||||
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
|
||||
{
|
||||
return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt | ModPlatform::LiteLoader);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||
{
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourceFolderModel.h"
|
||||
|
||||
static ModrinthAPI api;
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
@ -39,7 +38,7 @@ void ModrinthCheckUpdate::executeTask()
|
||||
QStringList hashes;
|
||||
auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||
|
||||
ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", 10);
|
||||
ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
for (auto* mod : m_mods) {
|
||||
if (!mod->enabled()) {
|
||||
emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!"));
|
||||
@ -111,11 +110,11 @@ void ModrinthCheckUpdate::executeTask()
|
||||
// so we may want to filter it
|
||||
QString loader_filter;
|
||||
if (m_loaders.has_value()) {
|
||||
static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric,
|
||||
ResourceAPI::ModLoaderType::Quilt };
|
||||
static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge,
|
||||
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
|
||||
for (auto flag : flags) {
|
||||
if (m_loaders.value().testFlag(flag)) {
|
||||
loader_filter = api.getModLoaderString(flag);
|
||||
loader_filter = ModPlatform::getModLoaderString(flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -145,9 +144,6 @@ void ModrinthCheckUpdate::executeTask()
|
||||
auto mod = *mod_iter;
|
||||
|
||||
auto key = project_ver.hash;
|
||||
if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) {
|
||||
if (mod->version() == project_ver.version_number)
|
||||
continue;
|
||||
|
||||
// Fake pack with the necessary info to pass to the download task :)
|
||||
auto pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
@ -159,12 +155,16 @@ void ModrinthCheckUpdate::executeTask()
|
||||
pack->authors.append({ author });
|
||||
pack->description = mod->description();
|
||||
pack->provider = ModPlatform::ResourceProvider::MODRINTH;
|
||||
if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) {
|
||||
if (mod->version() == project_ver.version_number)
|
||||
continue;
|
||||
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_mods_folder);
|
||||
|
||||
m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.changelog,
|
||||
ModPlatform::ResourceProvider::MODRINTH, download_task);
|
||||
}
|
||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
failed(e.cause() + " : " + e.what());
|
||||
|
@ -10,7 +10,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
|
||||
public:
|
||||
ModrinthCheckUpdate(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||
{}
|
||||
|
@ -211,6 +211,8 @@ bool ModrinthCreationTask::createInstance()
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
|
||||
if (!m_forge_version.isEmpty())
|
||||
components->setComponentVersion("net.minecraftforge", m_forge_version);
|
||||
if (!m_neoForge_version.isEmpty())
|
||||
components->setComponentVersion("net.neoforged", m_neoForge_version);
|
||||
|
||||
if (m_instIcon != "default") {
|
||||
instance.setIconKey(m_instIcon);
|
||||
@ -398,6 +400,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
||||
m_quilt_version = Json::requireString(*it, "Quilt Loader version");
|
||||
} else if (name == "forge") {
|
||||
m_forge_version = Json::requireString(*it, "Forge version");
|
||||
} else if (name == "neoforge") {
|
||||
m_neoForge_version = Json::requireString(*it, "NeoForge version");
|
||||
} else {
|
||||
throw JSONValidationError("Unknown dependency type: " + name);
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
||||
QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version;
|
||||
QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version;
|
||||
QString m_managed_id, m_managed_version_id, m_managed_name;
|
||||
|
||||
std::vector<Modrinth::File> m_files;
|
||||
|
@ -33,12 +33,14 @@ const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "z
|
||||
ModrinthPackExportTask::ModrinthPackExportTask(const QString& name,
|
||||
const QString& version,
|
||||
const QString& summary,
|
||||
bool optionalFiles,
|
||||
InstancePtr instance,
|
||||
const QString& output,
|
||||
MMCZip::FilterFunction filter)
|
||||
: name(name)
|
||||
, version(version)
|
||||
, summary(summary)
|
||||
, optionalFiles(optionalFiles)
|
||||
, instance(instance)
|
||||
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
||||
, gameRoot(instance->gameRoot())
|
||||
@ -245,6 +247,7 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
||||
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
||||
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
||||
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
||||
const ComponentPtr neoForge = profile->getComponent("net.neoforged");
|
||||
|
||||
// convert all available components to mrpack dependencies
|
||||
QJsonObject dependencies;
|
||||
@ -256,6 +259,8 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
||||
dependencies["fabric-loader"] = fabric->m_version;
|
||||
if (forge != nullptr)
|
||||
dependencies["forge"] = forge->m_version;
|
||||
if (neoForge != nullptr)
|
||||
dependencies["neoforge"] = neoForge->m_version;
|
||||
|
||||
out["dependencies"] = dependencies;
|
||||
}
|
||||
@ -267,6 +272,7 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
||||
QString path = iterator.key();
|
||||
const ResolvedFile& value = iterator.value();
|
||||
|
||||
if (optionalFiles) {
|
||||
// detect disabled mod
|
||||
const QFileInfo pathInfo(path);
|
||||
if (pathInfo.suffix() == "disabled") {
|
||||
@ -278,6 +284,7 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
||||
env["server"] = "optional";
|
||||
fileOut["env"] = env;
|
||||
}
|
||||
}
|
||||
|
||||
fileOut["path"] = path;
|
||||
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
||||
|
@ -31,6 +31,7 @@ class ModrinthPackExportTask : public Task {
|
||||
ModrinthPackExportTask(const QString& name,
|
||||
const QString& version,
|
||||
const QString& summary,
|
||||
bool optionalFiles,
|
||||
InstancePtr instance,
|
||||
const QString& output,
|
||||
MMCZip::FilterFunction filter);
|
||||
@ -50,6 +51,7 @@ class ModrinthPackExportTask : public Task {
|
||||
|
||||
// inputs
|
||||
const QString name, version, summary;
|
||||
const bool optionalFiles;
|
||||
const InstancePtr instance;
|
||||
MinecraftInstance* mcInstance;
|
||||
const QDir gameRoot;
|
||||
|
@ -93,19 +93,19 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
||||
pack.extraDataLoaded = true;
|
||||
}
|
||||
|
||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QJsonArray& arr,
|
||||
[[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
const BaseInstance* inst)
|
||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst)
|
||||
{
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
QString mcVersion = (static_cast<const MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft");
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
auto loaders = profile->getSupportedModLoaders();
|
||||
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
@ -134,7 +134,18 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
||||
}
|
||||
auto loaders = Json::requireArray(obj, "loaders");
|
||||
for (auto loader : loaders) {
|
||||
file.loaders.append(loader.toString());
|
||||
if (loader == "neoforge")
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
if (loader == "forge")
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
if (loader == "cauldron")
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
if (loader == "liteloader")
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
if (loader == "fabric")
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
if (loader == "quilt")
|
||||
file.loaders |= ModPlatform::Quilt;
|
||||
}
|
||||
file.version = Json::requireString(obj, "name");
|
||||
file.version_number = Json::requireString(obj, "version_number");
|
||||
@ -218,15 +229,20 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst)
|
||||
-> ModPlatform::IndexedVersion
|
||||
{
|
||||
QVector<ModPlatform::IndexedVersion> versions;
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
auto loaders = profile->getSupportedModLoaders();
|
||||
|
||||
QVector<ModPlatform::IndexedVersion> versions;
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
versions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
|
@ -26,11 +26,8 @@ namespace Modrinth {
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
QJsonArray& arr,
|
||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||
const BaseInstance* inst);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst);
|
||||
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion;
|
||||
|
||||
} // namespace Modrinth
|
||||
|
@ -218,9 +218,24 @@ void HttpMetaCache::Load()
|
||||
if (!index.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
|
||||
QJsonDocument json = QJsonDocument::fromJson(index.readAll());
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument json = QJsonDocument::fromJson(index.readAll(), &parseError);
|
||||
|
||||
auto root = Json::requireObject(json, "HttpMetaCache root");
|
||||
// Fail if the JSON is invalid.
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qCritical() << QString("Failed to parse HttpMetaCache file: %1 at offset %2")
|
||||
.arg(parseError.errorString(), QString::number(parseError.offset))
|
||||
.toUtf8();
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the root is an object.
|
||||
if (!json.isObject()) {
|
||||
qCritical() << "HttpMetaCache root should be an object.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto root = json.object();
|
||||
|
||||
// check file version first
|
||||
auto version_val = Json::ensureString(root, "version");
|
||||
|
@ -36,9 +36,14 @@
|
||||
*/
|
||||
|
||||
#include "NetJob.h"
|
||||
#include "Application.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
NetJob::NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network)
|
||||
: ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network)
|
||||
{}
|
||||
|
||||
auto NetJob::addNetAction(NetAction::Ptr action) -> bool
|
||||
{
|
||||
action->setNetwork(m_network);
|
||||
|
@ -52,9 +52,7 @@ class NetJob : public ConcurrentTask {
|
||||
public:
|
||||
using Ptr = shared_qobject_ptr<NetJob>;
|
||||
|
||||
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network)
|
||||
: ConcurrentTask(nullptr, job_name), m_network(network)
|
||||
{}
|
||||
explicit NetJob(QString job_name, shared_qobject_ptr<QNetworkAccessManager> network);
|
||||
~NetJob() override = default;
|
||||
|
||||
void startNext() override;
|
||||
|
@ -350,6 +350,7 @@
|
||||
|
||||
<file>scalable/instances/quiltmc.svg</file> <!-- CC0 QuiltMC -->
|
||||
<file>scalable/instances/fabricmc.svg</file> <!-- CC0 unascribed, https://github.com/FabricMC/community/blob/main/media/unascribed/README.md -->
|
||||
<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 -->
|
||||
</qresource>
|
||||
|
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="272" height="272" version="1.1" viewBox="0 0 272 272" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><g transform="matrix(1.25,0,0,1.25,-544,-34)" stroke-width="0"><path d="m512 224v16h64v-16h32v-16h16v-32h16v-16h-16l-8-8v-32l8-8v-32l-8-8-16 16h-16l-24-24h-32l-24 24h-16l-16-16-8 8v32l8 8v32l-8 8h-16v16h16v32h16v16z" fill="#a44e37"/><path d="m480 176v16l8 8h112l8-8v-16h16v-16l-8-8 16-16-32-32h-16l-8-8h-16l-8 8v16l-16 16v-32l-8-8h-16l-8 8h-16l-32 32 16 16-8 8v16z" fill="#d7742f"/><path d="m528 144h16v-16h16v-32l-8-8h-16l-8 8z" fill="#e68c37"/><path d="m576 96h32v16h-32z" fill="#bf6134"/><path d="m528 80h32v16h-32z" fill="#bf6134"/><path d="m480 96h32v16h-32z" fill="#bb5f33"/><g fill="#bf6134"><path d="m528 160h32v16h-32z"/><path d="m480 192h32v16h-32z"/><path d="m576 192h32v16h-32z"/></g><path d="m512 192v32h16l8-8h16l8 8h16v-32h-16l-8 8h-16l-8-8z" fill="#f9f4f4"/><path d="m528 208v16h32v-16z" fill="#e7d9d3"/><path d="m528 208v-16h32v16z" fill="#13151a"/><path d="m480 128h16l8 8v24l-8 8h-8l-8-8z" fill="#f9f4f4"/><path d="m480 160h16l8 8-8 8h-16z" fill="#e7d9d3"/><path d="m512 128v16l-8 8-8-8v-16z" fill="#e7d9d3"/><path d="m512 144v16l-8 8-8-8v-16z" fill="#262a33"/><path d="m512 160v16h-16v-16z" fill="#13151a"/><g transform="matrix(-1,0,0,1,1088,0)"><path d="m480 128h16l8 8v24l-8 8h-8l-8-8z" fill="#f9f4f4"/><path d="m480 160h16l8 8-8 8h-16z" fill="#e7d9d3"/><path d="m512 128v16l-8 8-8-8v-16z" fill="#e7d9d3"/><path d="m512 144v16l-8 8-8-8v-16z" fill="#262a33"/><path d="m512 160v16h-16v-16z" fill="#13151a"/></g><path d="m608 96v-16h16v-16h-16v-16h-16v-16h-32v48h16l16 16z" fill="#66534d"/><path d="m608 64v16h-16l-16-16v-16h16v16z" fill="#8d7168"/><path d="m584 88-8-8v-16h16v16z" fill="#e7d9d3"/><path d="m576 80v16h16v-16z" fill="#c7a3b9"/><g transform="matrix(-1,0,0,1,1088,0)"><path d="m608 96v-16h16v-16h-16v-16h-16v-16h-32v48h16l16 16z" fill="#66534d"/><path d="m608 64v16h-16l-16-16v-16h16v16z" fill="#8d7168"/><path d="m584 88-8-8v-16h16v16z" fill="#e7d9d3"/><path d="m576 80v16h16v-16z" fill="#c7a3b9"/></g><g fill="#bb5f33"><path d="m480 112h-16v16h16z"/><path d="m464 128h-16v16h16z"/><path d="m480 144h-16v16h16z"/><path d="m624 144h-16v16h16z"/><path d="m640 128h-16v16h16z"/><path d="m624 112h-16v16h16z"/></g></g><metadata><rdf:RDF><cc:Work rdf:about=""><dc:creator><cc:Agent><dc:title>Sefa Eyeoglu <contact@scrumplex.net></dc:title></cc:Agent></dc:creator></cc:Work></rdf:RDF></metadata></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@ -51,6 +51,9 @@ class ConcurrentTask : public Task {
|
||||
explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6);
|
||||
~ConcurrentTask() override;
|
||||
|
||||
// safe to call before starting the task
|
||||
void setMaxConcurrent(int max_concurrent) { m_total_max_size = max_concurrent; }
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
|
||||
inline auto isMultiStep() const -> bool override { return totalSize() > 1; }
|
||||
|
@ -44,8 +44,6 @@
|
||||
#include <QPushButton>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/widgets/PageContainer.h"
|
||||
|
||||
#include "InstancePageProvider.h"
|
||||
@ -76,40 +74,44 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget* parent) : QMainWin
|
||||
|
||||
// Add custom buttons to the page container layout.
|
||||
{
|
||||
auto horizontalLayout = new QHBoxLayout();
|
||||
auto horizontalLayout = new QHBoxLayout(this);
|
||||
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
||||
horizontalLayout->setContentsMargins(6, -1, 6, -1);
|
||||
|
||||
auto btnHelp = new QPushButton();
|
||||
auto btnHelp = new QPushButton(this);
|
||||
btnHelp->setText(tr("Help"));
|
||||
horizontalLayout->addWidget(btnHelp);
|
||||
connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help()));
|
||||
connect(btnHelp, &QPushButton::clicked, m_container, &PageContainer::help);
|
||||
|
||||
auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
horizontalLayout->addSpacerItem(spacer);
|
||||
|
||||
m_killButton = new QPushButton();
|
||||
m_launchButton = new QToolButton(this);
|
||||
m_launchButton->setText(tr("&Launch"));
|
||||
m_launchButton->setToolTip(tr("Launch the instance"));
|
||||
m_launchButton->setPopupMode(QToolButton::MenuButtonPopup);
|
||||
m_launchButton->setMinimumWidth(80); // HACK!!
|
||||
horizontalLayout->addWidget(m_launchButton);
|
||||
connect(m_launchButton, &QPushButton::clicked, this, [this] { APPLICATION->launch(m_instance); });
|
||||
|
||||
m_killButton = new QPushButton(this);
|
||||
m_killButton->setText(tr("&Kill"));
|
||||
m_killButton->setToolTip(tr("Kill the running instance"));
|
||||
m_killButton->setShortcut(QKeySequence(tr("Ctrl+K")));
|
||||
horizontalLayout->addWidget(m_killButton);
|
||||
connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked()));
|
||||
connect(m_killButton, &QPushButton::clicked, this, [this] { APPLICATION->kill(m_instance); });
|
||||
|
||||
m_launchOfflineButton = new QPushButton();
|
||||
horizontalLayout->addWidget(m_launchOfflineButton);
|
||||
m_launchOfflineButton->setText(tr("Launch Offline"));
|
||||
updateButtons();
|
||||
|
||||
m_launchDemoButton = new QPushButton();
|
||||
horizontalLayout->addWidget(m_launchDemoButton);
|
||||
m_launchDemoButton->setText(tr("Launch Demo"));
|
||||
|
||||
updateLaunchButtons();
|
||||
connect(m_launchOfflineButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftOffline_clicked()));
|
||||
connect(m_launchDemoButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftDemo_clicked()));
|
||||
|
||||
m_closeButton = new QPushButton();
|
||||
m_closeButton = new QPushButton(this);
|
||||
m_closeButton->setText(tr("Close"));
|
||||
horizontalLayout->addWidget(m_closeButton);
|
||||
connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked()));
|
||||
connect(m_closeButton, &QPushButton::clicked, this, &QMainWindow::close);
|
||||
|
||||
m_container->addButtons(horizontalLayout);
|
||||
|
||||
connect(m_instance.get(), &BaseInstance::profilerChanged, this, &InstanceWindow::updateButtons);
|
||||
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceWindow::updateButtons);
|
||||
}
|
||||
|
||||
// restore window state
|
||||
@ -149,47 +151,18 @@ void InstanceWindow::on_instanceStatusChanged(BaseInstance::Status, BaseInstance
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceWindow::updateLaunchButtons()
|
||||
void InstanceWindow::updateButtons()
|
||||
{
|
||||
if (m_instance->isRunning()) {
|
||||
m_launchOfflineButton->setEnabled(false);
|
||||
m_launchDemoButton->setEnabled(false);
|
||||
m_killButton->setText(tr("Kill"));
|
||||
m_killButton->setObjectName("killButton");
|
||||
m_killButton->setToolTip(tr("Kill the running instance"));
|
||||
} else if (!m_instance->canLaunch()) {
|
||||
m_launchOfflineButton->setEnabled(false);
|
||||
m_launchDemoButton->setEnabled(false);
|
||||
m_killButton->setText(tr("Launch"));
|
||||
m_killButton->setObjectName("launchButton");
|
||||
m_killButton->setToolTip(tr("Launch the instance"));
|
||||
m_killButton->setEnabled(false);
|
||||
} else {
|
||||
m_launchOfflineButton->setEnabled(true);
|
||||
m_launchButton->setEnabled(m_instance->canLaunch());
|
||||
m_killButton->setEnabled(m_instance->isRunning());
|
||||
|
||||
// Disable demo-mode if not available.
|
||||
auto instance = dynamic_cast<MinecraftInstance*>(m_instance.get());
|
||||
if (instance) {
|
||||
m_launchDemoButton->setEnabled(instance->supportsDemo());
|
||||
}
|
||||
|
||||
m_killButton->setText(tr("Launch"));
|
||||
m_killButton->setObjectName("launchButton");
|
||||
m_killButton->setToolTip(tr("Launch the instance"));
|
||||
}
|
||||
// NOTE: this is a hack to force the button to recalculate its style
|
||||
m_killButton->setStyleSheet("/* */");
|
||||
m_killButton->setStyleSheet(QString());
|
||||
}
|
||||
|
||||
void InstanceWindow::on_btnLaunchMinecraftOffline_clicked()
|
||||
{
|
||||
APPLICATION->launch(m_instance, false, false, nullptr);
|
||||
}
|
||||
|
||||
void InstanceWindow::on_btnLaunchMinecraftDemo_clicked()
|
||||
{
|
||||
APPLICATION->launch(m_instance, false, true, nullptr);
|
||||
QMenu* launchMenu = m_launchButton->menu();
|
||||
if (launchMenu)
|
||||
launchMenu->clear();
|
||||
else
|
||||
launchMenu = new QMenu(this);
|
||||
m_instance->populateLaunchMenu(launchMenu);
|
||||
m_launchButton->setMenu(launchMenu);
|
||||
}
|
||||
|
||||
void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc)
|
||||
@ -199,18 +172,13 @@ void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> pr
|
||||
|
||||
void InstanceWindow::runningStateChanged(bool running)
|
||||
{
|
||||
updateLaunchButtons();
|
||||
updateButtons();
|
||||
m_container->refreshContainer();
|
||||
if (running) {
|
||||
selectPage("log");
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceWindow::on_closeButton_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void InstanceWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
bool proceed = true;
|
||||
@ -233,15 +201,6 @@ bool InstanceWindow::saveAll()
|
||||
return m_container->saveAll();
|
||||
}
|
||||
|
||||
void InstanceWindow::on_btnKillMinecraft_clicked()
|
||||
{
|
||||
if (m_instance->isRunning()) {
|
||||
APPLICATION->kill(m_instance);
|
||||
} else {
|
||||
APPLICATION->launch(m_instance, true, false, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
QString InstanceWindow::instanceId()
|
||||
{
|
||||
return m_instance->id();
|
||||
@ -252,17 +211,15 @@ bool InstanceWindow::selectPage(QString pageId)
|
||||
return m_container->selectPage(pageId);
|
||||
}
|
||||
|
||||
BasePage* InstanceWindow::selectedPage() const
|
||||
{
|
||||
return m_container->selectedPage();
|
||||
}
|
||||
|
||||
void InstanceWindow::refreshContainer()
|
||||
{
|
||||
m_container->refreshContainer();
|
||||
}
|
||||
|
||||
InstanceWindow::~InstanceWindow() {}
|
||||
BasePage* InstanceWindow::selectedPage() const
|
||||
{
|
||||
return m_container->selectedPage();
|
||||
}
|
||||
|
||||
bool InstanceWindow::requestClose()
|
||||
{
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "LaunchController.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
@ -53,7 +54,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
||||
|
||||
public:
|
||||
explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0);
|
||||
virtual ~InstanceWindow();
|
||||
virtual ~InstanceWindow() = default;
|
||||
|
||||
bool selectPage(QString pageId) override;
|
||||
BasePage* selectedPage() const override;
|
||||
@ -71,11 +72,6 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
||||
void isClosing();
|
||||
|
||||
private slots:
|
||||
void on_closeButton_clicked();
|
||||
void on_btnKillMinecraft_clicked();
|
||||
void on_btnLaunchMinecraftOffline_clicked();
|
||||
void on_btnLaunchMinecraftDemo_clicked();
|
||||
|
||||
void instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc);
|
||||
void runningStateChanged(bool running);
|
||||
void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus);
|
||||
@ -84,7 +80,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
||||
void closeEvent(QCloseEvent*) override;
|
||||
|
||||
private:
|
||||
void updateLaunchButtons();
|
||||
void updateButtons();
|
||||
|
||||
private:
|
||||
shared_qobject_ptr<LaunchTask> m_proc;
|
||||
@ -92,7 +88,6 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
||||
bool m_doNotSave = false;
|
||||
PageContainer* m_container = nullptr;
|
||||
QPushButton* m_closeButton = nullptr;
|
||||
QToolButton* m_launchButton = nullptr;
|
||||
QPushButton* m_killButton = nullptr;
|
||||
QPushButton* m_launchOfflineButton = nullptr;
|
||||
QPushButton* m_launchDemoButton = nullptr;
|
||||
};
|
||||
|
@ -43,7 +43,6 @@
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "ui/dialogs/ExportToModListDialog.h"
|
||||
#include "ui_MainWindow.h"
|
||||
|
||||
#include <QDir>
|
||||
@ -90,17 +89,14 @@
|
||||
#include <news/NewsChecker.h>
|
||||
#include <tools/BaseProfiler.h>
|
||||
#include <updater/ExternalUpdater.h>
|
||||
#include "InstancePageProvider.h"
|
||||
#include "InstanceWindow.h"
|
||||
#include "JavaCommon.h"
|
||||
#include "LaunchController.h"
|
||||
|
||||
#include "ui/dialogs/AboutDialog.h"
|
||||
#include "ui/dialogs/CopyInstanceDialog.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/EditAccountDialog.h"
|
||||
#include "ui/dialogs/ExportInstanceDialog.h"
|
||||
#include "ui/dialogs/ExportPackDialog.h"
|
||||
#include "ui/dialogs/ExportToModListDialog.h"
|
||||
#include "ui/dialogs/IconPickerDialog.h"
|
||||
#include "ui/dialogs/ImportResourceDialog.h"
|
||||
#include "ui/dialogs/NewInstanceDialog.h"
|
||||
@ -113,17 +109,22 @@
|
||||
#include "ui/themes/ThemeManager.h"
|
||||
#include "ui/widgets/LabeledToolButton.h"
|
||||
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/VersionFile.h"
|
||||
#include "minecraft/WorldList.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourcePackFolderModel.h"
|
||||
#include "minecraft/mod/ShaderPackFolderModel.h"
|
||||
#include "minecraft/mod/TexturePackFolderModel.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
|
||||
#include "KonamiCode.h"
|
||||
|
||||
#include "InstanceCopyTask.h"
|
||||
#include "InstanceImportTask.h"
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
@ -553,71 +554,15 @@ void MainWindow::updateMainToolBar()
|
||||
ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
||||
}
|
||||
|
||||
void MainWindow::updateToolsMenu()
|
||||
void MainWindow::updateLaunchButton()
|
||||
{
|
||||
bool currentInstanceRunning = m_selectedInstance && m_selectedInstance->isRunning();
|
||||
|
||||
ui->actionLaunchInstance->setDisabled(!m_selectedInstance || currentInstanceRunning);
|
||||
ui->actionLaunchInstanceOffline->setDisabled(!m_selectedInstance || currentInstanceRunning);
|
||||
ui->actionLaunchInstanceDemo->setDisabled(!m_selectedInstance || currentInstanceRunning);
|
||||
|
||||
QMenu* launchMenu = ui->actionLaunchInstance->menu();
|
||||
if (launchMenu) {
|
||||
if (launchMenu)
|
||||
launchMenu->clear();
|
||||
} else {
|
||||
else
|
||||
launchMenu = new QMenu(this);
|
||||
}
|
||||
QAction* normalLaunch = launchMenu->addAction(tr("Launch"));
|
||||
normalLaunch->setShortcut(QKeySequence::Open);
|
||||
QAction* normalLaunchOffline = launchMenu->addAction(tr("Launch Offline"));
|
||||
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
|
||||
QAction* normalLaunchDemo = launchMenu->addAction(tr("Launch Demo"));
|
||||
normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
|
||||
if (m_selectedInstance) {
|
||||
normalLaunch->setEnabled(m_selectedInstance->canLaunch());
|
||||
normalLaunchOffline->setEnabled(m_selectedInstance->canLaunch());
|
||||
normalLaunchDemo->setEnabled(m_selectedInstance->canLaunch());
|
||||
|
||||
connect(normalLaunch, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, true, false); });
|
||||
connect(normalLaunchOffline, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, false, false); });
|
||||
connect(normalLaunchDemo, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, false, true); });
|
||||
} else {
|
||||
normalLaunch->setDisabled(true);
|
||||
normalLaunchOffline->setDisabled(true);
|
||||
normalLaunchDemo->setDisabled(true);
|
||||
}
|
||||
|
||||
// Disable demo-mode if not available.
|
||||
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
|
||||
if (instance) {
|
||||
normalLaunchDemo->setEnabled(instance->supportsDemo());
|
||||
}
|
||||
|
||||
QString profilersTitle = tr("Profilers");
|
||||
launchMenu->addSeparator()->setText(profilersTitle);
|
||||
for (auto profiler : APPLICATION->profilers().values()) {
|
||||
QAction* profilerAction = launchMenu->addAction(profiler->name());
|
||||
QAction* profilerOfflineAction = launchMenu->addAction(tr("%1 Offline").arg(profiler->name()));
|
||||
QString error;
|
||||
if (!profiler->check(&error)) {
|
||||
profilerAction->setDisabled(true);
|
||||
profilerOfflineAction->setDisabled(true);
|
||||
QString profilerToolTip = tr("Profiler not setup correctly. Go into settings, \"External Tools\".");
|
||||
profilerAction->setToolTip(profilerToolTip);
|
||||
profilerOfflineAction->setToolTip(profilerToolTip);
|
||||
} else if (m_selectedInstance) {
|
||||
profilerAction->setEnabled(m_selectedInstance->canLaunch());
|
||||
profilerOfflineAction->setEnabled(m_selectedInstance->canLaunch());
|
||||
|
||||
connect(profilerAction, &QAction::triggered,
|
||||
[this, profiler]() { APPLICATION->launch(m_selectedInstance, true, false, profiler.get()); });
|
||||
connect(profilerOfflineAction, &QAction::triggered,
|
||||
[this, profiler]() { APPLICATION->launch(m_selectedInstance, false, false, profiler.get()); });
|
||||
} else {
|
||||
profilerAction->setDisabled(true);
|
||||
profilerOfflineAction->setDisabled(true);
|
||||
}
|
||||
}
|
||||
if (m_selectedInstance)
|
||||
m_selectedInstance->populateLaunchMenu(launchMenu);
|
||||
ui->actionLaunchInstance->setMenu(launchMenu);
|
||||
}
|
||||
|
||||
@ -927,7 +872,7 @@ void MainWindow::finalizeInstance(InstancePtr inst)
|
||||
} else {
|
||||
CustomMessageBox::selectable(this, tr("Error"),
|
||||
tr("The launcher cannot download Minecraft or update instances unless you have at least "
|
||||
"one account added.\nPlease add your Mojang or Minecraft account."),
|
||||
"one account added.\nPlease add your Microsoft or Mojang account."),
|
||||
QMessageBox::Warning)
|
||||
->show();
|
||||
}
|
||||
@ -980,6 +925,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
||||
if (url.scheme().isEmpty())
|
||||
url.setScheme("file");
|
||||
|
||||
ModPlatform::IndexedVersion version;
|
||||
QMap<QString, QString> extra_info;
|
||||
QUrl local_url;
|
||||
if (!url.isLocalFile()) { // download the remote resource and identify
|
||||
@ -989,6 +935,11 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
||||
// format of url curseforge://install?addonId=IDHERE&fileId=IDHERE
|
||||
QUrlQuery query(url);
|
||||
|
||||
if (query.allQueryItemValues("addonId").isEmpty() || query.allQueryItemValues("fileId").isEmpty()) {
|
||||
qDebug() << "Invalid curseforge link:" << url;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto addonId = query.allQueryItemValues("addonId")[0];
|
||||
auto fileId = query.allQueryItemValues("fileId")[0];
|
||||
|
||||
@ -1000,20 +951,19 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
||||
auto api = FlameAPI();
|
||||
auto job = api.getFile(addonId, fileId, array);
|
||||
|
||||
QString resource_name;
|
||||
|
||||
connect(job.get(), &Task::failed, this,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||
connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &resource_name] {
|
||||
connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &version] {
|
||||
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
|
||||
auto doc = Json::requireDocument(*array);
|
||||
auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
|
||||
// No way to find out if it's a mod or a modpack before here
|
||||
// And also we need to check if it ends with .zip, instead of any better way
|
||||
auto fileName = Json::ensureString(data, "fileName");
|
||||
version = FlameMod::loadIndexedPackVersion(data);
|
||||
auto fileName = version.fileName;
|
||||
|
||||
// Have to use ensureString then use QUrl to get proper url encoding
|
||||
dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl"));
|
||||
dl_url = QUrl(version.downloadUrl);
|
||||
if (!dl_url.isValid()) {
|
||||
CustomMessageBox::selectable(
|
||||
this, tr("Error"),
|
||||
@ -1024,7 +974,6 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
||||
}
|
||||
|
||||
QFileInfo dl_file(dl_url.fileName());
|
||||
resource_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
|
||||
});
|
||||
|
||||
{ // drop stack
|
||||
@ -1099,7 +1048,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
||||
qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName;
|
||||
break;
|
||||
case PackedResourceType::Mod:
|
||||
minecraftInst->loaderModList()->installMod(localFileName);
|
||||
minecraftInst->loaderModList()->installMod(localFileName, version);
|
||||
break;
|
||||
case PackedResourceType::ShaderPack:
|
||||
minecraftInst->shaderPackList()->installResource(localFileName);
|
||||
@ -1278,7 +1227,7 @@ void MainWindow::globalSettingsClosed()
|
||||
proxymodel->invalidate();
|
||||
proxymodel->sort(0);
|
||||
updateMainToolBar();
|
||||
updateToolsMenu();
|
||||
updateLaunchButton();
|
||||
updateThemeMenu();
|
||||
updateStatusCenter();
|
||||
// This needs to be done to prevent UI elements disappearing in the event the config is changed
|
||||
@ -1408,10 +1357,11 @@ void MainWindow::on_actionDeleteInstance_triggered()
|
||||
|
||||
if (APPLICATION->instances()->trashInstance(id)) {
|
||||
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
APPLICATION->instances()->deleteInstance(id);
|
||||
}
|
||||
APPLICATION->settings()->set("SelectedInstance", QString());
|
||||
selectionBad();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionExportInstanceZip_triggered()
|
||||
@ -1513,20 +1463,6 @@ void MainWindow::activateInstance(InstancePtr instance)
|
||||
APPLICATION->launch(instance);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionLaunchInstanceOffline_triggered()
|
||||
{
|
||||
if (m_selectedInstance) {
|
||||
APPLICATION->launch(m_selectedInstance, false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionLaunchInstanceDemo_triggered()
|
||||
{
|
||||
if (m_selectedInstance) {
|
||||
APPLICATION->launch(m_selectedInstance, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionKillInstance_triggered()
|
||||
{
|
||||
if (m_selectedInstance && m_selectedInstance->isRunning()) {
|
||||
@ -1700,6 +1636,7 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
|
||||
}
|
||||
if (m_selectedInstance) {
|
||||
disconnect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
|
||||
disconnect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance);
|
||||
}
|
||||
QString id = current.data(InstanceList::InstanceIDRole).toString();
|
||||
m_selectedInstance = APPLICATION->instances()->getInstanceById(id);
|
||||
@ -1707,14 +1644,6 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
|
||||
ui->instanceToolBar->setEnabled(true);
|
||||
setInstanceActionsEnabled(true);
|
||||
ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch());
|
||||
ui->actionLaunchInstanceOffline->setEnabled(m_selectedInstance->canLaunch());
|
||||
ui->actionLaunchInstanceDemo->setEnabled(m_selectedInstance->canLaunch());
|
||||
|
||||
// Disable demo-mode if not available.
|
||||
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
|
||||
if (instance) {
|
||||
ui->actionLaunchInstanceDemo->setEnabled(instance->supportsDemo());
|
||||
}
|
||||
|
||||
ui->actionKillInstance->setEnabled(m_selectedInstance->isRunning());
|
||||
ui->actionExportInstance->setEnabled(m_selectedInstance->canExport());
|
||||
@ -1723,18 +1652,13 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
|
||||
updateStatusCenter();
|
||||
updateInstanceToolIcon(m_selectedInstance->iconKey());
|
||||
|
||||
updateToolsMenu();
|
||||
updateLaunchButton();
|
||||
|
||||
APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id());
|
||||
|
||||
connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
|
||||
connect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance);
|
||||
} else {
|
||||
ui->instanceToolBar->setEnabled(false);
|
||||
setInstanceActionsEnabled(false);
|
||||
ui->actionLaunchInstance->setEnabled(false);
|
||||
ui->actionLaunchInstanceOffline->setEnabled(false);
|
||||
ui->actionLaunchInstanceDemo->setEnabled(false);
|
||||
ui->actionKillInstance->setEnabled(false);
|
||||
APPLICATION->settings()->set("SelectedInstance", QString());
|
||||
selectionBad();
|
||||
return;
|
||||
@ -1759,11 +1683,12 @@ void MainWindow::selectionBad()
|
||||
{
|
||||
// start by reseting everything...
|
||||
m_selectedInstance = nullptr;
|
||||
m_statusLeft->setText(tr("No instance selected"));
|
||||
|
||||
statusBar()->clearMessage();
|
||||
ui->instanceToolBar->setEnabled(false);
|
||||
setInstanceActionsEnabled(false);
|
||||
updateToolsMenu();
|
||||
updateLaunchButton();
|
||||
renameButton->setText(tr("Rename Instance"));
|
||||
updateInstanceToolIcon("grass");
|
||||
|
||||
@ -1810,7 +1735,9 @@ void MainWindow::updateStatusCenter()
|
||||
|
||||
int timePlayed = APPLICATION->instances()->getTotalPlayTime();
|
||||
if (timePlayed > 0) {
|
||||
m_statusCenter->setText(tr("Total playtime: %1").arg(Time::prettifyDuration(timePlayed)));
|
||||
m_statusCenter->setText(
|
||||
tr("Total playtime: %1")
|
||||
.arg(Time::prettifyDuration(timePlayed, APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||
}
|
||||
}
|
||||
// "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here)
|
||||
@ -1826,7 +1753,7 @@ void MainWindow::setInstanceActionsEnabled(bool enabled)
|
||||
ui->actionCreateInstanceShortcut->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void MainWindow::refreshCurrentInstance([[maybe_unused]] bool running)
|
||||
void MainWindow::refreshCurrentInstance()
|
||||
{
|
||||
auto current = view->selectionModel()->currentIndex();
|
||||
instanceChanged(current, current);
|
||||
|
@ -144,10 +144,6 @@ class MainWindow : public QMainWindow {
|
||||
|
||||
void on_actionLaunchInstance_triggered();
|
||||
|
||||
void on_actionLaunchInstanceOffline_triggered();
|
||||
|
||||
void on_actionLaunchInstanceDemo_triggered();
|
||||
|
||||
void on_actionKillInstance_triggered();
|
||||
|
||||
void on_actionDeleteInstance_triggered();
|
||||
@ -155,10 +151,7 @@ class MainWindow : public QMainWindow {
|
||||
void deleteGroup();
|
||||
void undoTrashInstance();
|
||||
|
||||
inline void on_actionExportInstance_triggered()
|
||||
{
|
||||
on_actionExportInstanceZip_triggered();
|
||||
}
|
||||
inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); }
|
||||
void on_actionExportInstanceZip_triggered();
|
||||
void on_actionExportInstanceMrPack_triggered();
|
||||
void on_actionExportInstanceFlamePack_triggered();
|
||||
@ -181,7 +174,7 @@ class MainWindow : public QMainWindow {
|
||||
|
||||
void updateMainToolBar();
|
||||
|
||||
void updateToolsMenu();
|
||||
void updateLaunchButton();
|
||||
|
||||
void updateThemeMenu();
|
||||
|
||||
@ -215,7 +208,7 @@ class MainWindow : public QMainWindow {
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
#endif
|
||||
|
||||
void refreshCurrentInstance(bool running);
|
||||
void refreshCurrentInstance();
|
||||
|
||||
private:
|
||||
void retranslateUi();
|
||||
|
@ -440,22 +440,6 @@
|
||||
<string>Ctrl+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLaunchInstanceOffline">
|
||||
<property name="text">
|
||||
<string>Launch &Offline</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Launch the selected instance in offline mode.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLaunchInstanceDemo">
|
||||
<property name="text">
|
||||
<string>Launch &Demo</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Launch the selected instance in demo mode.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportInstance">
|
||||
<property name="icon">
|
||||
<iconset theme="export">
|
||||
|
@ -44,7 +44,8 @@
|
||||
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods)
|
||||
: QDialog(parent), ui(new Ui::BlockedModsDialog), m_mods(mods)
|
||||
{
|
||||
m_hashing_task = shared_qobject_ptr<ConcurrentTask>(new ConcurrentTask(this, "MakeHashesTask", 10));
|
||||
m_hashing_task = shared_qobject_ptr<ConcurrentTask>(
|
||||
new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()));
|
||||
connect(m_hashing_task.get(), &Task::finished, this, &BlockedModsDialog::hashTaskFinished);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
@ -37,15 +37,21 @@
|
||||
ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider)
|
||||
: QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider)
|
||||
{
|
||||
Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME);
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->name->setText(instance->name());
|
||||
ui->name->setPlaceholderText(instance->name());
|
||||
ui->name->setText(instance->settings()->get("ExportName").toString());
|
||||
ui->version->setText(instance->settings()->get("ExportVersion").toString());
|
||||
ui->optionalFiles->setChecked(instance->settings()->get("ExportOptionalFiles").toBool());
|
||||
|
||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||
ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]);
|
||||
setWindowTitle("Export Modrinth Pack");
|
||||
setWindowTitle(tr("Export Modrinth Pack"));
|
||||
ui->summary->setText(instance->settings()->get("ExportSummary").toString());
|
||||
} else {
|
||||
setWindowTitle("Export CurseForge Pack");
|
||||
ui->version->setText("");
|
||||
ui->summaryLabel->setText("Author");
|
||||
setWindowTitle(tr("Export CurseForge Pack"));
|
||||
ui->summaryLabel->setText(tr("&Author"));
|
||||
ui->summary->setText(instance->settings()->get("ExportAuthor").toString());
|
||||
}
|
||||
|
||||
// ensure a valid pack is generated
|
||||
@ -75,20 +81,19 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
|
||||
|
||||
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
||||
if (mcInstance) {
|
||||
mcInstance->loaderModList()->update();
|
||||
const QDir index = mcInstance->loaderModList()->indexDir();
|
||||
if (index.exists())
|
||||
proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath()));
|
||||
proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath()));
|
||||
}
|
||||
|
||||
ui->treeView->setModel(proxy);
|
||||
ui->treeView->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot())));
|
||||
ui->treeView->sortByColumn(0, Qt::AscendingOrder);
|
||||
ui->files->setModel(proxy);
|
||||
ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot())));
|
||||
ui->files->sortByColumn(0, Qt::AscendingOrder);
|
||||
|
||||
model->setFilter(filter);
|
||||
model->setRootPath(instance->gameRoot());
|
||||
|
||||
QHeaderView* headerView = ui->treeView->header();
|
||||
QHeaderView* headerView = ui->files->header();
|
||||
headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
}
|
||||
@ -100,26 +105,41 @@ ExportPackDialog::~ExportPackDialog()
|
||||
|
||||
void ExportPackDialog::done(int result)
|
||||
{
|
||||
if (result == Accepted) {
|
||||
const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text());
|
||||
QString output;
|
||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
|
||||
FS::PathCombine(QDir::homePath(), filename + ".mrpack"), "Modrinth pack (*.mrpack *.zip)",
|
||||
nullptr);
|
||||
else
|
||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
|
||||
FS::PathCombine(QDir::homePath(), filename + ".zip"), "CurseForge pack (*.zip)", nullptr);
|
||||
auto settings = instance->settings();
|
||||
settings->set("ExportName", ui->name->text());
|
||||
settings->set("ExportVersion", ui->version->text());
|
||||
settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text());
|
||||
settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked());
|
||||
|
||||
if (result == Accepted) {
|
||||
const QString name = ui->name->text().isEmpty() ? instance->name() : ui->name->text();
|
||||
const QString filename = FS::RemoveInvalidFilenameChars(name);
|
||||
|
||||
QString output;
|
||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".mrpack"),
|
||||
"Modrinth pack (*.mrpack *.zip)", nullptr);
|
||||
if (output.isEmpty())
|
||||
return;
|
||||
if (!(output.endsWith(".zip") || output.endsWith(".mrpack")))
|
||||
output.append(".mrpack");
|
||||
} else {
|
||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".zip"),
|
||||
"CurseForge pack (*.zip)", nullptr);
|
||||
if (output.isEmpty())
|
||||
return;
|
||||
if (!output.endsWith(".zip"))
|
||||
output.append(".zip");
|
||||
}
|
||||
|
||||
Task* task;
|
||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
||||
task = new ModrinthPackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||
else
|
||||
task = new FlamePackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||
task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance,
|
||||
output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||
} else {
|
||||
task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output,
|
||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||
}
|
||||
|
||||
connect(task, &Task::failed,
|
||||
[this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||
@ -140,7 +160,6 @@ void ExportPackDialog::done(int result)
|
||||
|
||||
void ExportPackDialog::validate()
|
||||
{
|
||||
const bool invalid =
|
||||
ui->name->text().isEmpty() || ((m_provider == ModPlatform::ResourceProvider::MODRINTH) && ui->version->text().isEmpty());
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)
|
||||
->setDisabled(m_provider == ModPlatform::ResourceProvider::MODRINTH && ui->version->text().isEmpty());
|
||||
}
|
||||
|
@ -7,12 +7,9 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>650</width>
|
||||
<height>413</height>
|
||||
<height>510</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Export Pack</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -20,13 +17,16 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="information">
|
||||
<property name="title">
|
||||
<string>Information</string>
|
||||
<string>&Description</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="summaryLabel">
|
||||
<property name="text">
|
||||
<string>Summary</string>
|
||||
<string>&Summary</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>summary</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -36,14 +36,20 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
<string>&Name</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>name</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="versionLabel">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
<string>&Version</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>version</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -57,19 +63,27 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="options">
|
||||
<property name="title">
|
||||
<string>&Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="filesLabel">
|
||||
<property name="text">
|
||||
<string>Files</string>
|
||||
<string>&Files</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>files</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<widget class="QTreeView" name="files">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -84,6 +98,19 @@
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="optionalFiles">
|
||||
<property name="text">
|
||||
<string>&Mark disabled files as optional</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
@ -97,7 +124,8 @@
|
||||
<tabstop>name</tabstop>
|
||||
<tabstop>version</tabstop>
|
||||
<tabstop>summary</tabstop>
|
||||
<tabstop>treeView</tabstop>
|
||||
<tabstop>files</tabstop>
|
||||
<tabstop>optionalFiles</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
@ -129,7 +129,9 @@ InstallLoaderDialog::InstallLoaderDialog(std::shared_ptr<PackProfile> profile, c
|
||||
|
||||
QList<BasePage*> InstallLoaderDialog::getPages()
|
||||
{
|
||||
return { // Forge
|
||||
return { // NeoForge
|
||||
new InstallLoaderPage("net.neoforged", "neoforged", tr("NeoForge"), {}, profile),
|
||||
// Forge
|
||||
new InstallLoaderPage("net.minecraftforge", "forge", tr("Forge"), {}, profile),
|
||||
// Fabric
|
||||
new InstallLoaderPage("net.fabricmc.fabric-loader", "fabricmc", tr("Fabric"), Version("1.14"), profile),
|
||||
|
@ -3,10 +3,11 @@
|
||||
#include "CustomMessageBox.h"
|
||||
#include "ProgressDialog.h"
|
||||
#include "ScrollMessageBox.h"
|
||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "ui_ReviewMessageBox.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "Markdown.h"
|
||||
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
@ -30,9 +31,9 @@ static std::list<Version> mcVersions(BaseInstance* inst)
|
||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
|
||||
}
|
||||
|
||||
static std::optional<ResourceAPI::ModLoaderTypes> mcLoaders(BaseInstance* inst)
|
||||
static std::optional<ModPlatform::ModLoaderTypes> mcLoaders(BaseInstance* inst)
|
||||
{
|
||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders() };
|
||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders() };
|
||||
}
|
||||
|
||||
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||
@ -43,7 +44,8 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||
, m_parent(parent)
|
||||
, m_mod_model(mods)
|
||||
, m_candidates(search_for)
|
||||
, m_second_try_metadata(new ConcurrentTask())
|
||||
, m_second_try_metadata(
|
||||
new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()))
|
||||
, m_instance(instance)
|
||||
{
|
||||
ReviewMessageBox::setGeometry(0, 0, 800, 600);
|
||||
@ -126,6 +128,8 @@ void ModUpdateDialog::checkCandidates()
|
||||
return;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers;
|
||||
|
||||
// Add found updates for Modrinth
|
||||
if (m_modrinth_check_task) {
|
||||
auto modrinth_updates = m_modrinth_check_task->getUpdatable();
|
||||
@ -135,6 +139,7 @@ void ModUpdateDialog::checkCandidates()
|
||||
appendMod(updatable);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
selectedVers.append(m_modrinth_check_task->getDependencies());
|
||||
}
|
||||
|
||||
// Add found updated for Flame
|
||||
@ -146,6 +151,7 @@ void ModUpdateDialog::checkCandidates()
|
||||
appendMod(updatable);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
selectedVers.append(m_flame_check_task->getDependencies());
|
||||
}
|
||||
|
||||
// Report failed update checking
|
||||
@ -180,6 +186,47 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
}
|
||||
|
||||
{ // dependencies
|
||||
auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, m_mod_model.get(), selectedVers);
|
||||
|
||||
connect(depTask.get(), &Task::failed, this,
|
||||
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
|
||||
connect(depTask.get(), &Task::succeeded, this, [&]() {
|
||||
QStringList warnings = depTask->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
|
||||
}
|
||||
});
|
||||
|
||||
ProgressDialog progress_dialog_deps(m_parent);
|
||||
progress_dialog_deps.setSkipButton(true, tr("Abort"));
|
||||
progress_dialog_deps.setWindowTitle(tr("Checking for dependencies..."));
|
||||
auto dret = progress_dialog_deps.execWithTask(depTask.get());
|
||||
|
||||
// If the dialog was skipped / some download error happened
|
||||
if (dret == QDialog::DialogCode::Rejected) {
|
||||
m_aborted = true;
|
||||
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
static FlameAPI api;
|
||||
|
||||
auto getRequiredBy = depTask->getRequiredBy();
|
||||
|
||||
for (auto dep : depTask->getDependecies()) {
|
||||
auto changelog = dep->version.changelog;
|
||||
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
|
||||
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
|
||||
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_mod_model);
|
||||
CheckUpdateTask::UpdatableMod updatable = { dep->pack->name, dep->version.hash, "", dep->version.version,
|
||||
changelog, dep->pack->provider, download_task };
|
||||
|
||||
appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString()));
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no mod to be updated
|
||||
if (ui->modTreeWidget->topLevelItemCount() == 0) {
|
||||
m_no_updates = true;
|
||||
@ -238,6 +285,10 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
if (skip_rest)
|
||||
continue;
|
||||
|
||||
if (candidate->type() == ResourceType::FOLDER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (confirm_rest) {
|
||||
addToTmp(candidate, provider_rest);
|
||||
should_try_others.insert(candidate->internal_id(), try_others_rest);
|
||||
@ -348,7 +399,7 @@ void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::R
|
||||
}
|
||||
}
|
||||
|
||||
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info)
|
||||
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy)
|
||||
{
|
||||
auto item_top = new QTreeWidgetItem(ui->modTreeWidget);
|
||||
item_top->setCheckState(0, Qt::CheckState::Checked);
|
||||
@ -364,6 +415,21 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info)
|
||||
auto new_version_item = new QTreeWidgetItem(item_top);
|
||||
new_version_item->setText(0, tr("New version: %1").arg(info.new_version));
|
||||
|
||||
if (!requiredBy.isEmpty()) {
|
||||
auto requiredByItem = new QTreeWidgetItem(item_top);
|
||||
if (requiredBy.length() == 1) {
|
||||
requiredByItem->setText(0, tr("Required by: %1").arg(requiredBy.back()));
|
||||
} else {
|
||||
requiredByItem->setText(0, tr("Required by:"));
|
||||
auto i = 0;
|
||||
for (auto req : requiredBy) {
|
||||
auto reqItem = new QTreeWidgetItem(requiredByItem);
|
||||
reqItem->setText(0, req);
|
||||
reqItem->insertChildren(i++, { reqItem });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto changelog_item = new QTreeWidgetItem(item_top);
|
||||
changelog_item->setText(0, tr("Changelog of the latest version"));
|
||||
|
||||
|
@ -23,7 +23,7 @@ class ModUpdateDialog final : public ReviewMessageBox {
|
||||
|
||||
void checkCandidates();
|
||||
|
||||
void appendMod(const CheckUpdateTask::UpdatableMod& info);
|
||||
void appendMod(const CheckUpdateTask::UpdatableMod& info, QStringList requiredBy = {});
|
||||
|
||||
const QList<ResourceDownloadTask::Ptr> getTasks();
|
||||
auto indexDir() const -> QDir { return m_mod_model->indexDir(); }
|
||||
|
@ -127,35 +127,12 @@ void ResourceDownloadDialog::connectButtons()
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
QStringList getRequiredBy(QList<ResourceDownloadDialog::DownloadTaskPtr> tasks, ResourceDownloadDialog::DownloadTaskPtr pack)
|
||||
{
|
||||
auto addonId = pack->getPack()->addonId;
|
||||
auto provider = pack->getPack()->provider;
|
||||
auto version = pack->getVersionID();
|
||||
auto req = QStringList();
|
||||
for (auto& task : tasks) {
|
||||
if (provider != task->getPack()->provider)
|
||||
continue;
|
||||
auto deps = task->getVersion().dependencies;
|
||||
if (auto dep = std::find_if(deps.begin(), deps.end(),
|
||||
[addonId, provider, version](const ModPlatform::Dependency& d) {
|
||||
return d.type == ModPlatform::DependencyType::REQUIRED &&
|
||||
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
|
||||
? version == d.version
|
||||
: d.addonId == addonId);
|
||||
});
|
||||
dep != deps.end()) {
|
||||
req.append(task->getName());
|
||||
}
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
void ResourceDownloadDialog::confirm()
|
||||
{
|
||||
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
||||
confirm_dialog->retranslateUi(resourcesString());
|
||||
|
||||
QHash<QString, QStringList> getRequiredBy;
|
||||
if (auto task = getModDependenciesTask(); task) {
|
||||
connect(task.get(), &Task::failed, this,
|
||||
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
@ -180,6 +157,7 @@ void ResourceDownloadDialog::confirm()
|
||||
} else {
|
||||
for (auto dep : task->getDependecies())
|
||||
addResource(dep->pack, dep->version);
|
||||
getRequiredBy = task->getRequiredBy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +167,8 @@ void ResourceDownloadDialog::confirm()
|
||||
});
|
||||
for (auto& task : selected) {
|
||||
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
||||
ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) });
|
||||
ProviderCaps.name(task->getProvider()),
|
||||
getRequiredBy.value(task->getPack()->addonId.toString()) });
|
||||
}
|
||||
|
||||
if (confirm_dialog->exec()) {
|
||||
@ -279,7 +258,7 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
||||
{
|
||||
QList<BasePage*> pages;
|
||||
|
||||
auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getModLoaders().value();
|
||||
auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getSupportedModLoaders().value();
|
||||
|
||||
if (ModrinthAPI::validateModLoaders(loaders))
|
||||
pages.append(ModrinthModPage::create(this, *m_instance));
|
||||
@ -370,6 +349,8 @@ QList<BasePage*> ShaderPackDownloadDialog::getPages()
|
||||
{
|
||||
QList<BasePage*> pages;
|
||||
pages.append(ModrinthShaderPackPage::create(this, *m_instance));
|
||||
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
||||
pages.append(FlameShaderPackPage::create(this, *m_instance));
|
||||
return pages;
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,6 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>28</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
|
@ -64,7 +64,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new
|
||||
ui->setupUi(this);
|
||||
ui->listView->setEmptyString(
|
||||
tr("Welcome!\n"
|
||||
"If you're new here, you can click the \"Add\" button to add your Mojang or Minecraft account."));
|
||||
"If you're new here, you can select the \"Add Microsoft\" or \"Add Mojang\" buttons to link your Microsoft and/or Mojang "
|
||||
"accounts."));
|
||||
ui->listView->setEmptyMode(VersionListView::String);
|
||||
ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
@ -85,6 +86,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
||||
[this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); });
|
||||
connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu);
|
||||
connect(ui->listView, &VersionListView::activated, this,
|
||||
[this](const QModelIndex& index) { m_accounts->setDefaultAccount(m_accounts->at(index.row())); });
|
||||
|
||||
connect(m_accounts.get(), &AccountList::listChanged, this, &AccountListPage::listChanged);
|
||||
connect(m_accounts.get(), &AccountList::listActivityChanged, this, &AccountListPage::listChanged);
|
||||
|
@ -185,6 +185,7 @@ void JavaPage::updateThresholds()
|
||||
{
|
||||
auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
|
||||
unsigned int maxMem = ui->maxMemSpinBox->value();
|
||||
unsigned int minMem = ui->minMemSpinBox->value();
|
||||
|
||||
QString iconName;
|
||||
|
||||
@ -194,6 +195,9 @@ void JavaPage::updateThresholds()
|
||||
} else if (maxMem > (sysMiB * 0.9)) {
|
||||
iconName = "status-yellow";
|
||||
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
|
||||
} else if (maxMem < minMem) {
|
||||
iconName = "status-yellow";
|
||||
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
|
||||
} else {
|
||||
iconName = "status-good";
|
||||
ui->labelMaxMemIcon->setToolTip("");
|
||||
|
@ -189,6 +189,9 @@ void LauncherPage::applySettings()
|
||||
|
||||
s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked());
|
||||
|
||||
s->set("NumberOfConcurrentTasks", ui->numberOfConcurrentTasksSpinBox->value());
|
||||
s->set("NumberOfConcurrentDownloads", ui->numberOfConcurrentDownloadsSpinBox->value());
|
||||
|
||||
// Console settings
|
||||
s->set("ShowConsole", ui->showConsoleCheck->isChecked());
|
||||
s->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
|
||||
@ -236,6 +239,9 @@ void LauncherPage::loadSettings()
|
||||
#endif
|
||||
ui->preferMenuBarCheckBox->setChecked(s->get("MenuBarInsteadOfToolBar").toBool());
|
||||
|
||||
ui->numberOfConcurrentTasksSpinBox->setValue(s->get("NumberOfConcurrentTasks").toInt());
|
||||
ui->numberOfConcurrentDownloadsSpinBox->setValue(s->get("NumberOfConcurrentDownloads").toInt());
|
||||
|
||||
// Console settings
|
||||
ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool());
|
||||
ui->autoCloseConsoleCheck->setChecked(s->get("AutoCloseConsole").toBool());
|
||||
|
@ -189,6 +189,43 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="miscellaneousGroupBox">
|
||||
<property name="title">
|
||||
<string>Miscellaneous</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="numberOfConcurrentTasksLabel">
|
||||
<property name="text">
|
||||
<string>Number of concurrent tasks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="numberOfConcurrentTasksSpinBox">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="numberOfConcurrentDownloadsLabel">
|
||||
<property name="text">
|
||||
<string>Number of concurrent downloads</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="numberOfConcurrentDownloadsSpinBox">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
|
@ -2,7 +2,6 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 seth <getchoo at tuta dot io>
|
||||
*
|
||||
* 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
|
||||
@ -35,6 +34,7 @@
|
||||
*/
|
||||
|
||||
#include "MinecraftPage.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "ui_MinecraftPage.h"
|
||||
|
||||
#include <QDir>
|
||||
@ -44,9 +44,15 @@
|
||||
#include "Application.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "MangoHud.h"
|
||||
#endif
|
||||
|
||||
MinecraftPage::MinecraftPage(QWidget* parent) : QWidget(parent), ui(new Ui::MinecraftPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &MinecraftPage::onUseNativeGLFWChanged);
|
||||
connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &MinecraftPage::onUseNativeOpenALChanged);
|
||||
loadSettings();
|
||||
updateCheckboxStuff();
|
||||
}
|
||||
@ -74,6 +80,16 @@ void MinecraftPage::on_maximizedCheckBox_clicked(bool checked)
|
||||
updateCheckboxStuff();
|
||||
}
|
||||
|
||||
void MinecraftPage::onUseNativeGLFWChanged(bool checked)
|
||||
{
|
||||
ui->lineEditGLFWPath->setEnabled(checked);
|
||||
}
|
||||
|
||||
void MinecraftPage::onUseNativeOpenALChanged(bool checked)
|
||||
{
|
||||
ui->lineEditOpenALPath->setEnabled(checked);
|
||||
}
|
||||
|
||||
void MinecraftPage::applySettings()
|
||||
{
|
||||
auto s = APPLICATION->settings();
|
||||
@ -84,8 +100,10 @@ void MinecraftPage::applySettings()
|
||||
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
||||
|
||||
// Native library workarounds
|
||||
s->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
||||
s->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked());
|
||||
s->set("CustomGLFWPath", ui->lineEditGLFWPath->text());
|
||||
s->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
||||
s->set("CustomOpenALPath", ui->lineEditOpenALPath->text());
|
||||
|
||||
// Peformance related options
|
||||
s->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked());
|
||||
@ -96,13 +114,11 @@ void MinecraftPage::applySettings()
|
||||
s->set("ShowGameTime", ui->showGameTime->isChecked());
|
||||
s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked());
|
||||
s->set("RecordGameTime", ui->recordGameTime->isChecked());
|
||||
s->set("ShowGameTimeWithoutDays", ui->showGameTimeWithoutDays->isChecked());
|
||||
|
||||
// Miscellaneous
|
||||
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
|
||||
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
|
||||
|
||||
// Mod loader settings
|
||||
s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked());
|
||||
}
|
||||
|
||||
void MinecraftPage::loadSettings()
|
||||
@ -114,8 +130,20 @@ void MinecraftPage::loadSettings()
|
||||
ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
|
||||
ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
|
||||
|
||||
ui->useNativeOpenALCheck->setChecked(s->get("UseNativeOpenAL").toBool());
|
||||
ui->useNativeGLFWCheck->setChecked(s->get("UseNativeGLFW").toBool());
|
||||
ui->lineEditGLFWPath->setText(s->get("CustomGLFWPath").toString());
|
||||
ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.GLFW_LIBRARY_NAME));
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!APPLICATION->m_detectedGLFWPath.isEmpty())
|
||||
ui->lineEditGLFWPath->setPlaceholderText(tr("Auto detected path: %1").arg(APPLICATION->m_detectedGLFWPath));
|
||||
#endif
|
||||
ui->useNativeOpenALCheck->setChecked(s->get("UseNativeOpenAL").toBool());
|
||||
ui->lineEditOpenALPath->setText(s->get("CustomOpenALPath").toString());
|
||||
ui->lineEditOpenALPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME));
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!APPLICATION->m_detectedOpenALPath.isEmpty())
|
||||
ui->lineEditOpenALPath->setPlaceholderText(tr("Auto detected path: %1").arg(APPLICATION->m_detectedOpenALPath));
|
||||
#endif
|
||||
|
||||
ui->enableFeralGamemodeCheck->setChecked(s->get("EnableFeralGamemode").toBool());
|
||||
ui->enableMangoHud->setChecked(s->get("EnableMangoHud").toBool());
|
||||
@ -138,11 +166,10 @@ void MinecraftPage::loadSettings()
|
||||
ui->showGameTime->setChecked(s->get("ShowGameTime").toBool());
|
||||
ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool());
|
||||
ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool());
|
||||
ui->showGameTimeWithoutDays->setChecked(s->get("ShowGameTimeWithoutDays").toBool());
|
||||
|
||||
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
|
||||
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
|
||||
|
||||
ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool());
|
||||
}
|
||||
|
||||
void MinecraftPage::retranslate()
|
||||
|
@ -70,6 +70,9 @@ class MinecraftPage : public QWidget, public BasePage {
|
||||
private slots:
|
||||
void on_maximizedCheckBox_clicked(bool checked);
|
||||
|
||||
void onUseNativeGLFWChanged(bool checked);
|
||||
void onUseNativeOpenALChanged(bool checked);
|
||||
|
||||
private:
|
||||
Ui::MinecraftPage* ui;
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user