Merge branch 'develop' into feat/launcher-updater
Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
commit
befa3baa6a
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
uses: korthout/backport-action@v1.3.1
|
uses: korthout/backport-action@v1.4.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
|
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: cachix/install-nix-action@v22
|
- uses: cachix/install-nix-action@v22
|
||||||
|
|
||||||
- uses: DeterminateSystems/update-flake-lock@v19
|
- uses: DeterminateSystems/update-flake-lock@v20
|
||||||
with:
|
with:
|
||||||
commit-msg: "chore(nix): update lockfile"
|
commit-msg: "chore(nix): update lockfile"
|
||||||
pr-title: "chore(nix): update lockfile"
|
pr-title: "chore(nix): update lockfile"
|
||||||
|
@ -219,6 +219,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_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||||
|
|
||||||
|
# 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
|
# API Keys
|
||||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
# 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.
|
# of these platforms, please change these API keys beforehand.
|
||||||
|
@ -118,6 +118,9 @@ Config::Config()
|
|||||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||||
META_URL = "@Launcher_META_URL@";
|
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@";
|
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||||
|
@ -152,6 +152,9 @@ class Config {
|
|||||||
*/
|
*/
|
||||||
QString META_URL;
|
QString META_URL;
|
||||||
|
|
||||||
|
QString GLFW_LIBRARY_NAME;
|
||||||
|
QString OPENAL_LIBRARY_NAME;
|
||||||
|
|
||||||
QString BUG_TRACKER_URL;
|
QString BUG_TRACKER_URL;
|
||||||
QString TRANSLATIONS_URL;
|
QString TRANSLATIONS_URL;
|
||||||
QString MATRIX_URL;
|
QString MATRIX_URL;
|
||||||
|
@ -67,5 +67,16 @@
|
|||||||
<string>Alternate</string>
|
<string>Alternate</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Curseforge</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>curseforge</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
12
flake.lock
generated
12
flake.lock
generated
@ -91,11 +91,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1691853136,
|
"lastModified": 1693145325,
|
||||||
"narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=",
|
"narHash": "sha256-Gat9xskErH1zOcLjYMhSDBo0JTBZKfGS0xJlIRnj6Rc=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f0451844bbdf545f696f029d1448de4906c7f753",
|
"rev": "cddebdb60de376c1bdb7a4e6ee3d98355453fe56",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -138,11 +138,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1691747570,
|
"lastModified": 1692274144,
|
||||||
"narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=",
|
"narHash": "sha256-BxTQuRUANQ81u8DJznQyPmRsg63t4Yc+0kcyq6OLz8s=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a",
|
"rev": "7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
* Copyright (C) 2022 Tayou <git@tayou.org>
|
* Copyright (C) 2022 Tayou <git@tayou.org>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -227,8 +226,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||||
{ { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" },
|
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||||
|
// Has to be positional for some OS to handle that properly
|
||||||
|
parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]");
|
||||||
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
@ -241,13 +243,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
m_instanceIdToShowWindowOf = parser.value("show");
|
m_instanceIdToShowWindowOf = parser.value("show");
|
||||||
|
|
||||||
for (auto zip_path : parser.values("import")) {
|
for (auto url : parser.values("import")) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// treat unspecified positional arguments as import urls
|
// treat unspecified positional arguments as import urls
|
||||||
for (auto zip_path : parser.positionalArguments()) {
|
for (auto url : parser.positionalArguments()) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// error if --launch is missing with --server or --profile
|
// error if --launch is missing with --server or --profile
|
||||||
@ -347,11 +349,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
activate.command = "activate";
|
activate.command = "activate";
|
||||||
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
||||||
|
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
for (auto zip_url : m_zipsToImport) {
|
for (auto url : m_urlsToImport) {
|
||||||
ApplicationMessage import;
|
ApplicationMessage import;
|
||||||
import.command = "import";
|
import.command = "import";
|
||||||
import.args.insert("path", zip_url.toString());
|
import.args.insert("url", url.toString());
|
||||||
m_peerInstance->sendMessage(import.serialize(), timeout);
|
m_peerInstance->sendMessage(import.serialize(), timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -616,12 +618,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
m_settings->registerSetting("IgnoreJavaCompatibility", false);
|
||||||
m_settings->registerSetting("IgnoreJavaWizard", false);
|
m_settings->registerSetting("IgnoreJavaWizard", false);
|
||||||
|
|
||||||
// Mod loader settings
|
|
||||||
m_settings->registerSetting("DisableQuiltBeacon", false);
|
|
||||||
|
|
||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||||
|
m_settings->registerSetting("CustomOpenALPath", "");
|
||||||
m_settings->registerSetting("UseNativeGLFW", false);
|
m_settings->registerSetting("UseNativeGLFW", false);
|
||||||
|
m_settings->registerSetting("CustomGLFWPath", "");
|
||||||
|
|
||||||
// Peformance related options
|
// Peformance related options
|
||||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||||
@ -632,6 +633,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("ShowGameTime", true);
|
m_settings->registerSetting("ShowGameTime", true);
|
||||||
m_settings->registerSetting("ShowGlobalGameTime", true);
|
m_settings->registerSetting("ShowGlobalGameTime", true);
|
||||||
m_settings->registerSetting("RecordGameTime", true);
|
m_settings->registerSetting("RecordGameTime", true);
|
||||||
|
m_settings->registerSetting("ShowGameTimeWithoutDays", false);
|
||||||
|
|
||||||
// Minecraft mods
|
// Minecraft mods
|
||||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||||
@ -872,6 +874,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
updateCapabilities();
|
updateCapabilities();
|
||||||
|
|
||||||
|
detectLibraries();
|
||||||
|
|
||||||
// check update locks
|
// check update locks
|
||||||
{
|
{
|
||||||
auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log");
|
auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log");
|
||||||
@ -1112,7 +1116,7 @@ void Application::performMainStartupAction()
|
|||||||
qDebug() << " Launching with account" << m_profileToUse;
|
qDebug() << " Launching with account" << m_profileToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(inst, true, false, nullptr, serverToJoin, accountToUse);
|
launch(inst, true, false, serverToJoin, accountToUse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1143,9 +1147,9 @@ void Application::performMainStartupAction()
|
|||||||
qDebug() << "<> Updater started.";
|
qDebug() << "<> Updater started.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
qDebug() << "<> Importing from zip:" << m_zipsToImport;
|
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||||
m_mainWindow->processURLs(m_zipsToImport);
|
m_mainWindow->processURLs(m_urlsToImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1187,12 +1191,12 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
if (command == "activate") {
|
if (command == "activate") {
|
||||||
showMainWindow();
|
showMainWindow();
|
||||||
} else if (command == "import") {
|
} else if (command == "import") {
|
||||||
QString path = received.args["path"];
|
QString url = received.args["url"];
|
||||||
if (path.isEmpty()) {
|
if (url.isEmpty()) {
|
||||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
|
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||||
} else if (command == "launch") {
|
} else if (command == "launch") {
|
||||||
QString id = received.args["id"];
|
QString id = received.args["id"];
|
||||||
QString server = received.args["server"];
|
QString server = received.args["server"];
|
||||||
@ -1225,7 +1229,7 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(instance, true, false, nullptr, serverObject, accountObject);
|
launch(instance, true, false, serverObject, accountObject);
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Received invalid message" << message;
|
qWarning() << "Received invalid message" << message;
|
||||||
}
|
}
|
||||||
@ -1266,7 +1270,6 @@ bool Application::openJsonEditor(const QString& filename)
|
|||||||
bool Application::launch(InstancePtr instance,
|
bool Application::launch(InstancePtr instance,
|
||||||
bool online,
|
bool online,
|
||||||
bool demo,
|
bool demo,
|
||||||
BaseProfilerFactory* profiler,
|
|
||||||
MinecraftServerTargetPtr serverToJoin,
|
MinecraftServerTargetPtr serverToJoin,
|
||||||
MinecraftAccountPtr accountToUse)
|
MinecraftAccountPtr accountToUse)
|
||||||
{
|
{
|
||||||
@ -1274,7 +1277,7 @@ bool Application::launch(InstancePtr instance,
|
|||||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||||
} else if (instance->canLaunch()) {
|
} else if (instance->canLaunch()) {
|
||||||
auto& extras = m_instanceExtras[instance->id()];
|
auto& extras = m_instanceExtras[instance->id()];
|
||||||
auto& window = extras.window;
|
auto window = extras.window;
|
||||||
if (window) {
|
if (window) {
|
||||||
if (!window->saveAll()) {
|
if (!window->saveAll()) {
|
||||||
return false;
|
return false;
|
||||||
@ -1285,7 +1288,7 @@ bool Application::launch(InstancePtr instance,
|
|||||||
controller->setInstance(instance);
|
controller->setInstance(instance);
|
||||||
controller->setOnline(online);
|
controller->setOnline(online);
|
||||||
controller->setDemo(demo);
|
controller->setDemo(demo);
|
||||||
controller->setProfiler(profiler);
|
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||||
controller->setServerToJoin(serverToJoin);
|
controller->setServerToJoin(serverToJoin);
|
||||||
controller->setAccountToUse(accountToUse);
|
controller->setAccountToUse(accountToUse);
|
||||||
if (window) {
|
if (window) {
|
||||||
@ -1577,6 +1580,15 @@ void Application::updateCapabilities()
|
|||||||
#endif
|
#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)
|
QString Application::getJarPath(QString jarFile)
|
||||||
{
|
{
|
||||||
QStringList potentialPaths = {
|
QStringList potentialPaths = {
|
||||||
@ -1755,3 +1767,13 @@ void Application::triggerUpdateCheck()
|
|||||||
qDebug() << "Updater not available.";
|
qDebug() << "Updater not available.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl Application::normalizeImportUrl(QString const& url)
|
||||||
|
{
|
||||||
|
auto local_file = QFileInfo(url);
|
||||||
|
if (local_file.exists()) {
|
||||||
|
return QUrl::fromLocalFile(local_file.absoluteFilePath());
|
||||||
|
} else {
|
||||||
|
return QUrl::fromUserInput(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -142,6 +142,8 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
void updateCapabilities();
|
void updateCapabilities();
|
||||||
|
|
||||||
|
void detectLibraries();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Finds and returns the full path to a jar file.
|
* Finds and returns the full path to a jar file.
|
||||||
* Returns a null-string if it could not be found.
|
* Returns a null-string if it could not be found.
|
||||||
@ -183,6 +185,8 @@ class Application : public QApplication {
|
|||||||
bool updaterEnabled();
|
bool updaterEnabled();
|
||||||
QString updaterBinaryName();
|
QString updaterBinaryName();
|
||||||
|
|
||||||
|
QUrl normalizeImportUrl(QString const& url);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
@ -197,7 +201,6 @@ class Application : public QApplication {
|
|||||||
bool launch(InstancePtr instance,
|
bool launch(InstancePtr instance,
|
||||||
bool online = true,
|
bool online = true,
|
||||||
bool demo = false,
|
bool demo = false,
|
||||||
BaseProfilerFactory* profiler = nullptr,
|
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||||
MinecraftAccountPtr accountToUse = nullptr);
|
MinecraftAccountPtr accountToUse = nullptr);
|
||||||
bool kill(InstancePtr instance);
|
bool kill(InstancePtr instance);
|
||||||
@ -282,11 +285,13 @@ class Application : public QApplication {
|
|||||||
SetupWizard* m_setupWizard = nullptr;
|
SetupWizard* m_setupWizard = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
QString m_detectedGLFWPath;
|
||||||
|
QString m_detectedOpenALPath;
|
||||||
QString m_instanceIdToLaunch;
|
QString m_instanceIdToLaunch;
|
||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QList<QUrl> m_zipsToImport;
|
QList<QUrl> m_urlsToImport;
|
||||||
QString m_instanceIdToShowWindowOf;
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -100,6 +101,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
m_settings->registerSetting("ManagedPackName", "");
|
m_settings->registerSetting("ManagedPackName", "");
|
||||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||||
|
|
||||||
|
m_settings->registerSetting("Profiler", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getPreLaunchCommand()
|
QString BaseInstance::getPreLaunchCommand()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -38,6 +39,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QMenu>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@ -246,6 +248,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
virtual bool canEdit() const = 0;
|
virtual bool canEdit() const = 0;
|
||||||
virtual bool canExport() const = 0;
|
virtual bool canExport() const = 0;
|
||||||
|
|
||||||
|
virtual void populateLaunchMenu(QMenu* menu) = 0;
|
||||||
|
|
||||||
bool reloadSettings();
|
bool reloadSettings();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -282,6 +286,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
|||||||
|
|
||||||
void runningStatusChanged(bool running);
|
void runningStatusChanged(bool running);
|
||||||
|
|
||||||
|
void profilerChanged();
|
||||||
|
|
||||||
void statusChanged(Status from, Status to);
|
void statusChanged(Status from, Status to);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
@ -267,10 +267,7 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP
|
|||||||
|
|
||||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||||
{
|
{
|
||||||
auto fileName = fileInfo.fileName();
|
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
@ -90,23 +91,25 @@ void InstanceImportTask::executeTask()
|
|||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
m_downloadRequired = true;
|
m_downloadRequired = true;
|
||||||
|
|
||||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
downloadFromUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::downloadFromUrl()
|
||||||
|
{
|
||||||
|
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
m_archivePath = entry->getFullPath();
|
|
||||||
|
|
||||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||||
|
m_archivePath = entry->getFullPath();
|
||||||
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||||
|
|
||||||
m_filesNetJob->start();
|
m_filesNetJob->start();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::downloadSucceeded()
|
void InstanceImportTask::downloadSucceeded()
|
||||||
|
@ -101,4 +101,5 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
|
|
||||||
// FIXME: nuke
|
// FIXME: nuke
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
|
void downloadFromUrl();
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -361,22 +362,21 @@ void LaunchController::readyForLaunch()
|
|||||||
QString error;
|
QString error;
|
||||||
if (!m_profiler->check(&error)) {
|
if (!m_profiler->check(&error)) {
|
||||||
m_launcher->abort();
|
m_launcher->abort();
|
||||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
|
||||||
emitFailed("Profiler startup failed!");
|
emitFailed("Profiler startup failed!");
|
||||||
|
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||||
|
|
||||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
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 "
|
msg.setText(tr("The game launch is delayed until you press the "
|
||||||
"button. This is the right time to setup the profiler, as the "
|
"button. This is the right time to setup the profiler, as the "
|
||||||
"profiler server is running now.\n\n%1")
|
"profiler server is running now.\n\n%1")
|
||||||
.arg(message));
|
.arg(message));
|
||||||
msg.setWindowTitle(tr("Waiting."));
|
msg.setWindowTitle(tr("Waiting."));
|
||||||
msg.setIcon(QMessageBox::Information);
|
msg.setIcon(QMessageBox::Information);
|
||||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
|
||||||
msg.setModal(true);
|
|
||||||
msg.exec();
|
msg.exec();
|
||||||
m_launcher->proceed();
|
m_launcher->proceed();
|
||||||
});
|
});
|
||||||
|
@ -16,19 +16,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <MMCTime.h>
|
#include <MMCTime.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
QString Time::prettifyDuration(int64_t duration)
|
QString Time::prettifyDuration(int64_t duration, bool noDays)
|
||||||
{
|
{
|
||||||
int seconds = (int)(duration % 60);
|
int seconds = (int)(duration % 60);
|
||||||
duration /= 60;
|
duration /= 60;
|
||||||
int minutes = (int)(duration % 60);
|
int minutes = (int)(duration % 60);
|
||||||
duration /= 60;
|
duration /= 60;
|
||||||
int hours = (int)(duration % 24);
|
int hours = (int)(noDays ? duration : (duration % 24));
|
||||||
int days = (int)(duration / 24);
|
int days = (int)(noDays ? 0 : (duration / 24));
|
||||||
if ((hours == 0) && (days == 0)) {
|
if ((hours == 0) && (days == 0)) {
|
||||||
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
namespace Time {
|
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`.
|
* @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/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@ -26,6 +27,15 @@
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "MangoHud.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 {
|
namespace MangoHud {
|
||||||
|
|
||||||
QString getLibraryString()
|
QString getLibraryString()
|
||||||
@ -106,4 +116,37 @@ QString getLibraryString()
|
|||||||
|
|
||||||
return QString();
|
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
|
} // namespace MangoHud
|
||||||
|
|
||||||
|
#ifdef UNDEF_GNU_SOURCE
|
||||||
|
#undef _GNU_SOURCE
|
||||||
|
#undef UNDEF_GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
@ -24,4 +24,6 @@
|
|||||||
namespace MangoHud {
|
namespace MangoHud {
|
||||||
|
|
||||||
QString getLibraryString();
|
QString getLibraryString();
|
||||||
}
|
|
||||||
|
QString findLibrary(QString libName);
|
||||||
|
} // namespace MangoHud
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -62,6 +63,7 @@ class NullInstance : public BaseInstance {
|
|||||||
bool canExport() const override { return false; }
|
bool canExport() const override { return false; }
|
||||||
bool canEdit() const override { return false; }
|
bool canEdit() const override { return false; }
|
||||||
bool canLaunch() const override { return false; }
|
bool canLaunch() const override { return false; }
|
||||||
|
void populateLaunchMenu(QMenu* menu) override {}
|
||||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
||||||
{
|
{
|
||||||
QStringList out;
|
QStringList out;
|
||||||
|
@ -104,14 +104,8 @@ class Version {
|
|||||||
|
|
||||||
QString m_fullString;
|
QString m_fullString;
|
||||||
|
|
||||||
[[nodiscard]] inline bool isAppendix() const
|
[[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
|
||||||
{
|
[[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
|
||||||
return m_stringPart.startsWith('+');
|
|
||||||
}
|
|
||||||
[[nodiscard]] inline bool isPreRelease() const
|
|
||||||
{
|
|
||||||
return m_stringPart.startsWith('-') && m_stringPart.length() > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(const Section& other) const
|
inline bool operator==(const Section& other) const
|
||||||
{
|
{
|
||||||
@ -157,14 +151,8 @@ class Version {
|
|||||||
return m_fullString < other.m_fullString;
|
return m_fullString < other.m_fullString;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator!=(const Section& other) const
|
inline bool operator!=(const Section& other) const { return !(*this == other); }
|
||||||
{
|
inline bool operator>(const Section& other) const { return !(*this < other || *this == other); }
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
inline bool operator>(const Section& other) const
|
|
||||||
{
|
|
||||||
return !(*this < other || *this == other);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "ComponentUpdateTask_p.h"
|
#include "ComponentUpdateTask_p.h"
|
||||||
#include "OneSixVersionFormat.h"
|
|
||||||
#include "PackProfile.h"
|
#include "PackProfile.h"
|
||||||
#include "PackProfile_p.h"
|
#include "PackProfile_p.h"
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
#include "cassert"
|
#include "cassert"
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/Version.h"
|
#include "meta/Version.h"
|
||||||
#include "meta/VersionList.h"
|
#include "minecraft/OneSixVersionFormat.h"
|
||||||
|
#include "minecraft/ProfileUtils.h"
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
* Copyright (c) 2023 seth <getchoo at tuta dot io>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -88,6 +87,10 @@
|
|||||||
#include "minecraft/gameoptions/GameOptions.h"
|
#include "minecraft/gameoptions/GameOptions.h"
|
||||||
#include "minecraft/update/FoldersTask.h"
|
#include "minecraft/update/FoldersTask.h"
|
||||||
|
|
||||||
|
#include "tools/BaseProfiler.h"
|
||||||
|
|
||||||
|
#include <QActionGroup>
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
#include "MangoHud.h"
|
#include "MangoHud.h"
|
||||||
#endif
|
#endif
|
||||||
@ -166,7 +169,9 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
|
||||||
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
|
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("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
|
||||||
|
m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
|
||||||
|
|
||||||
// Peformance related options
|
// Peformance related options
|
||||||
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
|
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("CloseAfterLaunch"), miscellaneousOverride);
|
||||||
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), 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");
|
m_settings->set("InstanceType", "OneSix");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +195,12 @@ void MinecraftInstance::loadSpecificSettings()
|
|||||||
m_settings->registerSetting("UseAccountForInstance", false);
|
m_settings->registerSetting("UseAccountForInstance", false);
|
||||||
m_settings->registerSetting("InstanceAccountId", "");
|
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!";
|
qDebug() << "Instance-type specific settings were loaded!";
|
||||||
|
|
||||||
setSpecificSettingsLoaded(true);
|
setSpecificSettingsLoaded(true);
|
||||||
@ -229,6 +236,50 @@ QSet<QString> MinecraftInstance::traits() const
|
|||||||
return profile->getTraits();
|
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
|
QString MinecraftInstance::gameRoot() const
|
||||||
{
|
{
|
||||||
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
|
||||||
@ -385,10 +436,31 @@ QStringList MinecraftInstance::extraArguments()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto loaders = version->getModLoaders();
|
QString openALPath;
|
||||||
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
|
QString glfwPath;
|
||||||
list.append("-Dloader.disable_beacon=true");
|
|
||||||
|
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;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,13 +940,16 @@ QString MinecraftInstance::getStatusbarDescription()
|
|||||||
if (m_settings->get("ShowGameTime").toBool()) {
|
if (m_settings->get("ShowGameTime").toBool()) {
|
||||||
if (lastTimePlayed() > 0) {
|
if (lastTimePlayed() > 0) {
|
||||||
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
|
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(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
|
||||||
.arg(Time::prettifyDuration(lastTimePlayed())));
|
.arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalTimePlayed() > 0) {
|
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()) {
|
if (hasCrashed()) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -70,6 +70,8 @@ class MinecraftInstance : public BaseInstance {
|
|||||||
|
|
||||||
bool canExport() const override { return true; }
|
bool canExport() const override { return true; }
|
||||||
|
|
||||||
|
void populateLaunchMenu(QMenu* menu) override;
|
||||||
|
|
||||||
////// Directories and files //////
|
////// Directories and files //////
|
||||||
QString jarModsDir() const;
|
QString jarModsDir() const;
|
||||||
QString resourcePacksDir() const;
|
QString resourcePacksDir() const;
|
||||||
|
@ -58,14 +58,14 @@
|
|||||||
#include "ComponentUpdateTask.h"
|
#include "ComponentUpdateTask.h"
|
||||||
#include "PackProfile.h"
|
#include "PackProfile.h"
|
||||||
#include "PackProfile_p.h"
|
#include "PackProfile_p.h"
|
||||||
|
#include "minecraft/mod/Mod.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
#include "Application.h"
|
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
|
||||||
#include "modplatform/ResourceAPI.h"
|
{ "net.minecraftforge", ModPlatform::Forge },
|
||||||
|
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
|
||||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge },
|
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
|
||||||
{ "net.fabricmc.fabric-loader", ResourceAPI::Fabric },
|
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
|
||||||
{ "org.quiltmc.quilt-loader", ResourceAPI::Quilt },
|
|
||||||
{ "com.mumfrey.liteloader", ResourceAPI::LiteLoader } };
|
|
||||||
|
|
||||||
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
|
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;
|
bool has_any_loader = false;
|
||||||
|
|
||||||
QMapIterator<QString, ResourceAPI::ModLoaderType> i(modloaderMapping);
|
QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
|
||||||
|
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
i.next();
|
i.next();
|
||||||
@ -1008,3 +1008,18 @@ std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
|
|||||||
return {};
|
return {};
|
||||||
return result;
|
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 <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
#include "LaunchProfile.h"
|
#include "LaunchProfile.h"
|
||||||
#include "Library.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "MojangDownloadInfo.h"
|
|
||||||
#include "ProfileUtils.h"
|
|
||||||
#include "modplatform/ResourceAPI.h"
|
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
class MinecraftInstance;
|
class MinecraftInstance;
|
||||||
@ -146,7 +143,9 @@ class PackProfile : public QAbstractListModel {
|
|||||||
// todo(merged): is this the best approach
|
// todo(merged): is this the best approach
|
||||||
void appendComponent(ComponentPtr component);
|
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:
|
private:
|
||||||
void scheduleSave();
|
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)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,7 +423,8 @@ bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int rol
|
|||||||
if (value == Qt::Checked) {
|
if (value == Qt::Checked) {
|
||||||
MinecraftAccountPtr account = at(idx.row());
|
MinecraftAccountPtr account = at(idx.row());
|
||||||
setDefaultAccount(account);
|
setDefaultAccount(account);
|
||||||
}
|
} else if (m_defaultAccount == at(idx.row()))
|
||||||
|
setDefaultAccount(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));
|
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;
|
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);
|
QuaZip zip(source);
|
||||||
if (!zip.open(QuaZip::mdUnzip)) {
|
if (!zip.open(QuaZip::mdUnzip)) {
|
||||||
@ -52,12 +52,6 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH
|
|||||||
do {
|
do {
|
||||||
QString name = zip.getCurrentFileName();
|
QString name = zip.getCurrentFileName();
|
||||||
auto lowercase = name.toLower();
|
auto lowercase = name.toLower();
|
||||||
if (nativeGLFW && name.contains("glfw")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (nativeOpenAL && name.contains("openal")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (applyJnilibHack) {
|
if (applyJnilibHack) {
|
||||||
name = replaceSuffix(name, ".jnilib", ".dylib");
|
name = replaceSuffix(name, ".jnilib", ".dylib");
|
||||||
}
|
}
|
||||||
@ -83,14 +77,12 @@ void ExtractNatives::executeTask()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto settings = minecraftInstance->settings();
|
auto settings = minecraftInstance->settings();
|
||||||
bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
|
|
||||||
bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
|
|
||||||
|
|
||||||
auto outputPath = minecraftInstance->getNativePath();
|
auto outputPath = minecraftInstance->getNativePath();
|
||||||
auto javaVersion = minecraftInstance->getJavaVersion();
|
auto javaVersion = minecraftInstance->getJavaVersion();
|
||||||
bool jniHackEnabled = javaVersion.major() >= 8;
|
bool jniHackEnabled = javaVersion.major() >= 8;
|
||||||
for (const auto& source : toExtract) {
|
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'");
|
const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
|
||||||
emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
|
emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
|
||||||
emitFailed(tr(reason).arg(source, outputPath));
|
emitFailed(tr(reason).arg(source, outputPath));
|
||||||
|
@ -51,8 +51,13 @@
|
|||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include "Json.h"
|
||||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||||
|
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||||
#include "minecraft/mod/tasks/ModFolderLoadTask.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)
|
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)
|
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
||||||
@ -309,3 +314,47 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
|
|||||||
|
|
||||||
emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
|
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/LocalModParseTask.h"
|
||||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
class LegacyInstance;
|
class LegacyInstance;
|
||||||
class BaseInstance;
|
class BaseInstance;
|
||||||
@ -75,6 +76,7 @@ class ModFolderModel : public ResourceFolderModel {
|
|||||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||||
|
|
||||||
bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); }
|
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);
|
bool uninstallMod(const QString& filename, bool preserve_metadata = false);
|
||||||
|
|
||||||
/// Deletes all the selected mods
|
/// Deletes all the selected mods
|
||||||
|
@ -39,9 +39,9 @@ static Version mcVersion(BaseInstance* inst)
|
|||||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
|
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,
|
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||||
@ -75,7 +75,7 @@ void GetModDependenciesTask::prepare()
|
|||||||
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
||||||
const ModPlatform::ResourceProvider providerName)
|
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 overide = ModPlatform::getOverrideDeps();
|
||||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) {
|
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);
|
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);
|
pDep->version = provider.mod->loadDependencyVersions(dep, arr);
|
||||||
if (!pDep->version.addonId.isValid()) {
|
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 overide = ModPlatform::getOverrideDeps();
|
||||||
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||||
[dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; });
|
[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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
removePack(dep.addonId);
|
||||||
qWarning() << "Error while reading mod version empty ";
|
qWarning() << "Error while reading mod version empty ";
|
||||||
qDebug() << doc;
|
qDebug() << doc;
|
||||||
return;
|
return;
|
||||||
|
@ -80,5 +80,5 @@ class GetModDependenciesTask : public SequentialTask {
|
|||||||
Provider m_modrinth_provider;
|
Provider m_modrinth_provider;
|
||||||
|
|
||||||
Version m_version;
|
Version m_version;
|
||||||
ResourceAPI::ModLoaderTypes m_loaderType;
|
ModPlatform::ModLoaderTypes m_loaderType;
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ class CheckUpdateTask : public Task {
|
|||||||
public:
|
public:
|
||||||
CheckUpdateTask(QList<Mod*>& mods,
|
CheckUpdateTask(QList<Mod*>& mods,
|
||||||
std::list<Version>& mcVersions,
|
std::list<Version>& mcVersions,
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<ModFolderModel> mods_folder)
|
std::shared_ptr<ModFolderModel> mods_folder)
|
||||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){};
|
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){};
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class CheckUpdateTask : public Task {
|
|||||||
protected:
|
protected:
|
||||||
QList<Mod*>& m_mods;
|
QList<Mod*>& m_mods;
|
||||||
std::list<Version>& m_game_versions;
|
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::shared_ptr<ModFolderModel> m_mods_folder;
|
||||||
|
|
||||||
std::vector<UpdatableMod> m_updatable;
|
std::vector<UpdatableMod> m_updatable;
|
||||||
|
@ -83,4 +83,25 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
|||||||
projectID.toString();
|
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
|
} // namespace ModPlatform
|
||||||
|
@ -30,6 +30,9 @@ class QIODevice;
|
|||||||
|
|
||||||
namespace ModPlatform {
|
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 ResourceProvider { MODRINTH, FLAME };
|
||||||
|
|
||||||
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
||||||
@ -70,7 +73,7 @@ struct IndexedVersion {
|
|||||||
QString downloadUrl;
|
QString downloadUrl;
|
||||||
QString date;
|
QString date;
|
||||||
QString fileName;
|
QString fileName;
|
||||||
QStringList loaders = {};
|
ModLoaderTypes loaders = {};
|
||||||
QString hash_type;
|
QString hash_type;
|
||||||
QString hash;
|
QString hash;
|
||||||
bool is_preferred = true;
|
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; });
|
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
|
||||||
|
|
||||||
struct OverrideDep {
|
struct OverrideDep {
|
||||||
QString quilt;
|
QString quilt;
|
||||||
@ -148,6 +150,14 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
|||||||
|
|
||||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
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
|
} // namespace ModPlatform
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
||||||
|
@ -54,9 +54,6 @@ class ResourceAPI {
|
|||||||
public:
|
public:
|
||||||
virtual ~ResourceAPI() = default;
|
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 {
|
struct SortingMethod {
|
||||||
// The index of the sorting method. Used to allow for arbitrary ordering in the list of methods.
|
// The index of the sorting method. Used to allow for arbitrary ordering in the list of methods.
|
||||||
// Used by Flame in the API request.
|
// Used by Flame in the API request.
|
||||||
@ -74,7 +71,7 @@ class ResourceAPI {
|
|||||||
|
|
||||||
std::optional<QString> search;
|
std::optional<QString> search;
|
||||||
std::optional<SortingMethod> sorting;
|
std::optional<SortingMethod> sorting;
|
||||||
std::optional<ModLoaderTypes> loaders;
|
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||||
std::optional<std::list<Version> > versions;
|
std::optional<std::list<Version> > versions;
|
||||||
};
|
};
|
||||||
struct SearchCallbacks {
|
struct SearchCallbacks {
|
||||||
@ -87,7 +84,7 @@ class ResourceAPI {
|
|||||||
ModPlatform::IndexedPack pack;
|
ModPlatform::IndexedPack pack;
|
||||||
|
|
||||||
std::optional<std::list<Version> > mcVersions;
|
std::optional<std::list<Version> > mcVersions;
|
||||||
std::optional<ModLoaderTypes> loaders;
|
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||||
|
|
||||||
VersionSearchArgs(VersionSearchArgs const&) = default;
|
VersionSearchArgs(VersionSearchArgs const&) = default;
|
||||||
void operator=(VersionSearchArgs other)
|
void operator=(VersionSearchArgs other)
|
||||||
@ -114,7 +111,7 @@ class ResourceAPI {
|
|||||||
struct DependencySearchArgs {
|
struct DependencySearchArgs {
|
||||||
ModPlatform::Dependency dependency;
|
ModPlatform::Dependency dependency;
|
||||||
Version mcVersion;
|
Version mcVersion;
|
||||||
ModLoaderTypes loader;
|
ModPlatform::ModLoaderTypes loader;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DependencySearchCallbacks {
|
struct DependencySearchCallbacks {
|
||||||
@ -161,25 +158,6 @@ class ResourceAPI {
|
|||||||
return nullptr;
|
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:
|
protected:
|
||||||
[[nodiscard]] inline QString debugName() const { return "External resource API"; }
|
[[nodiscard]] inline QString debugName() const { return "External resource API"; }
|
||||||
|
|
||||||
|
@ -1005,15 +1005,30 @@ static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString&
|
|||||||
if (!vlist)
|
if (!vlist)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!vlist->isLoaded())
|
if (!vlist->isLoaded()) {
|
||||||
vlist->load(Net::Mode::Online);
|
QEventLoop loadVersionLoop;
|
||||||
|
auto task = vlist->getLoadTask();
|
||||||
|
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||||
|
if (!task->isRunning())
|
||||||
|
task->start();
|
||||||
|
|
||||||
|
loadVersionLoop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
auto ver = vlist->getVersion(version);
|
auto ver = vlist->getVersion(version);
|
||||||
if (!ver)
|
if (!ver)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!ver->isLoaded())
|
if (!ver->isLoaded()) {
|
||||||
|
QEventLoop loadVersionLoop;
|
||||||
ver->load(Net::Mode::Online);
|
ver->load(Net::Mode::Online);
|
||||||
|
auto task = ver->getCurrentTask();
|
||||||
|
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||||
|
if (!task->isRunning())
|
||||||
|
task->start();
|
||||||
|
|
||||||
|
loadVersionLoop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
return ver;
|
return ver;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "FileResolvingTask.h"
|
#include "FileResolvingTask.h"
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
#include "net/ApiUpload.h"
|
#include "net/ApiUpload.h"
|
||||||
#include "net/Upload.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 url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
|
||||||
auto output = std::make_shared<QByteArray>();
|
auto output = std::make_shared<QByteArray>();
|
||||||
auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output);
|
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);
|
m_checkJob->addNetAction(dl);
|
||||||
blockedProjects.insert(&out, output);
|
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
|
// 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
|
// which file is relative to each loader, so it's best to not use any one and
|
||||||
// let the user download it manually.
|
// let the user download it manually.
|
||||||
if (file.loaders.size() <= 1) {
|
if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) {
|
||||||
out->url = file.downloadUrl;
|
out->url = file.downloadUrl;
|
||||||
qDebug() << "Found alternative on modrinth " << out->fileName;
|
qDebug() << "Found alternative on modrinth " << out->fileName;
|
||||||
} else {
|
} else {
|
||||||
@ -175,7 +176,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
|
|||||||
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
|
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
|
||||||
auto dl = Net::ApiDownload::makeByteArray(url, output);
|
auto dl = Net::ApiDownload::makeByteArray(url, output);
|
||||||
qDebug() << "Fetching url slug for file:" << mod->fileName;
|
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 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 json = QJsonDocument::fromJson(*output);
|
||||||
auto base =
|
auto base =
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include "FlameModIndex.h"
|
#include "FlameModIndex.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
#include "net/ApiUpload.h"
|
#include "net/ApiUpload.h"
|
||||||
@ -131,19 +130,13 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
|
|||||||
auto obj = Json::requireObject(doc);
|
auto obj = Json::requireObject(doc);
|
||||||
auto arr = Json::requireArray(obj, "data");
|
auto arr = Json::requireArray(obj, "data");
|
||||||
|
|
||||||
QJsonObject latest_file_obj;
|
|
||||||
ModPlatform::IndexedVersion ver_tmp;
|
|
||||||
|
|
||||||
for (auto file : arr) {
|
for (auto file : arr) {
|
||||||
auto file_obj = Json::requireObject(file);
|
auto file_obj = Json::requireObject(file);
|
||||||
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
|
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
|
||||||
if (file_tmp.date > ver_tmp.date) {
|
if (file_tmp.date > ver.date && (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders))
|
||||||
ver_tmp = file_tmp;
|
ver = file_tmp;
|
||||||
latest_file_obj = file_obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ver = FlameMod::loadIndexedPackVersion(latest_file_obj);
|
|
||||||
} catch (Json::JsonException& e) {
|
} catch (Json::JsonException& e) {
|
||||||
qCritical() << "Failed to parse response from a version request.";
|
qCritical() << "Failed to parse response from a version request.";
|
||||||
qCritical() << e.what();
|
qCritical() << e.what();
|
||||||
@ -204,6 +197,17 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteAr
|
|||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const
|
||||||
|
{
|
||||||
|
auto netJob = makeShared<NetJob>(QString("Flame::GetFile"), APPLICATION->network());
|
||||||
|
netJob->addNetAction(
|
||||||
|
Net::ApiDownload::makeByteArray(QUrl(QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(addonId, fileId)), response));
|
||||||
|
|
||||||
|
QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; });
|
||||||
|
|
||||||
|
return netJob;
|
||||||
|
}
|
||||||
|
|
||||||
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
||||||
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
|
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
|
||||||
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
||||||
|
@ -20,10 +20,14 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
||||||
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
||||||
|
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[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:
|
private:
|
||||||
static int getClassId(ModPlatform::ResourceType type)
|
static int getClassId(ModPlatform::ResourceType type)
|
||||||
@ -37,19 +41,42 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getMappedModLoader(ModLoaderTypes loaders)
|
static int getMappedModLoader(ModPlatform::ModLoaderType loaders)
|
||||||
{
|
{
|
||||||
// https://docs.curseforge.com/?http#tocS_ModLoaderType
|
// https://docs.curseforge.com/?http#tocS_ModLoaderType
|
||||||
if (loaders & Forge)
|
switch (loaders) {
|
||||||
|
case ModPlatform::Forge:
|
||||||
return 1;
|
return 1;
|
||||||
if (loaders & Fabric)
|
case ModPlatform::Cauldron:
|
||||||
|
return 2;
|
||||||
|
case ModPlatform::LiteLoader:
|
||||||
|
return 3;
|
||||||
|
case ModPlatform::Fabric:
|
||||||
return 4;
|
return 4;
|
||||||
// TODO: remove this once Quilt drops official Fabric support
|
case ModPlatform::Quilt:
|
||||||
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
|
return 5;
|
||||||
return 4; // Quilt would probably be 5
|
case ModPlatform::NeoForge:
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
return 0;
|
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:
|
private:
|
||||||
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
@ -66,7 +93,7 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
||||||
get_arguments.append("sortOrder=desc");
|
get_arguments.append("sortOrder=desc");
|
||||||
if (args.loaders.has_value())
|
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);
|
get_arguments.append(gameVersionStr);
|
||||||
|
|
||||||
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
||||||
@ -80,47 +107,27 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
auto addonId = args.pack.addonId.toString();
|
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())
|
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()) {
|
if (args.loaders.has_value() && ModPlatform::hasSingleModLoaderSelected(args.loaders.value())) {
|
||||||
int mappedModLoader = getMappedModLoader(args.loaders.value());
|
int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loaders.value())));
|
||||||
|
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
return url;
|
||||||
|
|
||||||
get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader));
|
|
||||||
}
|
|
||||||
|
|
||||||
return url + get_parameters.join('&');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
auto mappedModLoader = getMappedModLoader(args.loader);
|
|
||||||
auto addonId = args.dependency.addonId.toString();
|
auto addonId = args.dependency.addonId.toString();
|
||||||
if (args.loader & Quilt) {
|
auto url =
|
||||||
auto overide = ModPlatform::getOverrideDeps();
|
QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString());
|
||||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
|
if (args.loader && ModPlatform::hasSingleModLoaderSelected(args.loader)) {
|
||||||
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
|
int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loader)));
|
||||||
});
|
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||||
if (over != overide.cend()) {
|
|
||||||
mappedModLoader = 5;
|
|
||||||
}
|
}
|
||||||
}
|
return url;
|
||||||
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3")
|
|
||||||
.arg(addonId)
|
|
||||||
.arg(args.mcVersion.toString())
|
|
||||||
.arg(mappedModLoader);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -5,13 +5,11 @@
|
|||||||
#include <MurmurHash2.h>
|
#include <MurmurHash2.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
#include "ResourceDownloadTask.h"
|
#include "ResourceDownloadTask.h"
|
||||||
|
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
#include "minecraft/mod/ResourceFolderModel.h"
|
|
||||||
|
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class FlameCheckUpdate : public CheckUpdateTask {
|
|||||||
public:
|
public:
|
||||||
FlameCheckUpdate(QList<Mod*>& mods,
|
FlameCheckUpdate(QList<Mod*>& mods,
|
||||||
std::list<Version>& mcVersions,
|
std::list<Version>& mcVersions,
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<ModFolderModel> mods_folder)
|
std::shared_ptr<ModFolderModel> mods_folder)
|
||||||
: CheckUpdateTask(mods, mcVersions, loaders, 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.
|
// 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
|
// not all mod loaders depend on a given Minecraft version, so we won't do this
|
||||||
// filtering for those loaders.
|
// 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) {
|
auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) {
|
||||||
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
|
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
|
||||||
});
|
});
|
||||||
@ -350,7 +350,11 @@ bool FlameCreationTask::createInstance()
|
|||||||
|
|
||||||
for (auto& loader : m_pack.minecraft.modLoaders) {
|
for (auto& loader : m_pack.minecraft.modLoaders) {
|
||||||
auto id = loader.id;
|
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-");
|
id.remove("forge-");
|
||||||
loaderType = "forge";
|
loaderType = "forge";
|
||||||
loaderUid = "net.minecraftforge";
|
loaderUid = "net.minecraftforge";
|
||||||
|
@ -81,6 +81,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||||
|
auto loaders = profile->getSupportedModLoaders();
|
||||||
|
|
||||||
for (auto versionIter : arr) {
|
for (auto versionIter : arr) {
|
||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
@ -89,7 +90,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
if (!file.addonId.isValid())
|
if (!file.addonId.isValid())
|
||||||
file.addonId = pack.addonId;
|
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);
|
unsortedVersions.append(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +117,19 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||||||
|
|
||||||
if (str.contains('.'))
|
if (str.contains('.'))
|
||||||
file.mcVersion.append(str);
|
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");
|
file.addonId = Json::requireInteger(obj, "modId");
|
||||||
@ -173,8 +188,11 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||||||
return file;
|
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;
|
QVector<ModPlatform::IndexedVersion> versions;
|
||||||
for (auto versionIter : arr) {
|
for (auto versionIter : arr) {
|
||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
@ -183,7 +201,8 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
|
|||||||
if (!file.addonId.isValid())
|
if (!file.addonId.isValid())
|
||||||
file.addonId = m.addonId;
|
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);
|
versions.append(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,5 +211,7 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
|
|||||||
return a.date > b.date;
|
return a.date > b.date;
|
||||||
};
|
};
|
||||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||||
|
if (versions.size() != 0)
|
||||||
return versions.front();
|
return versions.front();
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
||||||
const BaseInstance* inst);
|
const BaseInstance* inst);
|
||||||
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
|
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
|
} // namespace FlameMod
|
@ -43,12 +43,14 @@ const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" });
|
|||||||
FlamePackExportTask::FlamePackExportTask(const QString& name,
|
FlamePackExportTask::FlamePackExportTask(const QString& name,
|
||||||
const QString& version,
|
const QString& version,
|
||||||
const QString& author,
|
const QString& author,
|
||||||
|
bool optionalFiles,
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
const QString& output,
|
const QString& output,
|
||||||
MMCZip::FilterFunction filter)
|
MMCZip::FilterFunction filter)
|
||||||
: name(name)
|
: name(name)
|
||||||
, version(version)
|
, version(version)
|
||||||
, author(author)
|
, author(author)
|
||||||
|
, optionalFiles(optionalFiles)
|
||||||
, instance(instance)
|
, instance(instance)
|
||||||
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
||||||
, gameRoot(instance->gameRoot())
|
, gameRoot(instance->gameRoot())
|
||||||
@ -381,6 +383,7 @@ QByteArray FlamePackExportTask::generateIndex()
|
|||||||
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
||||||
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
||||||
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
||||||
|
const ComponentPtr neoforge = profile->getComponent("net.neoforged");
|
||||||
|
|
||||||
// convert all available components to mrpack dependencies
|
// convert all available components to mrpack dependencies
|
||||||
if (minecraft != nullptr)
|
if (minecraft != nullptr)
|
||||||
@ -392,6 +395,8 @@ QByteArray FlamePackExportTask::generateIndex()
|
|||||||
id = "fabric-" + fabric->getVersion();
|
id = "fabric-" + fabric->getVersion();
|
||||||
else if (forge != nullptr)
|
else if (forge != nullptr)
|
||||||
id = "forge-" + forge->getVersion();
|
id = "forge-" + forge->getVersion();
|
||||||
|
else if (neoforge != nullptr)
|
||||||
|
id = "neoforge-" + neoforge->getVersion();
|
||||||
version["modLoaders"] = QJsonArray();
|
version["modLoaders"] = QJsonArray();
|
||||||
if (!id.isEmpty()) {
|
if (!id.isEmpty()) {
|
||||||
QJsonObject loader;
|
QJsonObject loader;
|
||||||
@ -407,7 +412,7 @@ QByteArray FlamePackExportTask::generateIndex()
|
|||||||
QJsonObject file;
|
QJsonObject file;
|
||||||
file["projectID"] = mod.addonId;
|
file["projectID"] = mod.addonId;
|
||||||
file["fileID"] = mod.version;
|
file["fileID"] = mod.version;
|
||||||
file["required"] = mod.enabled;
|
file["required"] = mod.enabled || !optionalFiles;
|
||||||
files << file;
|
files << file;
|
||||||
}
|
}
|
||||||
obj["files"] = files;
|
obj["files"] = files;
|
||||||
|
@ -30,6 +30,7 @@ class FlamePackExportTask : public Task {
|
|||||||
FlamePackExportTask(const QString& name,
|
FlamePackExportTask(const QString& name,
|
||||||
const QString& version,
|
const QString& version,
|
||||||
const QString& author,
|
const QString& author,
|
||||||
|
bool optionalFiles,
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
const QString& output,
|
const QString& output,
|
||||||
MMCZip::FilterFunction filter);
|
MMCZip::FilterFunction filter);
|
||||||
@ -44,6 +45,7 @@ class FlamePackExportTask : public Task {
|
|||||||
|
|
||||||
// inputs
|
// inputs
|
||||||
const QString name, version, author;
|
const QString name, version, author;
|
||||||
|
const bool optionalFiles;
|
||||||
const InstancePtr instance;
|
const InstancePtr instance;
|
||||||
MinecraftInstance* mcInstance;
|
MinecraftInstance* mcInstance;
|
||||||
const QDir gameRoot;
|
const QDir gameRoot;
|
||||||
|
@ -131,7 +131,7 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
|
|||||||
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
||||||
auto response = std::make_shared<QByteArray>();
|
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, [=] {
|
QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
|
@ -59,16 +59,20 @@ Modpack parseDirectory(QString path)
|
|||||||
auto obj = Json::requireObject(target, "target");
|
auto obj = Json::requireObject(target, "target");
|
||||||
auto name = Json::requireString(obj, "name", "name");
|
auto name = Json::requireString(obj, "name", "name");
|
||||||
auto version = Json::requireString(obj, "version", "version");
|
auto version = Json::requireString(obj, "version", "version");
|
||||||
if (name == "forge") {
|
if (name == "neoforge") {
|
||||||
modpack.loaderType = ResourceAPI::Forge;
|
modpack.loaderType = ModPlatform::NeoForge;
|
||||||
|
modpack.version = version;
|
||||||
|
break;
|
||||||
|
} else if (name == "forge") {
|
||||||
|
modpack.loaderType = ModPlatform::Forge;
|
||||||
modpack.version = version;
|
modpack.version = version;
|
||||||
break;
|
break;
|
||||||
} else if (name == "fabric") {
|
} else if (name == "fabric") {
|
||||||
modpack.loaderType = ResourceAPI::Fabric;
|
modpack.loaderType = ModPlatform::Fabric;
|
||||||
modpack.version = version;
|
modpack.version = version;
|
||||||
break;
|
break;
|
||||||
} else if (name == "quilt") {
|
} else if (name == "quilt") {
|
||||||
modpack.loaderType = ResourceAPI::Quilt;
|
modpack.loaderType = ModPlatform::Quilt;
|
||||||
modpack.version = version;
|
modpack.version = version;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ struct Modpack {
|
|||||||
// not needed for instance creation
|
// not needed for instance creation
|
||||||
QVariant jvmArgs;
|
QVariant jvmArgs;
|
||||||
|
|
||||||
std::optional<ResourceAPI::ModLoaderType> loaderType;
|
std::optional<ModPlatform::ModLoaderType> loaderType;
|
||||||
QString loaderVersion;
|
QString loaderVersion;
|
||||||
|
|
||||||
QIcon icon;
|
QIcon icon;
|
||||||
|
@ -68,21 +68,25 @@ void PackInstallTask::copySettings()
|
|||||||
auto modloader = m_pack.loaderType;
|
auto modloader = m_pack.loaderType;
|
||||||
if (modloader.has_value())
|
if (modloader.has_value())
|
||||||
switch (modloader.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);
|
components->setComponentVersion("net.minecraftforge", m_pack.version, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ResourceAPI::Fabric: {
|
case ModPlatform::Fabric: {
|
||||||
components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
|
components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ResourceAPI::Quilt: {
|
case ModPlatform::Quilt: {
|
||||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true);
|
components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ResourceAPI::Cauldron:
|
case ModPlatform::Cauldron:
|
||||||
break;
|
break;
|
||||||
case ResourceAPI::LiteLoader:
|
case ModPlatform::LiteLoader:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
components->saveNow();
|
components->saveNow();
|
||||||
|
@ -42,7 +42,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
|
|||||||
Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<QByteArray> response)
|
std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
|
||||||
@ -72,7 +72,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
|
|||||||
Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
|
||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<QByteArray> response)
|
std::shared_ptr<QByteArray> response)
|
||||||
{
|
{
|
||||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
|
||||||
|
@ -19,13 +19,13 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
auto latestVersion(QString hash,
|
auto latestVersion(QString hash,
|
||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||||
|
|
||||||
auto latestVersions(const QStringList& hashes,
|
auto latestVersions(const QStringList& hashes,
|
||||||
QString hash_format,
|
QString hash_format,
|
||||||
std::optional<std::list<Version>> mcVersions,
|
std::optional<std::list<Version>> mcVersions,
|
||||||
std::optional<ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
std::shared_ptr<QByteArray> response) -> Task::Ptr;
|
||||||
|
|
||||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
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; };
|
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;
|
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) {
|
if (types & loader) {
|
||||||
l << getModLoaderString(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;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto getModLoaderFilters(ModLoaderTypes types) -> const QString
|
static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString
|
||||||
{
|
{
|
||||||
QStringList l;
|
QStringList l;
|
||||||
for (auto loader : getModLoaderStrings(types)) {
|
for (auto loader : getModLoaderStrings(types)) {
|
||||||
@ -141,7 +140,10 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
return s.isEmpty() ? QString() : s;
|
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
|
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include "tasks/ConcurrentTask.h"
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
#include "minecraft/mod/ResourceFolderModel.h"
|
|
||||||
|
|
||||||
static ModrinthAPI api;
|
static ModrinthAPI api;
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
@ -111,11 +110,11 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
// so we may want to filter it
|
// so we may want to filter it
|
||||||
QString loader_filter;
|
QString loader_filter;
|
||||||
if (m_loaders.has_value()) {
|
if (m_loaders.has_value()) {
|
||||||
static auto flags = { ResourceAPI::ModLoaderType::Forge, ResourceAPI::ModLoaderType::Fabric,
|
static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge,
|
||||||
ResourceAPI::ModLoaderType::Quilt };
|
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
|
||||||
for (auto flag : flags) {
|
for (auto flag : flags) {
|
||||||
if (m_loaders.value().testFlag(flag)) {
|
if (m_loaders.value().testFlag(flag)) {
|
||||||
loader_filter = api.getModLoaderString(flag);
|
loader_filter = ModPlatform::getModLoaderString(flag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
|
|||||||
public:
|
public:
|
||||||
ModrinthCheckUpdate(QList<Mod*>& mods,
|
ModrinthCheckUpdate(QList<Mod*>& mods,
|
||||||
std::list<Version>& mcVersions,
|
std::list<Version>& mcVersions,
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||||
std::shared_ptr<ModFolderModel> mods_folder)
|
std::shared_ptr<ModFolderModel> mods_folder)
|
||||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||||
{}
|
{}
|
||||||
|
@ -211,6 +211,8 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
|
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
|
||||||
if (!m_forge_version.isEmpty())
|
if (!m_forge_version.isEmpty())
|
||||||
components->setComponentVersion("net.minecraftforge", m_forge_version);
|
components->setComponentVersion("net.minecraftforge", m_forge_version);
|
||||||
|
if (!m_neoForge_version.isEmpty())
|
||||||
|
components->setComponentVersion("net.neoforged", m_neoForge_version);
|
||||||
|
|
||||||
if (m_instIcon != "default") {
|
if (m_instIcon != "default") {
|
||||||
instance.setIconKey(m_instIcon);
|
instance.setIconKey(m_instIcon);
|
||||||
@ -398,6 +400,8 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
|||||||
m_quilt_version = Json::requireString(*it, "Quilt Loader version");
|
m_quilt_version = Json::requireString(*it, "Quilt Loader version");
|
||||||
} else if (name == "forge") {
|
} else if (name == "forge") {
|
||||||
m_forge_version = Json::requireString(*it, "Forge version");
|
m_forge_version = Json::requireString(*it, "Forge version");
|
||||||
|
} else if (name == "neoforge") {
|
||||||
|
m_neoForge_version = Json::requireString(*it, "NeoForge version");
|
||||||
} else {
|
} else {
|
||||||
throw JSONValidationError("Unknown dependency type: " + name);
|
throw JSONValidationError("Unknown dependency type: " + name);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
|||||||
private:
|
private:
|
||||||
QWidget* m_parent = nullptr;
|
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;
|
QString m_managed_id, m_managed_version_id, m_managed_name;
|
||||||
|
|
||||||
std::vector<Modrinth::File> m_files;
|
std::vector<Modrinth::File> m_files;
|
||||||
|
@ -33,12 +33,14 @@ const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "z
|
|||||||
ModrinthPackExportTask::ModrinthPackExportTask(const QString& name,
|
ModrinthPackExportTask::ModrinthPackExportTask(const QString& name,
|
||||||
const QString& version,
|
const QString& version,
|
||||||
const QString& summary,
|
const QString& summary,
|
||||||
|
bool optionalFiles,
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
const QString& output,
|
const QString& output,
|
||||||
MMCZip::FilterFunction filter)
|
MMCZip::FilterFunction filter)
|
||||||
: name(name)
|
: name(name)
|
||||||
, version(version)
|
, version(version)
|
||||||
, summary(summary)
|
, summary(summary)
|
||||||
|
, optionalFiles(optionalFiles)
|
||||||
, instance(instance)
|
, instance(instance)
|
||||||
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
||||||
, gameRoot(instance->gameRoot())
|
, gameRoot(instance->gameRoot())
|
||||||
@ -245,6 +247,7 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
||||||
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
||||||
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
||||||
|
const ComponentPtr neoForge = profile->getComponent("net.neoforged");
|
||||||
|
|
||||||
// convert all available components to mrpack dependencies
|
// convert all available components to mrpack dependencies
|
||||||
QJsonObject dependencies;
|
QJsonObject dependencies;
|
||||||
@ -256,6 +259,8 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
dependencies["fabric-loader"] = fabric->m_version;
|
dependencies["fabric-loader"] = fabric->m_version;
|
||||||
if (forge != nullptr)
|
if (forge != nullptr)
|
||||||
dependencies["forge"] = forge->m_version;
|
dependencies["forge"] = forge->m_version;
|
||||||
|
if (neoForge != nullptr)
|
||||||
|
dependencies["neoforge"] = neoForge->m_version;
|
||||||
|
|
||||||
out["dependencies"] = dependencies;
|
out["dependencies"] = dependencies;
|
||||||
}
|
}
|
||||||
@ -267,6 +272,7 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
QString path = iterator.key();
|
QString path = iterator.key();
|
||||||
const ResolvedFile& value = iterator.value();
|
const ResolvedFile& value = iterator.value();
|
||||||
|
|
||||||
|
if (optionalFiles) {
|
||||||
// detect disabled mod
|
// detect disabled mod
|
||||||
const QFileInfo pathInfo(path);
|
const QFileInfo pathInfo(path);
|
||||||
if (pathInfo.suffix() == "disabled") {
|
if (pathInfo.suffix() == "disabled") {
|
||||||
@ -278,6 +284,7 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
env["server"] = "optional";
|
env["server"] = "optional";
|
||||||
fileOut["env"] = env;
|
fileOut["env"] = env;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileOut["path"] = path;
|
fileOut["path"] = path;
|
||||||
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
||||||
|
@ -31,6 +31,7 @@ class ModrinthPackExportTask : public Task {
|
|||||||
ModrinthPackExportTask(const QString& name,
|
ModrinthPackExportTask(const QString& name,
|
||||||
const QString& version,
|
const QString& version,
|
||||||
const QString& summary,
|
const QString& summary,
|
||||||
|
bool optionalFiles,
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
const QString& output,
|
const QString& output,
|
||||||
MMCZip::FilterFunction filter);
|
MMCZip::FilterFunction filter);
|
||||||
@ -50,6 +51,7 @@ class ModrinthPackExportTask : public Task {
|
|||||||
|
|
||||||
// inputs
|
// inputs
|
||||||
const QString name, version, summary;
|
const QString name, version, summary;
|
||||||
|
const bool optionalFiles;
|
||||||
const InstancePtr instance;
|
const InstancePtr instance;
|
||||||
MinecraftInstance* mcInstance;
|
MinecraftInstance* mcInstance;
|
||||||
const QDir gameRoot;
|
const QDir gameRoot;
|
||||||
|
@ -93,19 +93,19 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
|||||||
pack.extraDataLoaded = true;
|
pack.extraDataLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst)
|
||||||
QJsonArray& arr,
|
|
||||||
[[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network,
|
|
||||||
const BaseInstance* inst)
|
|
||||||
{
|
{
|
||||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
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) {
|
for (auto versionIter : arr) {
|
||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
auto file = loadIndexedPackVersion(obj);
|
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);
|
unsortedVersions.append(file);
|
||||||
}
|
}
|
||||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
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");
|
auto loaders = Json::requireArray(obj, "loaders");
|
||||||
for (auto loader : 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 = Json::requireString(obj, "name");
|
||||||
file.version_number = Json::requireString(obj, "version_number");
|
file.version_number = Json::requireString(obj, "version_number");
|
||||||
@ -218,15 +229,20 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
return {};
|
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) {
|
for (auto versionIter : arr) {
|
||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
auto file = loadIndexedPackVersion(obj);
|
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);
|
versions.append(file);
|
||||||
}
|
}
|
||||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
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 loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||||
void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst);
|
||||||
QJsonArray& arr,
|
|
||||||
const shared_qobject_ptr<QNetworkAccessManager>& network,
|
|
||||||
const BaseInstance* inst);
|
|
||||||
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
|
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
|
} // namespace Modrinth
|
||||||
|
@ -218,9 +218,24 @@ void HttpMetaCache::Load()
|
|||||||
if (!index.open(QIODevice::ReadOnly))
|
if (!index.open(QIODevice::ReadOnly))
|
||||||
return;
|
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
|
// check file version first
|
||||||
auto version_val = Json::ensureString(root, "version");
|
auto version_val = Json::ensureString(root, "version");
|
||||||
|
@ -350,6 +350,7 @@
|
|||||||
|
|
||||||
<file>scalable/instances/quiltmc.svg</file> <!-- CC0 QuiltMC -->
|
<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/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/forge.png</file> <!-- LGPL3 Forge Development LLC -->
|
||||||
<file>128x128/instances/liteloader.png</file> <!-- CC-BY-SA 4.0 LiteLoader -->
|
<file>128x128/instances/liteloader.png</file> <!-- CC-BY-SA 4.0 LiteLoader -->
|
||||||
</qresource>
|
</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 |
@ -44,8 +44,6 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
|
||||||
#include "ui/widgets/PageContainer.h"
|
#include "ui/widgets/PageContainer.h"
|
||||||
|
|
||||||
#include "InstancePageProvider.h"
|
#include "InstancePageProvider.h"
|
||||||
@ -76,40 +74,44 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget* parent) : QMainWin
|
|||||||
|
|
||||||
// Add custom buttons to the page container layout.
|
// Add custom buttons to the page container layout.
|
||||||
{
|
{
|
||||||
auto horizontalLayout = new QHBoxLayout();
|
auto horizontalLayout = new QHBoxLayout(this);
|
||||||
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
||||||
horizontalLayout->setContentsMargins(6, -1, 6, -1);
|
horizontalLayout->setContentsMargins(6, -1, 6, -1);
|
||||||
|
|
||||||
auto btnHelp = new QPushButton();
|
auto btnHelp = new QPushButton(this);
|
||||||
btnHelp->setText(tr("Help"));
|
btnHelp->setText(tr("Help"));
|
||||||
horizontalLayout->addWidget(btnHelp);
|
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);
|
auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
horizontalLayout->addSpacerItem(spacer);
|
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);
|
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();
|
updateButtons();
|
||||||
horizontalLayout->addWidget(m_launchOfflineButton);
|
|
||||||
m_launchOfflineButton->setText(tr("Launch Offline"));
|
|
||||||
|
|
||||||
m_launchDemoButton = new QPushButton();
|
m_closeButton = new QPushButton(this);
|
||||||
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->setText(tr("Close"));
|
m_closeButton->setText(tr("Close"));
|
||||||
horizontalLayout->addWidget(m_closeButton);
|
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);
|
m_container->addButtons(horizontalLayout);
|
||||||
|
|
||||||
|
connect(m_instance.get(), &BaseInstance::profilerChanged, this, &InstanceWindow::updateButtons);
|
||||||
|
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceWindow::updateButtons);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore window state
|
// 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_launchButton->setEnabled(m_instance->canLaunch());
|
||||||
m_launchOfflineButton->setEnabled(false);
|
m_killButton->setEnabled(m_instance->isRunning());
|
||||||
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);
|
|
||||||
|
|
||||||
// Disable demo-mode if not available.
|
QMenu* launchMenu = m_launchButton->menu();
|
||||||
auto instance = dynamic_cast<MinecraftInstance*>(m_instance.get());
|
if (launchMenu)
|
||||||
if (instance) {
|
launchMenu->clear();
|
||||||
m_launchDemoButton->setEnabled(instance->supportsDemo());
|
else
|
||||||
}
|
launchMenu = new QMenu(this);
|
||||||
|
m_instance->populateLaunchMenu(launchMenu);
|
||||||
m_killButton->setText(tr("Launch"));
|
m_launchButton->setMenu(launchMenu);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc)
|
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)
|
void InstanceWindow::runningStateChanged(bool running)
|
||||||
{
|
{
|
||||||
updateLaunchButtons();
|
updateButtons();
|
||||||
m_container->refreshContainer();
|
m_container->refreshContainer();
|
||||||
if (running) {
|
if (running) {
|
||||||
selectPage("log");
|
selectPage("log");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceWindow::on_closeButton_clicked()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceWindow::closeEvent(QCloseEvent* event)
|
void InstanceWindow::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
bool proceed = true;
|
bool proceed = true;
|
||||||
@ -233,15 +201,6 @@ bool InstanceWindow::saveAll()
|
|||||||
return m_container->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()
|
QString InstanceWindow::instanceId()
|
||||||
{
|
{
|
||||||
return m_instance->id();
|
return m_instance->id();
|
||||||
@ -252,17 +211,15 @@ bool InstanceWindow::selectPage(QString pageId)
|
|||||||
return m_container->selectPage(pageId);
|
return m_container->selectPage(pageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
BasePage* InstanceWindow::selectedPage() const
|
|
||||||
{
|
|
||||||
return m_container->selectedPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceWindow::refreshContainer()
|
void InstanceWindow::refreshContainer()
|
||||||
{
|
{
|
||||||
m_container->refreshContainer();
|
m_container->refreshContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceWindow::~InstanceWindow() {}
|
BasePage* InstanceWindow::selectedPage() const
|
||||||
|
{
|
||||||
|
return m_container->selectedPage();
|
||||||
|
}
|
||||||
|
|
||||||
bool InstanceWindow::requestClose()
|
bool InstanceWindow::requestClose()
|
||||||
{
|
{
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
#include "LaunchController.h"
|
#include "LaunchController.h"
|
||||||
#include "launch/LaunchTask.h"
|
#include "launch/LaunchTask.h"
|
||||||
@ -53,7 +54,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0);
|
explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0);
|
||||||
virtual ~InstanceWindow();
|
virtual ~InstanceWindow() = default;
|
||||||
|
|
||||||
bool selectPage(QString pageId) override;
|
bool selectPage(QString pageId) override;
|
||||||
BasePage* selectedPage() const override;
|
BasePage* selectedPage() const override;
|
||||||
@ -71,11 +72,6 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
|||||||
void isClosing();
|
void isClosing();
|
||||||
|
|
||||||
private slots:
|
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 instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc);
|
||||||
void runningStateChanged(bool running);
|
void runningStateChanged(bool running);
|
||||||
void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus);
|
void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus);
|
||||||
@ -84,7 +80,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
|||||||
void closeEvent(QCloseEvent*) override;
|
void closeEvent(QCloseEvent*) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateLaunchButtons();
|
void updateButtons();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_qobject_ptr<LaunchTask> m_proc;
|
shared_qobject_ptr<LaunchTask> m_proc;
|
||||||
@ -92,7 +88,6 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
|
|||||||
bool m_doNotSave = false;
|
bool m_doNotSave = false;
|
||||||
PageContainer* m_container = nullptr;
|
PageContainer* m_container = nullptr;
|
||||||
QPushButton* m_closeButton = nullptr;
|
QPushButton* m_closeButton = nullptr;
|
||||||
|
QToolButton* m_launchButton = nullptr;
|
||||||
QPushButton* m_killButton = nullptr;
|
QPushButton* m_killButton = nullptr;
|
||||||
QPushButton* m_launchOfflineButton = nullptr;
|
|
||||||
QPushButton* m_launchDemoButton = nullptr;
|
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "ui/dialogs/ExportToModListDialog.h"
|
|
||||||
#include "ui_MainWindow.h"
|
#include "ui_MainWindow.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -85,22 +84,19 @@
|
|||||||
#include <launch/LaunchTask.h>
|
#include <launch/LaunchTask.h>
|
||||||
#include <minecraft/MinecraftInstance.h>
|
#include <minecraft/MinecraftInstance.h>
|
||||||
#include <minecraft/auth/AccountList.h>
|
#include <minecraft/auth/AccountList.h>
|
||||||
#include <net/Download.h>
|
#include <net/ApiDownload.h>
|
||||||
#include <net/NetJob.h>
|
#include <net/NetJob.h>
|
||||||
#include <news/NewsChecker.h>
|
#include <news/NewsChecker.h>
|
||||||
#include <tools/BaseProfiler.h>
|
#include <tools/BaseProfiler.h>
|
||||||
#include <updater/ExternalUpdater.h>
|
#include <updater/ExternalUpdater.h>
|
||||||
#include "InstancePageProvider.h"
|
|
||||||
#include "InstanceWindow.h"
|
#include "InstanceWindow.h"
|
||||||
#include "JavaCommon.h"
|
|
||||||
#include "LaunchController.h"
|
|
||||||
|
|
||||||
#include "ui/dialogs/AboutDialog.h"
|
#include "ui/dialogs/AboutDialog.h"
|
||||||
#include "ui/dialogs/CopyInstanceDialog.h"
|
#include "ui/dialogs/CopyInstanceDialog.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/EditAccountDialog.h"
|
|
||||||
#include "ui/dialogs/ExportInstanceDialog.h"
|
#include "ui/dialogs/ExportInstanceDialog.h"
|
||||||
#include "ui/dialogs/ExportPackDialog.h"
|
#include "ui/dialogs/ExportPackDialog.h"
|
||||||
|
#include "ui/dialogs/ExportToModListDialog.h"
|
||||||
#include "ui/dialogs/IconPickerDialog.h"
|
#include "ui/dialogs/IconPickerDialog.h"
|
||||||
#include "ui/dialogs/ImportResourceDialog.h"
|
#include "ui/dialogs/ImportResourceDialog.h"
|
||||||
#include "ui/dialogs/NewInstanceDialog.h"
|
#include "ui/dialogs/NewInstanceDialog.h"
|
||||||
@ -113,15 +109,24 @@
|
|||||||
#include "ui/themes/ThemeManager.h"
|
#include "ui/themes/ThemeManager.h"
|
||||||
#include "ui/widgets/LabeledToolButton.h"
|
#include "ui/widgets/LabeledToolButton.h"
|
||||||
|
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "minecraft/VersionFile.h"
|
||||||
#include "minecraft/WorldList.h"
|
#include "minecraft/WorldList.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
#include "minecraft/mod/ResourcePackFolderModel.h"
|
||||||
#include "minecraft/mod/ShaderPackFolderModel.h"
|
#include "minecraft/mod/ShaderPackFolderModel.h"
|
||||||
|
#include "minecraft/mod/TexturePackFolderModel.h"
|
||||||
#include "minecraft/mod/tasks/LocalResourceParse.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 "KonamiCode.h"
|
||||||
|
|
||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
#include "InstanceImportTask.h"
|
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
#include "MMCTime.h"
|
#include "MMCTime.h"
|
||||||
|
|
||||||
@ -549,71 +554,15 @@ void MainWindow::updateMainToolBar()
|
|||||||
ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
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();
|
QMenu* launchMenu = ui->actionLaunchInstance->menu();
|
||||||
if (launchMenu) {
|
if (launchMenu)
|
||||||
launchMenu->clear();
|
launchMenu->clear();
|
||||||
} else {
|
else
|
||||||
launchMenu = new QMenu(this);
|
launchMenu = new QMenu(this);
|
||||||
}
|
if (m_selectedInstance)
|
||||||
QAction* normalLaunch = launchMenu->addAction(tr("Launch"));
|
m_selectedInstance->populateLaunchMenu(launchMenu);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui->actionLaunchInstance->setMenu(launchMenu);
|
ui->actionLaunchInstance->setMenu(launchMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,13 +873,13 @@ void MainWindow::finalizeInstance(InstancePtr inst)
|
|||||||
} else {
|
} else {
|
||||||
CustomMessageBox::selectable(this, tr("Error"),
|
CustomMessageBox::selectable(this, tr("Error"),
|
||||||
tr("The launcher cannot download Minecraft or update instances unless you have at least "
|
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)
|
QMessageBox::Warning)
|
||||||
->show();
|
->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addInstance(QString url)
|
void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info)
|
||||||
{
|
{
|
||||||
QString groupName;
|
QString groupName;
|
||||||
do {
|
do {
|
||||||
@ -950,7 +899,7 @@ void MainWindow::addInstance(QString url)
|
|||||||
groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString();
|
groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
NewInstanceDialog newInstDlg(groupName, url, this);
|
NewInstanceDialog newInstDlg(groupName, url, extra_info, this);
|
||||||
if (!newInstDlg.exec())
|
if (!newInstDlg.exec())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -977,18 +926,105 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||||||
if (url.scheme().isEmpty())
|
if (url.scheme().isEmpty())
|
||||||
url.setScheme("file");
|
url.setScheme("file");
|
||||||
|
|
||||||
if (!url.isLocalFile()) { // probably instance/modpack
|
ModPlatform::IndexedVersion version;
|
||||||
addInstance(url.toString());
|
QMap<QString, QString> extra_info;
|
||||||
break;
|
QUrl local_url;
|
||||||
|
if (!url.isLocalFile()) { // download the remote resource and identify
|
||||||
|
QUrl dl_url;
|
||||||
|
if (url.scheme() == "curseforge") {
|
||||||
|
// need to find the download link for the modpack / resource
|
||||||
|
// 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 localFileName = QDir::toNativeSeparators(url.toLocalFile());
|
auto addonId = query.allQueryItemValues("addonId")[0];
|
||||||
|
auto fileId = query.allQueryItemValues("fileId")[0];
|
||||||
|
|
||||||
|
extra_info.insert("pack_id", addonId);
|
||||||
|
extra_info.insert("pack_version_id", fileId);
|
||||||
|
|
||||||
|
auto array = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
|
auto api = FlameAPI();
|
||||||
|
auto job = api.getFile(addonId, fileId, array);
|
||||||
|
|
||||||
|
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, &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
|
||||||
|
version = FlameMod::loadIndexedPackVersion(data);
|
||||||
|
auto fileName = version.fileName;
|
||||||
|
|
||||||
|
// Have to use ensureString then use QUrl to get proper url encoding
|
||||||
|
dl_url = QUrl(version.downloadUrl);
|
||||||
|
if (!dl_url.isValid()) {
|
||||||
|
CustomMessageBox::selectable(
|
||||||
|
this, tr("Error"),
|
||||||
|
tr("The modpack, mod, or resource %1 is blocked for third-parties! Please download it manually.").arg(fileName),
|
||||||
|
QMessageBox::Critical)
|
||||||
|
->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo dl_file(dl_url.fileName());
|
||||||
|
});
|
||||||
|
|
||||||
|
{ // drop stack
|
||||||
|
ProgressDialog dlUrlDialod(this);
|
||||||
|
dlUrlDialod.setSkipButton(true, tr("Abort"));
|
||||||
|
dlUrlDialod.execWithTask(job.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dl_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dl_url.isValid()) {
|
||||||
|
continue; // no valid url to download this resource
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString path = dl_url.host() + '/' + dl_url.path();
|
||||||
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
|
entry->setStale(true);
|
||||||
|
auto dl_job = unique_qobject_ptr<NetJob>(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||||
|
dl_job->addNetAction(Net::ApiDownload::makeCached(dl_url, entry));
|
||||||
|
auto archivePath = entry->getFullPath();
|
||||||
|
|
||||||
|
bool dl_success = false;
|
||||||
|
connect(dl_job.get(), &Task::failed, this,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
|
connect(dl_job.get(), &Task::succeeded, this, [&dl_success] { dl_success = true; });
|
||||||
|
|
||||||
|
{ // drop stack
|
||||||
|
ProgressDialog dlUrlDialod(this);
|
||||||
|
dlUrlDialod.setSkipButton(true, tr("Abort"));
|
||||||
|
dlUrlDialod.execWithTask(dl_job.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dl_success) {
|
||||||
|
continue; // no local file to identify
|
||||||
|
}
|
||||||
|
local_url = QUrl::fromLocalFile(archivePath);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
local_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto localFileName = QDir::toNativeSeparators(local_url.toLocalFile());
|
||||||
QFileInfo localFileInfo(localFileName);
|
QFileInfo localFileInfo(localFileName);
|
||||||
|
|
||||||
auto type = ResourceUtils::identify(localFileInfo);
|
auto type = ResourceUtils::identify(localFileInfo);
|
||||||
|
|
||||||
if (ResourceUtils::ValidResourceTypes.count(type) == 0) { // probably instance/modpack
|
if (ResourceUtils::ValidResourceTypes.count(type) == 0) { // probably instance/modpack
|
||||||
addInstance(localFileName);
|
addInstance(localFileName, extra_info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1013,7 +1049,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||||||
qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName;
|
qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName;
|
||||||
break;
|
break;
|
||||||
case PackedResourceType::Mod:
|
case PackedResourceType::Mod:
|
||||||
minecraftInst->loaderModList()->installMod(localFileName);
|
minecraftInst->loaderModList()->installMod(localFileName, version);
|
||||||
break;
|
break;
|
||||||
case PackedResourceType::ShaderPack:
|
case PackedResourceType::ShaderPack:
|
||||||
minecraftInst->shaderPackList()->installResource(localFileName);
|
minecraftInst->shaderPackList()->installResource(localFileName);
|
||||||
@ -1193,7 +1229,7 @@ void MainWindow::globalSettingsClosed()
|
|||||||
proxymodel->invalidate();
|
proxymodel->invalidate();
|
||||||
proxymodel->sort(0);
|
proxymodel->sort(0);
|
||||||
updateMainToolBar();
|
updateMainToolBar();
|
||||||
updateToolsMenu();
|
updateLaunchButton();
|
||||||
updateThemeMenu();
|
updateThemeMenu();
|
||||||
updateStatusCenter();
|
updateStatusCenter();
|
||||||
// This needs to be done to prevent UI elements disappearing in the event the config is changed
|
// This needs to be done to prevent UI elements disappearing in the event the config is changed
|
||||||
@ -1323,10 +1359,11 @@ void MainWindow::on_actionDeleteInstance_triggered()
|
|||||||
|
|
||||||
if (APPLICATION->instances()->trashInstance(id)) {
|
if (APPLICATION->instances()->trashInstance(id)) {
|
||||||
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
APPLICATION->instances()->deleteInstance(id);
|
APPLICATION->instances()->deleteInstance(id);
|
||||||
|
}
|
||||||
|
APPLICATION->settings()->set("SelectedInstance", QString());
|
||||||
|
selectionBad();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionExportInstanceZip_triggered()
|
void MainWindow::on_actionExportInstanceZip_triggered()
|
||||||
@ -1428,20 +1465,6 @@ void MainWindow::activateInstance(InstancePtr instance)
|
|||||||
APPLICATION->launch(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()
|
void MainWindow::on_actionKillInstance_triggered()
|
||||||
{
|
{
|
||||||
if (m_selectedInstance && m_selectedInstance->isRunning()) {
|
if (m_selectedInstance && m_selectedInstance->isRunning()) {
|
||||||
@ -1615,6 +1638,7 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
|
|||||||
}
|
}
|
||||||
if (m_selectedInstance) {
|
if (m_selectedInstance) {
|
||||||
disconnect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
|
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();
|
QString id = current.data(InstanceList::InstanceIDRole).toString();
|
||||||
m_selectedInstance = APPLICATION->instances()->getInstanceById(id);
|
m_selectedInstance = APPLICATION->instances()->getInstanceById(id);
|
||||||
@ -1622,14 +1646,6 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
|
|||||||
ui->instanceToolBar->setEnabled(true);
|
ui->instanceToolBar->setEnabled(true);
|
||||||
setInstanceActionsEnabled(true);
|
setInstanceActionsEnabled(true);
|
||||||
ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch());
|
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->actionKillInstance->setEnabled(m_selectedInstance->isRunning());
|
||||||
ui->actionExportInstance->setEnabled(m_selectedInstance->canExport());
|
ui->actionExportInstance->setEnabled(m_selectedInstance->canExport());
|
||||||
@ -1638,18 +1654,13 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
|
|||||||
updateStatusCenter();
|
updateStatusCenter();
|
||||||
updateInstanceToolIcon(m_selectedInstance->iconKey());
|
updateInstanceToolIcon(m_selectedInstance->iconKey());
|
||||||
|
|
||||||
updateToolsMenu();
|
updateLaunchButton();
|
||||||
|
|
||||||
APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id());
|
APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id());
|
||||||
|
|
||||||
connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
|
connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
|
||||||
|
connect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance);
|
||||||
} else {
|
} 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());
|
APPLICATION->settings()->set("SelectedInstance", QString());
|
||||||
selectionBad();
|
selectionBad();
|
||||||
return;
|
return;
|
||||||
@ -1674,11 +1685,12 @@ void MainWindow::selectionBad()
|
|||||||
{
|
{
|
||||||
// start by reseting everything...
|
// start by reseting everything...
|
||||||
m_selectedInstance = nullptr;
|
m_selectedInstance = nullptr;
|
||||||
|
m_statusLeft->setText(tr("No instance selected"));
|
||||||
|
|
||||||
statusBar()->clearMessage();
|
statusBar()->clearMessage();
|
||||||
ui->instanceToolBar->setEnabled(false);
|
ui->instanceToolBar->setEnabled(false);
|
||||||
setInstanceActionsEnabled(false);
|
setInstanceActionsEnabled(false);
|
||||||
updateToolsMenu();
|
updateLaunchButton();
|
||||||
renameButton->setText(tr("Rename Instance"));
|
renameButton->setText(tr("Rename Instance"));
|
||||||
updateInstanceToolIcon("grass");
|
updateInstanceToolIcon("grass");
|
||||||
|
|
||||||
@ -1725,7 +1737,9 @@ void MainWindow::updateStatusCenter()
|
|||||||
|
|
||||||
int timePlayed = APPLICATION->instances()->getTotalPlayTime();
|
int timePlayed = APPLICATION->instances()->getTotalPlayTime();
|
||||||
if (timePlayed > 0) {
|
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)
|
// "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here)
|
||||||
@ -1741,7 +1755,7 @@ void MainWindow::setInstanceActionsEnabled(bool enabled)
|
|||||||
ui->actionCreateInstanceShortcut->setEnabled(enabled);
|
ui->actionCreateInstanceShortcut->setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::refreshCurrentInstance([[maybe_unused]] bool running)
|
void MainWindow::refreshCurrentInstance()
|
||||||
{
|
{
|
||||||
auto current = view->selectionModel()->currentIndex();
|
auto current = view->selectionModel()->currentIndex();
|
||||||
instanceChanged(current, current);
|
instanceChanged(current, current);
|
||||||
|
@ -144,10 +144,6 @@ class MainWindow : public QMainWindow {
|
|||||||
|
|
||||||
void on_actionLaunchInstance_triggered();
|
void on_actionLaunchInstance_triggered();
|
||||||
|
|
||||||
void on_actionLaunchInstanceOffline_triggered();
|
|
||||||
|
|
||||||
void on_actionLaunchInstanceDemo_triggered();
|
|
||||||
|
|
||||||
void on_actionKillInstance_triggered();
|
void on_actionKillInstance_triggered();
|
||||||
|
|
||||||
void on_actionDeleteInstance_triggered();
|
void on_actionDeleteInstance_triggered();
|
||||||
@ -155,10 +151,7 @@ class MainWindow : public QMainWindow {
|
|||||||
void deleteGroup();
|
void deleteGroup();
|
||||||
void undoTrashInstance();
|
void undoTrashInstance();
|
||||||
|
|
||||||
inline void on_actionExportInstance_triggered()
|
inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); }
|
||||||
{
|
|
||||||
on_actionExportInstanceZip_triggered();
|
|
||||||
}
|
|
||||||
void on_actionExportInstanceZip_triggered();
|
void on_actionExportInstanceZip_triggered();
|
||||||
void on_actionExportInstanceMrPack_triggered();
|
void on_actionExportInstanceMrPack_triggered();
|
||||||
void on_actionExportInstanceFlamePack_triggered();
|
void on_actionExportInstanceFlamePack_triggered();
|
||||||
@ -181,7 +174,7 @@ class MainWindow : public QMainWindow {
|
|||||||
|
|
||||||
void updateMainToolBar();
|
void updateMainToolBar();
|
||||||
|
|
||||||
void updateToolsMenu();
|
void updateLaunchButton();
|
||||||
|
|
||||||
void updateThemeMenu();
|
void updateThemeMenu();
|
||||||
|
|
||||||
@ -215,12 +208,12 @@ class MainWindow : public QMainWindow {
|
|||||||
void keyReleaseEvent(QKeyEvent* event) override;
|
void keyReleaseEvent(QKeyEvent* event) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void refreshCurrentInstance(bool running);
|
void refreshCurrentInstance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
void addInstance(QString url = QString());
|
void addInstance(const QString& url = QString(), const QMap<QString, QString>& extra_info = {});
|
||||||
void activateInstance(InstancePtr instance);
|
void activateInstance(InstancePtr instance);
|
||||||
void setCatBackground(bool enabled);
|
void setCatBackground(bool enabled);
|
||||||
void updateInstanceToolIcon(QString new_icon);
|
void updateInstanceToolIcon(QString new_icon);
|
||||||
|
@ -440,22 +440,6 @@
|
|||||||
<string>Ctrl+D</string>
|
<string>Ctrl+D</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</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">
|
<action name="actionExportInstance">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="export">
|
<iconset theme="export">
|
||||||
|
@ -37,15 +37,21 @@
|
|||||||
ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider)
|
ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider)
|
||||||
: QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(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->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) {
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||||
ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]);
|
setWindowTitle(tr("Export Modrinth Pack"));
|
||||||
setWindowTitle("Export Modrinth Pack");
|
ui->summary->setText(instance->settings()->get("ExportSummary").toString());
|
||||||
} else {
|
} else {
|
||||||
setWindowTitle("Export CurseForge Pack");
|
setWindowTitle(tr("Export CurseForge Pack"));
|
||||||
ui->version->setText("");
|
ui->summaryLabel->setText(tr("&Author"));
|
||||||
ui->summaryLabel->setText("Author");
|
ui->summary->setText(instance->settings()->get("ExportAuthor").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure a valid pack is generated
|
// ensure a valid pack is generated
|
||||||
@ -75,20 +81,19 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
|
|||||||
|
|
||||||
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
||||||
if (mcInstance) {
|
if (mcInstance) {
|
||||||
mcInstance->loaderModList()->update();
|
|
||||||
const QDir index = mcInstance->loaderModList()->indexDir();
|
const QDir index = mcInstance->loaderModList()->indexDir();
|
||||||
if (index.exists())
|
if (index.exists())
|
||||||
proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath()));
|
proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->treeView->setModel(proxy);
|
ui->files->setModel(proxy);
|
||||||
ui->treeView->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot())));
|
ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot())));
|
||||||
ui->treeView->sortByColumn(0, Qt::AscendingOrder);
|
ui->files->sortByColumn(0, Qt::AscendingOrder);
|
||||||
|
|
||||||
model->setFilter(filter);
|
model->setFilter(filter);
|
||||||
model->setRootPath(instance->gameRoot());
|
model->setRootPath(instance->gameRoot());
|
||||||
|
|
||||||
QHeaderView* headerView = ui->treeView->header();
|
QHeaderView* headerView = ui->files->header();
|
||||||
headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
|
headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
}
|
}
|
||||||
@ -100,26 +105,41 @@ ExportPackDialog::~ExportPackDialog()
|
|||||||
|
|
||||||
void ExportPackDialog::done(int result)
|
void ExportPackDialog::done(int result)
|
||||||
{
|
{
|
||||||
if (result == Accepted) {
|
auto settings = instance->settings();
|
||||||
const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text());
|
settings->set("ExportName", ui->name->text());
|
||||||
QString output;
|
settings->set("ExportVersion", ui->version->text());
|
||||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
settings->set(m_provider == ModPlatform::ResourceProvider::FLAME ? "ExportAuthor" : "ExportSummary", ui->summary->text());
|
||||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
|
settings->set("ExportOptionalFiles", ui->optionalFiles->isChecked());
|
||||||
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);
|
|
||||||
|
|
||||||
|
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())
|
if (output.isEmpty())
|
||||||
return;
|
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;
|
Task* task;
|
||||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||||
task = new ModrinthPackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
task = new ModrinthPackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance,
|
||||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
output, std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||||
else
|
} else {
|
||||||
task = new FlamePackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
task = new FlamePackExportTask(name, ui->version->text(), ui->summary->text(), ui->optionalFiles->isChecked(), instance, output,
|
||||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
connect(task, &Task::failed,
|
connect(task, &Task::failed,
|
||||||
[this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
[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()
|
void ExportPackDialog::validate()
|
||||||
{
|
{
|
||||||
const bool invalid =
|
ui->buttonBox->button(QDialogButtonBox::Ok)
|
||||||
ui->name->text().isEmpty() || ((m_provider == ModPlatform::ResourceProvider::MODRINTH) && ui->version->text().isEmpty());
|
->setDisabled(m_provider == ModPlatform::ResourceProvider::MODRINTH && ui->version->text().isEmpty());
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid);
|
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,9 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>650</width>
|
<width>650</width>
|
||||||
<height>413</height>
|
<height>510</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Export Pack</string>
|
|
||||||
</property>
|
|
||||||
<property name="sizeGripEnabled">
|
<property name="sizeGripEnabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -20,13 +17,16 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="information">
|
<widget class="QGroupBox" name="information">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Information</string>
|
<string>&Description</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="summaryLabel">
|
<widget class="QLabel" name="summaryLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Summary</string>
|
<string>&Summary</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>summary</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -36,14 +36,20 @@
|
|||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="nameLabel">
|
<widget class="QLabel" name="nameLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Name</string>
|
<string>&Name</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>name</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="versionLabel">
|
<widget class="QLabel" name="versionLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Version</string>
|
<string>&Version</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>version</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -57,19 +63,27 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="options">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="filesLabel">
|
<widget class="QLabel" name="filesLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Files</string>
|
<string>&Files</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>files</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="treeView">
|
<widget class="QTreeView" name="files">
|
||||||
<property name="alternatingRowColors">
|
<property name="alternatingRowColors">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -84,6 +98,19 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
@ -97,7 +124,8 @@
|
|||||||
<tabstop>name</tabstop>
|
<tabstop>name</tabstop>
|
||||||
<tabstop>version</tabstop>
|
<tabstop>version</tabstop>
|
||||||
<tabstop>summary</tabstop>
|
<tabstop>summary</tabstop>
|
||||||
<tabstop>treeView</tabstop>
|
<tabstop>files</tabstop>
|
||||||
|
<tabstop>optionalFiles</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
|
@ -129,7 +129,9 @@ InstallLoaderDialog::InstallLoaderDialog(std::shared_ptr<PackProfile> profile, c
|
|||||||
|
|
||||||
QList<BasePage*> InstallLoaderDialog::getPages()
|
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),
|
new InstallLoaderPage("net.minecraftforge", "forge", tr("Forge"), {}, profile),
|
||||||
// Fabric
|
// Fabric
|
||||||
new InstallLoaderPage("net.fabricmc.fabric-loader", "fabricmc", tr("Fabric"), Version("1.14"), profile),
|
new InstallLoaderPage("net.fabricmc.fabric-loader", "fabricmc", tr("Fabric"), Version("1.14"), profile),
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
#include "ScrollMessageBox.h"
|
#include "ScrollMessageBox.h"
|
||||||
#include "ui_ReviewMessageBox.h"
|
#include "ui_ReviewMessageBox.h"
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "Json.h"
|
|
||||||
#include "Markdown.h"
|
#include "Markdown.h"
|
||||||
|
|
||||||
#include "tasks/ConcurrentTask.h"
|
#include "tasks/ConcurrentTask.h"
|
||||||
@ -30,9 +28,9 @@ static std::list<Version> mcVersions(BaseInstance* inst)
|
|||||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
|
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,
|
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||||
|
@ -62,8 +62,10 @@
|
|||||||
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
||||||
#include "ui/pages/modplatform/technic/TechnicPage.h"
|
#include "ui/pages/modplatform/technic/TechnicPage.h"
|
||||||
#include "ui/widgets/PageContainer.h"
|
#include "ui/widgets/PageContainer.h"
|
||||||
|
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
||||||
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, QWidget* parent)
|
const QString& url,
|
||||||
|
const QMap<QString, QString>& extra_info,
|
||||||
|
QWidget* parent)
|
||||||
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
@ -125,6 +127,7 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString&
|
|||||||
QUrl actualUrl(url);
|
QUrl actualUrl(url);
|
||||||
m_container->selectPage("import");
|
m_container->selectPage("import");
|
||||||
importPage->setUrl(url);
|
importPage->setUrl(url);
|
||||||
|
importPage->setExtraInfo(extra_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDialogState();
|
updateDialogState();
|
||||||
|
@ -53,7 +53,10 @@ class NewInstanceDialog : public QDialog, public BasePageProvider {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NewInstanceDialog(const QString& initialGroup, const QString& url = QString(), QWidget* parent = 0);
|
explicit NewInstanceDialog(const QString& initialGroup,
|
||||||
|
const QString& url = QString(),
|
||||||
|
const QMap<QString, QString>& extra_info = {},
|
||||||
|
QWidget* parent = 0);
|
||||||
~NewInstanceDialog();
|
~NewInstanceDialog();
|
||||||
|
|
||||||
void updateDialogState();
|
void updateDialogState();
|
||||||
|
@ -279,7 +279,7 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
|||||||
{
|
{
|
||||||
QList<BasePage*> pages;
|
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))
|
if (ModrinthAPI::validateModLoaders(loaders))
|
||||||
pages.append(ModrinthModPage::create(this, *m_instance));
|
pages.append(ModrinthModPage::create(this, *m_instance));
|
||||||
|
@ -35,12 +35,6 @@
|
|||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>28</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Browse</string>
|
<string>Browse</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -64,7 +64,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->listView->setEmptyString(
|
ui->listView->setEmptyString(
|
||||||
tr("Welcome!\n"
|
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->setEmptyMode(VersionListView::String);
|
||||||
ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
@ -85,6 +86,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new
|
|||||||
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
||||||
[this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); });
|
[this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); });
|
||||||
connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu);
|
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::listChanged, this, &AccountListPage::listChanged);
|
||||||
connect(m_accounts.get(), &AccountList::listActivityChanged, 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;
|
auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
|
||||||
unsigned int maxMem = ui->maxMemSpinBox->value();
|
unsigned int maxMem = ui->maxMemSpinBox->value();
|
||||||
|
unsigned int minMem = ui->minMemSpinBox->value();
|
||||||
|
|
||||||
QString iconName;
|
QString iconName;
|
||||||
|
|
||||||
@ -194,6 +195,9 @@ void JavaPage::updateThresholds()
|
|||||||
} else if (maxMem > (sysMiB * 0.9)) {
|
} else if (maxMem > (sysMiB * 0.9)) {
|
||||||
iconName = "status-yellow";
|
iconName = "status-yellow";
|
||||||
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
|
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 {
|
} else {
|
||||||
iconName = "status-good";
|
iconName = "status-good";
|
||||||
ui->labelMaxMemIcon->setToolTip("");
|
ui->labelMaxMemIcon->setToolTip("");
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -35,6 +34,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MinecraftPage.h"
|
#include "MinecraftPage.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
#include "ui_MinecraftPage.h"
|
#include "ui_MinecraftPage.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -44,9 +44,15 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include "MangoHud.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
MinecraftPage::MinecraftPage(QWidget* parent) : QWidget(parent), ui(new Ui::MinecraftPage)
|
MinecraftPage::MinecraftPage(QWidget* parent) : QWidget(parent), ui(new Ui::MinecraftPage)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &MinecraftPage::onUseNativeGLFWChanged);
|
||||||
|
connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &MinecraftPage::onUseNativeOpenALChanged);
|
||||||
loadSettings();
|
loadSettings();
|
||||||
updateCheckboxStuff();
|
updateCheckboxStuff();
|
||||||
}
|
}
|
||||||
@ -74,6 +80,16 @@ void MinecraftPage::on_maximizedCheckBox_clicked(bool checked)
|
|||||||
updateCheckboxStuff();
|
updateCheckboxStuff();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MinecraftPage::onUseNativeGLFWChanged(bool checked)
|
||||||
|
{
|
||||||
|
ui->lineEditGLFWPath->setEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinecraftPage::onUseNativeOpenALChanged(bool checked)
|
||||||
|
{
|
||||||
|
ui->lineEditOpenALPath->setEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
void MinecraftPage::applySettings()
|
void MinecraftPage::applySettings()
|
||||||
{
|
{
|
||||||
auto s = APPLICATION->settings();
|
auto s = APPLICATION->settings();
|
||||||
@ -84,8 +100,10 @@ void MinecraftPage::applySettings()
|
|||||||
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
||||||
|
|
||||||
// Native library workarounds
|
// Native library workarounds
|
||||||
s->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
|
||||||
s->set("UseNativeGLFW", ui->useNativeGLFWCheck->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
|
// Peformance related options
|
||||||
s->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked());
|
s->set("EnableFeralGamemode", ui->enableFeralGamemodeCheck->isChecked());
|
||||||
@ -96,13 +114,11 @@ void MinecraftPage::applySettings()
|
|||||||
s->set("ShowGameTime", ui->showGameTime->isChecked());
|
s->set("ShowGameTime", ui->showGameTime->isChecked());
|
||||||
s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked());
|
s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked());
|
||||||
s->set("RecordGameTime", ui->recordGameTime->isChecked());
|
s->set("RecordGameTime", ui->recordGameTime->isChecked());
|
||||||
|
s->set("ShowGameTimeWithoutDays", ui->showGameTimeWithoutDays->isChecked());
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
|
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
|
||||||
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
|
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
|
||||||
|
|
||||||
// Mod loader settings
|
|
||||||
s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftPage::loadSettings()
|
void MinecraftPage::loadSettings()
|
||||||
@ -114,8 +130,20 @@ void MinecraftPage::loadSettings()
|
|||||||
ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
|
ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
|
||||||
ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
|
ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
|
||||||
|
|
||||||
ui->useNativeOpenALCheck->setChecked(s->get("UseNativeOpenAL").toBool());
|
|
||||||
ui->useNativeGLFWCheck->setChecked(s->get("UseNativeGLFW").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->enableFeralGamemodeCheck->setChecked(s->get("EnableFeralGamemode").toBool());
|
||||||
ui->enableMangoHud->setChecked(s->get("EnableMangoHud").toBool());
|
ui->enableMangoHud->setChecked(s->get("EnableMangoHud").toBool());
|
||||||
@ -138,11 +166,10 @@ void MinecraftPage::loadSettings()
|
|||||||
ui->showGameTime->setChecked(s->get("ShowGameTime").toBool());
|
ui->showGameTime->setChecked(s->get("ShowGameTime").toBool());
|
||||||
ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool());
|
ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool());
|
||||||
ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool());
|
ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool());
|
||||||
|
ui->showGameTimeWithoutDays->setChecked(s->get("ShowGameTimeWithoutDays").toBool());
|
||||||
|
|
||||||
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
|
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
|
||||||
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
|
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
|
||||||
|
|
||||||
ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MinecraftPage::retranslate()
|
void MinecraftPage::retranslate()
|
||||||
|
@ -70,6 +70,9 @@ class MinecraftPage : public QWidget, public BasePage {
|
|||||||
private slots:
|
private slots:
|
||||||
void on_maximizedCheckBox_clicked(bool checked);
|
void on_maximizedCheckBox_clicked(bool checked);
|
||||||
|
|
||||||
|
void onUseNativeGLFWChanged(bool checked);
|
||||||
|
void onUseNativeOpenALChanged(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MinecraftPage* ui;
|
Ui::MinecraftPage* ui;
|
||||||
};
|
};
|
||||||
|
@ -138,6 +138,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="showGameTimeWithoutDays">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show time spent playing in hours</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -190,45 +197,60 @@
|
|||||||
<string>Tweaks</string>
|
<string>Tweaks</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="modLoaderSettingsGroupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Mod loader settings</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="disableQuiltBeaconCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Disable Quilt Loader Beacon</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Disable Quilt loader's beacon for counting monthly active users</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
|
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Native library workarounds</string>
|
<string>Native library workarounds</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_11">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use system installation of &GLFW</string>
|
<string>Use system installation of &GLFW</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="labelGLFWPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>&GLFW library path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>lineEditGLFWPath</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use system installation of &OpenAL</string>
|
<string>Use system installation of &OpenAL</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="labelOpenALPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>&OpenAL library path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>lineEditOpenALPath</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditGLFWPath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditOpenALPath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -152,6 +152,7 @@ void ExternalResourcesPage::retranslate()
|
|||||||
void ExternalResourcesPage::itemActivated(const QModelIndex&)
|
void ExternalResourcesPage::itemActivated(const QModelIndex&)
|
||||||
{
|
{
|
||||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
||||||
|
m_model->setResourceEnabled(selection.indexes(), EnableAction::TOGGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExternalResourcesPage::filterTextChanged(const QString& newContents)
|
void ExternalResourcesPage::filterTextChanged(const QString& newContents)
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2023 seth <getchoo at tuta dot io>
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -48,6 +47,7 @@
|
|||||||
#include "ui/widgets/CustomCommands.h"
|
#include "ui/widgets/CustomCommands.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "BuildConfig.h"
|
||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
#include "minecraft/auth/AccountList.h"
|
#include "minecraft/auth/AccountList.h"
|
||||||
|
|
||||||
@ -66,6 +66,10 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent)
|
|||||||
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
|
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
|
||||||
connect(ui->instanceAccountSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
connect(ui->instanceAccountSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||||
&InstanceSettingsPage::changeInstanceAccount);
|
&InstanceSettingsPage::changeInstanceAccount);
|
||||||
|
|
||||||
|
connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeGLFWChanged);
|
||||||
|
connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeOpenALChanged);
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
updateThresholds();
|
updateThresholds();
|
||||||
@ -198,11 +202,15 @@ void InstanceSettingsPage::applySettings()
|
|||||||
bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked();
|
bool workarounds = ui->nativeWorkaroundsGroupBox->isChecked();
|
||||||
m_settings->set("OverrideNativeWorkarounds", workarounds);
|
m_settings->set("OverrideNativeWorkarounds", workarounds);
|
||||||
if (workarounds) {
|
if (workarounds) {
|
||||||
m_settings->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
|
||||||
m_settings->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked());
|
m_settings->set("UseNativeGLFW", ui->useNativeGLFWCheck->isChecked());
|
||||||
|
m_settings->set("CustomGLFWPath", ui->lineEditGLFWPath->text());
|
||||||
|
m_settings->set("UseNativeOpenAL", ui->useNativeOpenALCheck->isChecked());
|
||||||
|
m_settings->set("CustomOpenALPath", ui->lineEditOpenALPath->text());
|
||||||
} else {
|
} else {
|
||||||
m_settings->reset("UseNativeOpenAL");
|
|
||||||
m_settings->reset("UseNativeGLFW");
|
m_settings->reset("UseNativeGLFW");
|
||||||
|
m_settings->reset("CustomGLFWPath");
|
||||||
|
m_settings->reset("UseNativeOpenAL");
|
||||||
|
m_settings->reset("CustomOpenALPath");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performance
|
// Performance
|
||||||
@ -245,14 +253,6 @@ void InstanceSettingsPage::applySettings()
|
|||||||
m_settings->reset("InstanceAccountId");
|
m_settings->reset("InstanceAccountId");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool overrideModLoaderSettings = ui->modLoaderSettingsGroupBox->isChecked();
|
|
||||||
m_settings->set("OverrideModLoaderSettings", overrideModLoaderSettings);
|
|
||||||
if (overrideModLoaderSettings) {
|
|
||||||
m_settings->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked());
|
|
||||||
} else {
|
|
||||||
m_settings->reset("DisableQuiltBeacon");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This should probably be called by a signal instead
|
// FIXME: This should probably be called by a signal instead
|
||||||
m_instance->updateRuntimeContext();
|
m_instance->updateRuntimeContext();
|
||||||
}
|
}
|
||||||
@ -312,7 +312,19 @@ void InstanceSettingsPage::loadSettings()
|
|||||||
// Workarounds
|
// Workarounds
|
||||||
ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool());
|
ui->nativeWorkaroundsGroupBox->setChecked(m_settings->get("OverrideNativeWorkarounds").toBool());
|
||||||
ui->useNativeGLFWCheck->setChecked(m_settings->get("UseNativeGLFW").toBool());
|
ui->useNativeGLFWCheck->setChecked(m_settings->get("UseNativeGLFW").toBool());
|
||||||
|
ui->lineEditGLFWPath->setText(m_settings->get("CustomGLFWPath").toString());
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
ui->lineEditGLFWPath->setPlaceholderText(APPLICATION->m_detectedGLFWPath);
|
||||||
|
#else
|
||||||
|
ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.GLFW_LIBRARY_NAME));
|
||||||
|
#endif
|
||||||
ui->useNativeOpenALCheck->setChecked(m_settings->get("UseNativeOpenAL").toBool());
|
ui->useNativeOpenALCheck->setChecked(m_settings->get("UseNativeOpenAL").toBool());
|
||||||
|
ui->lineEditOpenALPath->setText(m_settings->get("CustomOpenALPath").toString());
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
ui->lineEditOpenALPath->setPlaceholderText(APPLICATION->m_detectedOpenALPath);
|
||||||
|
#else
|
||||||
|
ui->lineEditGLFWPath->setPlaceholderText(tr("Path to %1 library file").arg(BuildConfig.OPENAL_LIBRARY_NAME));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Performance
|
// Performance
|
||||||
ui->perfomanceGroupBox->setChecked(m_settings->get("OverridePerformance").toBool());
|
ui->perfomanceGroupBox->setChecked(m_settings->get("OverridePerformance").toBool());
|
||||||
@ -344,10 +356,6 @@ void InstanceSettingsPage::loadSettings()
|
|||||||
|
|
||||||
ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool());
|
ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool());
|
||||||
updateAccountsMenu();
|
updateAccountsMenu();
|
||||||
|
|
||||||
// Mod loader specific settings
|
|
||||||
ui->modLoaderSettingsGroupBox->setChecked(m_settings->get("OverrideModLoaderSettings").toBool());
|
|
||||||
ui->disableQuiltBeaconCheckBox->setChecked(m_settings->get("DisableQuiltBeacon").toBool());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceSettingsPage::on_javaDetectBtn_clicked()
|
void InstanceSettingsPage::on_javaDetectBtn_clicked()
|
||||||
@ -408,6 +416,16 @@ void InstanceSettingsPage::on_javaTestBtn_clicked()
|
|||||||
checker->run();
|
checker->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceSettingsPage::onUseNativeGLFWChanged(bool checked)
|
||||||
|
{
|
||||||
|
ui->lineEditGLFWPath->setEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceSettingsPage::onUseNativeOpenALChanged(bool checked)
|
||||||
|
{
|
||||||
|
ui->lineEditOpenALPath->setEnabled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceSettingsPage::updateAccountsMenu()
|
void InstanceSettingsPage::updateAccountsMenu()
|
||||||
{
|
{
|
||||||
ui->instanceAccountSelector->clear();
|
ui->instanceAccountSelector->clear();
|
||||||
@ -460,6 +478,7 @@ void InstanceSettingsPage::updateThresholds()
|
|||||||
{
|
{
|
||||||
auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
|
auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
|
||||||
unsigned int maxMem = ui->maxMemSpinBox->value();
|
unsigned int maxMem = ui->maxMemSpinBox->value();
|
||||||
|
unsigned int minMem = ui->minMemSpinBox->value();
|
||||||
|
|
||||||
QString iconName;
|
QString iconName;
|
||||||
|
|
||||||
@ -469,6 +488,9 @@ void InstanceSettingsPage::updateThresholds()
|
|||||||
} else if (maxMem > (sysMiB * 0.9)) {
|
} else if (maxMem > (sysMiB * 0.9)) {
|
||||||
iconName = "status-yellow";
|
iconName = "status-yellow";
|
||||||
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
|
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 {
|
} else {
|
||||||
iconName = "status-good";
|
iconName = "status-good";
|
||||||
ui->labelMaxMemIcon->setToolTip("");
|
ui->labelMaxMemIcon->setToolTip("");
|
||||||
|
@ -71,6 +71,9 @@ class InstanceSettingsPage : public QWidget, public BasePage {
|
|||||||
void on_javaBrowseBtn_clicked();
|
void on_javaBrowseBtn_clicked();
|
||||||
void on_maxMemSpinBox_valueChanged(int i);
|
void on_maxMemSpinBox_valueChanged(int i);
|
||||||
|
|
||||||
|
void onUseNativeGLFWChanged(bool checked);
|
||||||
|
void onUseNativeOpenALChanged(bool checked);
|
||||||
|
|
||||||
void applySettings();
|
void applySettings();
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
|
|
||||||
|
@ -443,18 +443,52 @@
|
|||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
<item>
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use system installation of OpenAL</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="labelGLFWPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>&GLFW library path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>lineEditGLFWPath</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
<widget class="QCheckBox" name="useNativeGLFWCheck">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use system installation of GLFW</string>
|
<string>Use system installation of GLFW</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="useNativeOpenALCheck">
|
<widget class="QLineEdit" name="lineEditGLFWPath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="labelOpenALPath">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use system installation of OpenAL</string>
|
<string>&OpenAL library path</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>lineEditOpenALPath</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditOpenALPath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -549,31 +583,6 @@
|
|||||||
<string>Miscellaneous</string>
|
<string>Miscellaneous</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="modLoaderSettingsGroupBox">
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>Mod loader settings</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="VerticalLayout_16">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="disableQuiltBeaconCheckBox">
|
|
||||||
<property name="text">
|
|
||||||
<string>Disable Quilt Loader Beacon</string>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Disable Quilt loader's beacon for counting monthly active users</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="gameTimeGroupBox">
|
<widget class="QGroupBox" name="gameTimeGroupBox">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -354,14 +354,8 @@ class ServersModel : public QAbstractListModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override
|
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override { return parent.isValid() ? 0 : m_servers.size(); }
|
||||||
{
|
int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : COLUMN_COUNT; }
|
||||||
return parent.isValid() ? 0 : m_servers.size();
|
|
||||||
}
|
|
||||||
int columnCount(const QModelIndex& parent) const override
|
|
||||||
{
|
|
||||||
return parent.isValid() ? 0 : COLUMN_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
Server* at(int index)
|
Server* at(int index)
|
||||||
{
|
{
|
||||||
@ -445,10 +439,7 @@ class ServersModel : public QAbstractListModel {
|
|||||||
qDebug() << "Changed:" << path;
|
qDebug() << "Changed:" << path;
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
void fileChanged(const QString& path)
|
void fileChanged(const QString& path) { qDebug() << "Changed:" << path; }
|
||||||
{
|
|
||||||
qDebug() << "Changed:" << path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void save_internal()
|
void save_internal()
|
||||||
@ -492,10 +483,7 @@ class ServersModel : public QAbstractListModel {
|
|||||||
m_saveTimer.stop();
|
m_saveTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool saveIsScheduled() const
|
bool saveIsScheduled() const { return m_dirty; }
|
||||||
{
|
|
||||||
return m_dirty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateFSObserver()
|
void updateFSObserver()
|
||||||
{
|
{
|
||||||
@ -743,7 +731,7 @@ void ServersPage::on_actionMove_Down_triggered()
|
|||||||
void ServersPage::on_actionJoin_triggered()
|
void ServersPage::on_actionJoin_triggered()
|
||||||
{
|
{
|
||||||
const auto& address = m_model->at(currentServer)->m_address;
|
const auto& address = m_model->at(currentServer)->m_address;
|
||||||
APPLICATION->launch(m_inst, true, false, nullptr, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address)));
|
APPLICATION->launch(m_inst, true, false, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ServersPage.moc"
|
#include "ServersPage.moc"
|
||||||
|
@ -166,14 +166,17 @@ VersionPage::VersionPage(MinecraftInstance* inst, QWidget* parent) : QMainWindow
|
|||||||
ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection);
|
ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
ui->packageView->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->packageView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
connect(ui->packageView->selectionModel(), &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent);
|
|
||||||
auto smodel = ui->packageView->selectionModel();
|
auto smodel = ui->packageView->selectionModel();
|
||||||
|
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent);
|
||||||
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
|
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
|
||||||
|
|
||||||
connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls);
|
connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls);
|
||||||
updateVersionControls();
|
updateVersionControls();
|
||||||
preselect(0);
|
preselect(0);
|
||||||
connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu);
|
connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu);
|
||||||
|
connect(ui->packageView, &QAbstractItemView::activated, this, [this](const QModelIndex& index) {
|
||||||
|
auto component = m_profile->getComponent(index.row());
|
||||||
|
component->setEnabled(!component->isEnabled());
|
||||||
|
});
|
||||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged);
|
connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +411,7 @@ void VersionPage::on_actionDownload_All_triggered()
|
|||||||
if (!APPLICATION->accounts()->anyAccountIsValid()) {
|
if (!APPLICATION->accounts()->anyAccountIsValid()) {
|
||||||
CustomMessageBox::selectable(this, tr("Error"),
|
CustomMessageBox::selectable(this, tr("Error"),
|
||||||
tr("Cannot download Minecraft or update instances unless you have at least "
|
tr("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)
|
QMessageBox::Warning)
|
||||||
->show();
|
->show();
|
||||||
return;
|
return;
|
||||||
|
@ -127,6 +127,9 @@ void CustomPage::loaderFilterChanged()
|
|||||||
ui->loaderVersionList->setEmptyString(tr("No mod loader is selected."));
|
ui->loaderVersionList->setEmptyString(tr("No mod loader is selected."));
|
||||||
ui->loaderVersionList->setEmptyMode(VersionListView::String);
|
ui->loaderVersionList->setEmptyMode(VersionListView::String);
|
||||||
return;
|
return;
|
||||||
|
} else if (ui->neoForgeFilter->isChecked()) {
|
||||||
|
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||||
|
m_selectedLoader = "net.neoforged";
|
||||||
} else if (ui->forgeFilter->isChecked()) {
|
} else if (ui->forgeFilter->isChecked()) {
|
||||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||||
m_selectedLoader = "net.minecraftforge";
|
m_selectedLoader = "net.minecraftforge";
|
||||||
|
@ -194,6 +194,16 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="neoForgeFilter">
|
||||||
|
<property name="text">
|
||||||
|
<string>NeoForge</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">loaderBtnGroup</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="forgeFilter">
|
<widget class="QRadioButton" name="forgeFilter">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -35,13 +35,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ImportPage.h"
|
#include "ImportPage.h"
|
||||||
|
|
||||||
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui_ImportPage.h"
|
#include "ui_ImportPage.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QValidator>
|
#include <QValidator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/NewInstanceDialog.h"
|
#include "ui/dialogs/NewInstanceDialog.h"
|
||||||
|
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
|
|
||||||
class UrlValidator : public QValidator {
|
class UrlValidator : public QValidator {
|
||||||
@ -106,10 +114,61 @@ void ImportPage::updateState()
|
|||||||
bool isMRPack = fi.suffix() == "mrpack";
|
bool isMRPack = fi.suffix() == "mrpack";
|
||||||
|
|
||||||
if (fi.exists() && (isZip || isMRPack)) {
|
if (fi.exists() && (isZip || isMRPack)) {
|
||||||
QFileInfo file_info(url.fileName());
|
auto extra_info = QMap(m_extra_info);
|
||||||
dialog->setSuggestedPack(file_info.completeBaseName(), new InstanceImportTask(url, this));
|
qDebug() << "Pack Extra Info" << extra_info << m_extra_info;
|
||||||
|
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info)));
|
||||||
dialog->setSuggestedIcon("default");
|
dialog->setSuggestedIcon("default");
|
||||||
}
|
}
|
||||||
|
} else if (url.scheme() == "curseforge") {
|
||||||
|
// need to find the download link for the modpack
|
||||||
|
// format of url curseforge://install?addonId=IDHERE&fileId=IDHERE
|
||||||
|
QUrlQuery query(url);
|
||||||
|
auto addonId = query.allQueryItemValues("addonId")[0];
|
||||||
|
auto fileId = query.allQueryItemValues("fileId")[0];
|
||||||
|
auto array = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
|
auto api = FlameAPI();
|
||||||
|
auto job = api.getFile(addonId, fileId, array);
|
||||||
|
|
||||||
|
connect(job.get(), &NetJob::failed, this,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
|
connect(job.get(), &NetJob::succeeded, this, [this, array, addonId, fileId] {
|
||||||
|
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");
|
||||||
|
if (fileName.endsWith(".zip")) {
|
||||||
|
// Have to use ensureString then use QUrl to get proper url encoding
|
||||||
|
auto dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl"));
|
||||||
|
if (!dl_url.isValid()) {
|
||||||
|
CustomMessageBox::selectable(
|
||||||
|
this, tr("Error"),
|
||||||
|
tr("The modpack %1 is blocked for third-parties! Please download it manually.").arg(fileName),
|
||||||
|
QMessageBox::Critical)
|
||||||
|
->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo dl_file(dl_url.fileName());
|
||||||
|
QString pack_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
|
||||||
|
|
||||||
|
QMap<QString, QString> extra_info;
|
||||||
|
extra_info.insert("pack_id", addonId);
|
||||||
|
extra_info.insert("pack_version_id", fileId);
|
||||||
|
|
||||||
|
dialog->setSuggestedPack(pack_name, new InstanceImportTask(dl_url, this, std::move(extra_info)));
|
||||||
|
dialog->setSuggestedIcon("default");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
CustomMessageBox::selectable(this, tr("Error"), tr("This url isn't a valid modpack !"), QMessageBox::Critical)->show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ProgressDialog dlUrlDialod(this);
|
||||||
|
dlUrlDialod.setSkipButton(true, tr("Abort"));
|
||||||
|
dlUrlDialod.execWithTask(job.get());
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (input.endsWith("?client=y")) {
|
if (input.endsWith("?client=y")) {
|
||||||
input.chop(9);
|
input.chop(9);
|
||||||
@ -118,7 +177,8 @@ void ImportPage::updateState()
|
|||||||
}
|
}
|
||||||
// hook, line and sinker.
|
// hook, line and sinker.
|
||||||
QFileInfo fi(url.fileName());
|
QFileInfo fi(url.fileName());
|
||||||
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this));
|
auto extra_info = QMap(m_extra_info);
|
||||||
|
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info)));
|
||||||
dialog->setSuggestedIcon("default");
|
dialog->setSuggestedIcon("default");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -132,6 +192,12 @@ void ImportPage::setUrl(const QString& url)
|
|||||||
updateState();
|
updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImportPage::setExtraInfo(const QMap<QString, QString>& extra_info)
|
||||||
|
{
|
||||||
|
m_extra_info = extra_info;
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
void ImportPage::on_modpackBtn_clicked()
|
void ImportPage::on_modpackBtn_clicked()
|
||||||
{
|
{
|
||||||
auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString();
|
auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString();
|
||||||
|
@ -62,7 +62,7 @@ class ImportPage : public QWidget, public BasePage {
|
|||||||
|
|
||||||
void setUrl(const QString& url);
|
void setUrl(const QString& url);
|
||||||
void openedImpl() override;
|
void openedImpl() override;
|
||||||
|
void setExtraInfo(const QMap<QString, QString>& extra_info);
|
||||||
private slots:
|
private slots:
|
||||||
void on_modpackBtn_clicked();
|
void on_modpackBtn_clicked();
|
||||||
void updateState();
|
void updateState();
|
||||||
@ -73,4 +73,5 @@ class ImportPage : public QWidget, public BasePage {
|
|||||||
private:
|
private:
|
||||||
Ui::ImportPage* ui = nullptr;
|
Ui::ImportPage* ui = nullptr;
|
||||||
NewInstanceDialog* dialog = nullptr;
|
NewInstanceDialog* dialog = nullptr;
|
||||||
|
QMap<QString, QString> m_extra_info = {};
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>- CurseForge modpacks (ZIP)</string>
|
<string>- CurseForge modpacks (ZIP / curseforge:// URL)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignCenter</set>
|
||||||
|
@ -33,7 +33,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
|
|||||||
|
|
||||||
auto sort = getCurrentSortingMethodByIndex();
|
auto sort = getCurrentSortingMethodByIndex();
|
||||||
|
|
||||||
return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getModLoaders(), versions };
|
return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions };
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry)
|
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry)
|
||||||
@ -48,7 +48,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en
|
|||||||
if (!m_filter->versions.empty())
|
if (!m_filter->versions.empty())
|
||||||
versions = m_filter->versions;
|
versions = m_filter->versions;
|
||||||
|
|
||||||
return { pack, versions, profile->getModLoaders() };
|
return { pack, versions, profile->getSupportedModLoaders() };
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry)
|
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry)
|
||||||
|
@ -124,8 +124,7 @@ void ModPage::updateVersionList()
|
|||||||
auto version = current_pack->versions[i];
|
auto version = current_pack->versions[i];
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
for (auto& mcVer : m_filter->versions) {
|
for (auto& mcVer : m_filter->versions) {
|
||||||
// NOTE: Flame doesn't care about loader, so passing it changes nothing.
|
if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) {
|
||||||
if (validateVersion(version, mcVer.toString(), packProfile->getModLoaders())) {
|
|
||||||
valid = true;
|
valid = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class ModPage : public ResourcePage {
|
|||||||
|
|
||||||
virtual auto validateVersion(ModPlatform::IndexedVersion& ver,
|
virtual auto validateVersion(ModPlatform::IndexedVersion& ver,
|
||||||
QString mineVer,
|
QString mineVer,
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> loaders = {}) const -> bool = 0;
|
std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const -> bool = 0;
|
||||||
|
|
||||||
[[nodiscard]] bool supportsFiltering() const override { return true; };
|
[[nodiscard]] bool supportsFiltering() const override { return true; };
|
||||||
auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }
|
auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#include "ShaderPackPage.h"
|
#include "ShaderPackPage.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
#include "ui_ResourcePage.h"
|
#include "ui_ResourcePage.h"
|
||||||
|
|
||||||
#include "ShaderPackModel.h"
|
#include "ShaderPackModel.h"
|
||||||
@ -48,7 +49,7 @@ void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pac
|
|||||||
const std::shared_ptr<ResourceFolderModel> base_model)
|
const std::shared_ptr<ResourceFolderModel> base_model)
|
||||||
{
|
{
|
||||||
QString custom_target_folder;
|
QString custom_target_folder;
|
||||||
if (version.loaders.contains(QStringLiteral("canvas")))
|
if (version.loaders & ModPlatform::Cauldron)
|
||||||
custom_target_folder = QStringLiteral("resourcepacks");
|
custom_target_folder = QStringLiteral("resourcepacks");
|
||||||
m_model->addPack(pack, version, base_model, false, custom_target_folder);
|
m_model->addPack(pack, version, base_model, false, custom_target_folder);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr
|
|||||||
|
|
||||||
auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||||
{
|
{
|
||||||
return FlameMod::loadDependencyVersions(m, arr);
|
return FlameMod::loadDependencyVersions(m, arr, &m_base_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||||
|
@ -68,10 +68,10 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) :
|
|||||||
|
|
||||||
auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver,
|
auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver,
|
||||||
QString mineVer,
|
QString mineVer,
|
||||||
std::optional<ResourceAPI::ModLoaderTypes> loaders) const -> bool
|
std::optional<ModPlatform::ModLoaderTypes> loaders) const -> bool
|
||||||
{
|
{
|
||||||
Q_UNUSED(loaders);
|
return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() &&
|
||||||
return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty();
|
(!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user