Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into fix_login
This commit is contained in:
commit
b676a67b3c
@ -2,3 +2,6 @@
|
|||||||
|
|
||||||
# tabs -> spaces
|
# tabs -> spaces
|
||||||
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
||||||
|
|
||||||
|
# (nix) alejandra -> nixfmt
|
||||||
|
4c81d8c53d09196426568c4a31a4e752ed05397a
|
||||||
|
53
.github/workflows/build.yml
vendored
53
.github/workflows/build.yml
vendored
@ -39,6 +39,9 @@ on:
|
|||||||
APPLE_NOTARIZE_PASSWORD:
|
APPLE_NOTARIZE_PASSWORD:
|
||||||
description: Password used for notarizing macOS builds
|
description: Password used for notarizing macOS builds
|
||||||
required: false
|
required: false
|
||||||
|
CACHIX_AUTH_TOKEN:
|
||||||
|
description: Private token for authenticating against Cachix cache
|
||||||
|
required: false
|
||||||
GPG_PRIVATE_KEY:
|
GPG_PRIVATE_KEY:
|
||||||
description: Private key for AppImage signing
|
description: Private key for AppImage signing
|
||||||
required: false
|
required: false
|
||||||
@ -631,3 +634,53 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
bundle: "Prism Launcher.flatpak"
|
bundle: "Prism Launcher.flatpak"
|
||||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||||
|
|
||||||
|
nix:
|
||||||
|
name: Nix (${{ matrix.system }})
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
system: x86_64-linux
|
||||||
|
|
||||||
|
- os: macos-13
|
||||||
|
system: x86_64-darwin
|
||||||
|
|
||||||
|
- os: macos-14
|
||||||
|
system: aarch64-darwin
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v27
|
||||||
|
|
||||||
|
# For PRs
|
||||||
|
- name: Setup Nix Magic Cache
|
||||||
|
uses: DeterminateSystems/magic-nix-cache-action@v8
|
||||||
|
|
||||||
|
# For in-tree builds
|
||||||
|
- name: Setup Cachix
|
||||||
|
uses: cachix/cachix-action@v15
|
||||||
|
with:
|
||||||
|
name: prismlauncher
|
||||||
|
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run flake checks
|
||||||
|
run: |
|
||||||
|
nix flake check --print-build-logs --show-trace
|
||||||
|
|
||||||
|
- name: Build debug package
|
||||||
|
if: ${{ inputs.build_type == 'Debug' }}
|
||||||
|
run: |
|
||||||
|
nix build --print-build-logs .#prismlauncher-debug
|
||||||
|
|
||||||
|
- name: Build release package
|
||||||
|
if: ${{ inputs.build_type != 'Debug' }}
|
||||||
|
run: |
|
||||||
|
nix build --print-build-logs .#prismlauncher
|
||||||
|
1
.github/workflows/trigger_builds.yml
vendored
1
.github/workflows/trigger_builds.yml
vendored
@ -38,5 +38,6 @@ jobs:
|
|||||||
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
1
.github/workflows/trigger_release.yml
vendored
1
.github/workflows/trigger_release.yml
vendored
@ -22,6 +22,7 @@ jobs:
|
|||||||
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }}
|
||||||
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }}
|
||||||
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }}
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||||
|
|
||||||
|
@ -176,6 +176,8 @@ endif()
|
|||||||
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.")
|
||||||
|
set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 9)
|
set(Launcher_VERSION_MAJOR 9)
|
||||||
@ -205,6 +207,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss
|
|||||||
|
|
||||||
# Translations Platform URL
|
# Translations Platform URL
|
||||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
||||||
|
set(Launcher_TRANSLATION_FILES_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.")
|
||||||
|
|
||||||
# Matrix Space
|
# Matrix Space
|
||||||
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
||||||
|
@ -116,16 +116,19 @@ Config::Config()
|
|||||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||||
HELP_URL = "@Launcher_HELP_URL@";
|
HELP_URL = "@Launcher_HELP_URL@";
|
||||||
|
LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@";
|
||||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||||
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
||||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||||
META_URL = "@Launcher_META_URL@";
|
META_URL = "@Launcher_META_URL@";
|
||||||
|
FMLLIBS_BASE_URL = "@Launcher_FMLLIBS_BASE_URL@";
|
||||||
|
|
||||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_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@";
|
||||||
|
TRANSLATION_FILES_URL = "@Launcher_TRANSLATION_FILES_URL@";
|
||||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||||
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
DISCORD_URL = "@Launcher_DISCORD_URL@";
|
||||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||||
|
@ -133,6 +133,11 @@ class Config {
|
|||||||
*/
|
*/
|
||||||
QString HELP_URL;
|
QString HELP_URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL that gets opened when the user succesfully logins.
|
||||||
|
*/
|
||||||
|
QString LOGIN_CALLBACK_URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client ID you can get from Imgur when you register an application
|
* Client ID you can get from Imgur when you register an application
|
||||||
*/
|
*/
|
||||||
@ -165,8 +170,8 @@ class Config {
|
|||||||
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
|
||||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
QString FMLLIBS_BASE_URL;
|
||||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
QString TRANSLATION_FILES_URL;
|
||||||
|
|
||||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||||
|
|
||||||
|
25
flake.nix
25
flake.nix
@ -2,8 +2,10 @@
|
|||||||
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
||||||
|
|
||||||
nixConfig = {
|
nixConfig = {
|
||||||
extra-substituters = [ "https://cache.garnix.io" ];
|
extra-substituters = [ "https://prismlauncher.cachix.org" ];
|
||||||
extra-trusted-public-keys = [ "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" ];
|
extra-trusted-public-keys = [
|
||||||
|
"prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c="
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
@ -118,5 +120,24 @@
|
|||||||
# Only output them if they're available on the current system
|
# Only output them if they're available on the current system
|
||||||
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
|
lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# We put these under legacyPackages as they are meant for CI, not end user consumption
|
||||||
|
legacyPackages = forAllSystems (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
prismPackages = self.packages.${system};
|
||||||
|
legacyPackages = self.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
prismlauncher-debug = prismPackages.prismlauncher.override {
|
||||||
|
prismlauncher-unwrapped = legacyPackages.prismlauncher-unwrapped-debug;
|
||||||
|
};
|
||||||
|
|
||||||
|
prismlauncher-unwrapped-debug = prismPackages.prismlauncher-unwrapped.overrideAttrs {
|
||||||
|
cmakeBuildType = "Debug";
|
||||||
|
dontStrip = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
10
garnix.yaml
10
garnix.yaml
@ -1,10 +0,0 @@
|
|||||||
builds:
|
|
||||||
exclude:
|
|
||||||
# Currently broken on Garnix's end
|
|
||||||
- "*.x86_64-darwin.*"
|
|
||||||
include:
|
|
||||||
- "checks.x86_64-linux.*"
|
|
||||||
- "packages.x86_64-linux.*"
|
|
||||||
- "packages.aarch64-linux.*"
|
|
||||||
- "packages.x86_64-darwin.*"
|
|
||||||
- "packages.aarch64-darwin.*"
|
|
@ -780,6 +780,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
// FTBApp instances
|
// FTBApp instances
|
||||||
m_settings->registerSetting("FTBAppInstancesPath", "");
|
m_settings->registerSetting("FTBAppInstancesPath", "");
|
||||||
|
|
||||||
|
// Custom Technic Client ID
|
||||||
|
m_settings->registerSetting("TechnicClientID", "");
|
||||||
|
|
||||||
// Init page provider
|
// Init page provider
|
||||||
{
|
{
|
||||||
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
||||||
@ -1022,7 +1025,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// notify user if /tmp is mounted with `noexec` (#1693)
|
// notify user if /tmp is mounted with `noexec` (#1693)
|
||||||
{
|
QString jvmArgs = m_settings->get("JvmArgs").toString();
|
||||||
|
if (jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */
|
||||||
bool is_tmp_noexec = false;
|
bool is_tmp_noexec = false;
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(Q_OS_LINUX)
|
||||||
@ -1042,7 +1046,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
if (is_tmp_noexec) {
|
if (is_tmp_noexec) {
|
||||||
auto infoMsg =
|
auto infoMsg =
|
||||||
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
|
tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n"
|
||||||
"Some versions of Minecraft may not launch.\n");
|
"Some versions of Minecraft may not launch.\n"
|
||||||
|
"\n"
|
||||||
|
"You may solve this issue by remounting /tmp as 'exec' or setting "
|
||||||
|
"the java.io.tmpdir JVM argument to a writeable directory in a "
|
||||||
|
"filesystem where the 'exec' flag is set (e.g., /home/user/.local/tmp)\n");
|
||||||
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
|
auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok);
|
||||||
msgBox->setDefaultButton(QMessageBox::Ok);
|
msgBox->setDefaultButton(QMessageBox::Ok);
|
||||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
@ -1870,6 +1878,7 @@ QUrl Application::normalizeImportUrl(QString const& url)
|
|||||||
return QUrl::fromUserInput(url);
|
return QUrl::fromUserInput(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString Application::javaPath()
|
const QString Application::javaPath()
|
||||||
{
|
{
|
||||||
return m_settings->get("JavaDir").toString();
|
return m_settings->get("JavaDir").toString();
|
||||||
|
@ -439,6 +439,8 @@ set(JAVA_SOURCES
|
|||||||
java/download/ArchiveDownloadTask.h
|
java/download/ArchiveDownloadTask.h
|
||||||
java/download/ManifestDownloadTask.cpp
|
java/download/ManifestDownloadTask.cpp
|
||||||
java/download/ManifestDownloadTask.h
|
java/download/ManifestDownloadTask.h
|
||||||
|
java/download/SymlinkTask.cpp
|
||||||
|
java/download/SymlinkTask.h
|
||||||
|
|
||||||
ui/java/InstallJavaDialog.h
|
ui/java/InstallJavaDialog.h
|
||||||
ui/java/InstallJavaDialog.cpp
|
ui/java/InstallJavaDialog.cpp
|
||||||
|
@ -173,7 +173,11 @@ void InstanceCopyTask::copyFinished()
|
|||||||
allowed_symlinks_file
|
allowed_symlinks_file
|
||||||
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
||||||
|
|
||||||
|
try {
|
||||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||||
|
} catch (const FS::FileSystemException& e) {
|
||||||
|
qCritical() << "Failed to write symlink :" << e.cause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
@ -140,9 +140,9 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
|||||||
case Path:
|
case Path:
|
||||||
return tr("Filesystem path to this version");
|
return tr("Filesystem path to this version");
|
||||||
case JavaName:
|
case JavaName:
|
||||||
return tr("The alternative name of the java version");
|
return tr("The alternative name of the Java version");
|
||||||
case JavaMajor:
|
case JavaMajor:
|
||||||
return tr("The java major version");
|
return tr("The Java major version");
|
||||||
case Time:
|
case Time:
|
||||||
return tr("Release date of this version");
|
return tr("Release date of this version");
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ void ArchiveDownloadTask::executeTask()
|
|||||||
|
|
||||||
void ArchiveDownloadTask::extractJava(QString input)
|
void ArchiveDownloadTask::extractJava(QString input)
|
||||||
{
|
{
|
||||||
setStatus(tr("Extracting java"));
|
setStatus(tr("Extracting Java"));
|
||||||
if (input.endsWith("tar")) {
|
if (input.endsWith("tar")) {
|
||||||
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
|
setStatus(tr("Extracting Java (Progress is not reported for tar archives)"));
|
||||||
QFile in(input);
|
QFile in(input);
|
||||||
@ -95,7 +95,7 @@ void ArchiveDownloadTask::extractJava(QString input)
|
|||||||
}
|
}
|
||||||
auto files = zip->getFileNameList();
|
auto files = zip->getFileNameList();
|
||||||
if (files.isEmpty()) {
|
if (files.isEmpty()) {
|
||||||
emitFailed(tr("No files were found in the supplied zip file,"));
|
emitFailed(tr("No files were found in the supplied zip file."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_task = makeShared<MMCZip::ExtractZipTask>(zip, m_final_path, files[0]);
|
m_task = makeShared<MMCZip::ExtractZipTask>(zip, m_final_path, files[0]);
|
||||||
|
81
launcher/java/download/SymlinkTask.cpp
Normal file
81
launcher/java/download/SymlinkTask.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "java/download/SymlinkTask.h"
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
namespace Java {
|
||||||
|
SymlinkTask::SymlinkTask(QString final_path) : m_path(final_path) {}
|
||||||
|
|
||||||
|
QString findBinPath(QString root, QString pattern)
|
||||||
|
{
|
||||||
|
auto path = FS::PathCombine(root, pattern);
|
||||||
|
if (QFileInfo::exists(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entries = QDir(root).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (auto& entry : entries) {
|
||||||
|
path = FS::PathCombine(entry.absoluteFilePath(), pattern);
|
||||||
|
if (QFileInfo::exists(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymlinkTask::executeTask()
|
||||||
|
{
|
||||||
|
setStatus(tr("Checking for Java binary path"));
|
||||||
|
const auto binPath = FS::PathCombine("bin", "java");
|
||||||
|
const auto wantedPath = FS::PathCombine(m_path, binPath);
|
||||||
|
if (QFileInfo::exists(wantedPath)) {
|
||||||
|
emitSucceeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(tr("Searching for Java binary path"));
|
||||||
|
const auto contentsPartialPath = FS::PathCombine("Contents", "Home", binPath);
|
||||||
|
const auto relativePathToBin = findBinPath(m_path, contentsPartialPath);
|
||||||
|
if (relativePathToBin.isEmpty()) {
|
||||||
|
emitFailed(tr("Failed to find Java binary path"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto folderToLink = relativePathToBin.chopped(binPath.length());
|
||||||
|
|
||||||
|
setStatus(tr("Collecting folders to symlink"));
|
||||||
|
auto entries = QDir(folderToLink).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries);
|
||||||
|
QList<FS::LinkPair> files;
|
||||||
|
setProgress(0, entries.length());
|
||||||
|
for (auto& entry : entries) {
|
||||||
|
files.append({ entry.absoluteFilePath(), FS::PathCombine(m_path, entry.fileName()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(tr("Symlinking Java binary path"));
|
||||||
|
FS::create_link folderLink(files);
|
||||||
|
connect(&folderLink, &FS::create_link::fileLinked, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); });
|
||||||
|
if (!folderLink()) {
|
||||||
|
emitFailed(folderLink.getOSError().message().c_str());
|
||||||
|
} else {
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Java
|
36
launcher/java/download/SymlinkTask.h
Normal file
36
launcher/java/download/SymlinkTask.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023-2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
namespace Java {
|
||||||
|
|
||||||
|
class SymlinkTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SymlinkTask(QString final_path);
|
||||||
|
virtual ~SymlinkTask() = default;
|
||||||
|
|
||||||
|
void executeTask() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_path;
|
||||||
|
Task::Ptr m_task;
|
||||||
|
};
|
||||||
|
} // namespace Java
|
@ -90,15 +90,16 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
|||||||
|
|
||||||
{
|
{
|
||||||
auto replyHandler = new QOAuthHttpServerReplyHandler(this);
|
auto replyHandler = new QOAuthHttpServerReplyHandler(this);
|
||||||
replyHandler->setCallbackText(R"XXX(
|
replyHandler->setCallbackText(QString(R"XXX(
|
||||||
<noscript>
|
<noscript>
|
||||||
<meta http-equiv="Refresh" content="0; URL=https://prismlauncher.org/successful-login" />
|
<meta http-equiv="Refresh" content="0; URL=%1" />
|
||||||
</noscript>
|
</noscript>
|
||||||
Login Successful, redirecting...
|
Login Successful, redirecting...
|
||||||
<script>
|
<script>
|
||||||
window.location.replace("https://prismlauncher.org/successful-login");
|
window.location.replace("%1");
|
||||||
</script>
|
</script>
|
||||||
)XXX");
|
)XXX")
|
||||||
|
.arg(BuildConfig.LOGIN_CALLBACK_URL));
|
||||||
oauth2.setReplyHandler(replyHandler);
|
oauth2.setReplyHandler(replyHandler);
|
||||||
} else {
|
} else {
|
||||||
oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this));
|
oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this));
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "MessageLevel.h"
|
#include "MessageLevel.h"
|
||||||
|
#include "QObjectPtr.h"
|
||||||
#include "SysInfo.h"
|
#include "SysInfo.h"
|
||||||
#include "java/JavaInstall.h"
|
#include "java/JavaInstall.h"
|
||||||
#include "java/JavaInstallList.h"
|
#include "java/JavaInstallList.h"
|
||||||
@ -48,10 +49,12 @@
|
|||||||
#include "java/JavaVersion.h"
|
#include "java/JavaVersion.h"
|
||||||
#include "java/download/ArchiveDownloadTask.h"
|
#include "java/download/ArchiveDownloadTask.h"
|
||||||
#include "java/download/ManifestDownloadTask.h"
|
#include "java/download/ManifestDownloadTask.h"
|
||||||
|
#include "java/download/SymlinkTask.h"
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
|
|
||||||
AutoInstallJava::AutoInstallJava(LaunchTask* parent)
|
AutoInstallJava::AutoInstallJava(LaunchTask* parent)
|
||||||
: LaunchStep(parent)
|
: LaunchStep(parent)
|
||||||
@ -175,6 +178,12 @@ void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName)
|
|||||||
emitFailed(tr("Could not determine Java download type!"));
|
emitFailed(tr("Could not determine Java download type!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if defined(Q_OS_MACOS)
|
||||||
|
auto seq = makeShared<SequentialTask>(this, tr("Install Java"));
|
||||||
|
seq->addTask(m_current_task);
|
||||||
|
seq->addTask(makeShared<Java::SymlinkTask>(final_path));
|
||||||
|
m_current_task = seq;
|
||||||
|
#endif
|
||||||
auto deletePath = [final_path] { FS::deletePath(final_path); };
|
auto deletePath = [final_path] { FS::deletePath(final_path); };
|
||||||
connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) {
|
connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) {
|
||||||
deletePath();
|
deletePath();
|
||||||
|
@ -42,6 +42,6 @@ class LocalModUpdateTask : public Task {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QDir m_index_dir;
|
QDir m_index_dir;
|
||||||
ModPlatform::IndexedPack& m_mod;
|
ModPlatform::IndexedPack m_mod;
|
||||||
ModPlatform::IndexedVersion& m_mod_version;
|
ModPlatform::IndexedVersion m_mod_version;
|
||||||
};
|
};
|
||||||
|
@ -336,7 +336,11 @@ void SkinList::save()
|
|||||||
arr << s.toJSON();
|
arr << s.toJSON();
|
||||||
}
|
}
|
||||||
doc["skins"] = arr;
|
doc["skins"] = arr;
|
||||||
|
try {
|
||||||
Json::write(doc, m_dir.absoluteFilePath("index.json"));
|
Json::write(doc, m_dir.absoluteFilePath("index.json"));
|
||||||
|
} catch (const FS::FileSystemException& e) {
|
||||||
|
qCritical() << "Failed to write skin index file :" << e.cause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SkinList::getSelectedAccountSkin()
|
int SkinList::getSelectedAccountSkin()
|
||||||
|
@ -41,7 +41,7 @@ SkinModel::SkinModel(QDir skinDir, QJsonObject obj)
|
|||||||
|
|
||||||
QString SkinModel::name() const
|
QString SkinModel::name() const
|
||||||
{
|
{
|
||||||
return QFileInfo(m_path).baseName();
|
return QFileInfo(m_path).completeBaseName();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkinModel::rename(QString newName)
|
bool SkinModel::rename(QString newName)
|
||||||
|
@ -42,6 +42,9 @@ EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform:
|
|||||||
m_hashing_task->addTask(hash_task);
|
m_hashing_task->addTask(hash_task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EnsureMetadataTask::EnsureMetadataTask(QHash<QString, Mod*>& mods, QDir dir, ModPlatform::ResourceProvider prov)
|
||||||
|
: Task(nullptr), m_mods(mods), m_index_dir(dir), m_provider(prov), m_current_task(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Mod* mod)
|
Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Mod* mod)
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ModIndex.h"
|
#include "ModIndex.h"
|
||||||
#include "net/NetJob.h"
|
|
||||||
|
|
||||||
#include "modplatform/helpers/HashUtils.h"
|
#include "modplatform/helpers/HashUtils.h"
|
||||||
|
|
||||||
#include "tasks/ConcurrentTask.h"
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
class Mod;
|
class Mod;
|
||||||
class QDir;
|
|
||||||
|
|
||||||
class EnsureMetadataTask : public Task {
|
class EnsureMetadataTask : public Task {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -16,6 +16,7 @@ class EnsureMetadataTask : public Task {
|
|||||||
public:
|
public:
|
||||||
EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||||
EnsureMetadataTask(QList<Mod*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
EnsureMetadataTask(QList<Mod*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||||
|
EnsureMetadataTask(QHash<QString, Mod*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||||
|
|
||||||
~EnsureMetadataTask() = default;
|
~EnsureMetadataTask() = default;
|
||||||
|
|
||||||
|
@ -1,87 +1,101 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "FileResolvingTask.h"
|
#include "FileResolvingTask.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "QObjectPtr.h"
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
#include "net/ApiUpload.h"
|
#include "modplatform/flame/FlameModIndex.h"
|
||||||
#include "net/Upload.h"
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
static const FlameAPI flameAPI;
|
||||||
|
static ModrinthAPI modrinthAPI;
|
||||||
|
|
||||||
Flame::FileResolvingTask::FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess)
|
Flame::FileResolvingTask::FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess)
|
||||||
: m_network(network), m_toProcess(toProcess)
|
: m_network(network), m_manifest(toProcess)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool Flame::FileResolvingTask::abort()
|
bool Flame::FileResolvingTask::abort()
|
||||||
{
|
{
|
||||||
bool aborted = true;
|
bool aborted = true;
|
||||||
if (m_dljob)
|
if (m_task) {
|
||||||
aborted &= m_dljob->abort();
|
aborted = m_task->abort();
|
||||||
if (m_checkJob)
|
}
|
||||||
aborted &= m_checkJob->abort();
|
|
||||||
return aborted ? Task::abort() : false;
|
return aborted ? Task::abort() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flame::FileResolvingTask::executeTask()
|
void Flame::FileResolvingTask::executeTask()
|
||||||
{
|
{
|
||||||
if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
|
if (m_manifest.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setStatus(tr("Resolving mod IDs..."));
|
setStatus(tr("Resolving mod IDs..."));
|
||||||
setProgress(0, 3);
|
setProgress(0, 3);
|
||||||
m_dljob.reset(new NetJob("Mod id resolver", m_network));
|
m_result.reset(new QByteArray());
|
||||||
result.reset(new QByteArray());
|
|
||||||
// build json data to send
|
|
||||||
QJsonObject object;
|
|
||||||
|
|
||||||
object["fileIds"] = QJsonArray::fromVariantList(
|
QStringList fileIds;
|
||||||
std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) {
|
for (auto file : m_manifest.files) {
|
||||||
l.push_back(s.fileId);
|
fileIds.push_back(QString::number(file.fileId));
|
||||||
return l;
|
}
|
||||||
}));
|
m_task = flameAPI.getFiles(fileIds, m_result);
|
||||||
QByteArray data = Json::toText(object);
|
|
||||||
auto dl = Net::ApiUpload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data);
|
|
||||||
m_dljob->addNetAction(dl);
|
|
||||||
|
|
||||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||||
connect(m_dljob.get(), &NetJob::finished, this, [this, step_progress]() {
|
connect(m_task.get(), &Task::finished, this, [this, step_progress]() {
|
||||||
step_progress->state = TaskStepState::Succeeded;
|
step_progress->state = TaskStepState::Succeeded;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
netJobFinished();
|
netJobFinished();
|
||||||
});
|
});
|
||||||
connect(m_dljob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
|
connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) {
|
||||||
step_progress->state = TaskStepState::Failed;
|
step_progress->state = TaskStepState::Failed;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
});
|
});
|
||||||
connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||||
connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||||
qDebug() << "Resolve slug progress" << current << total;
|
qDebug() << "Resolve slug progress" << current << total;
|
||||||
step_progress->update(current, total);
|
step_progress->update(current, total);
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
});
|
});
|
||||||
connect(m_dljob.get(), &NetJob::status, this, [this, step_progress](QString status) {
|
connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) {
|
||||||
step_progress->status = status;
|
step_progress->status = status;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_dljob->start();
|
m_task->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flame::FileResolvingTask::netJobFinished()
|
void Flame::FileResolvingTask::netJobFinished()
|
||||||
{
|
{
|
||||||
setProgress(1, 3);
|
setProgress(1, 3);
|
||||||
// job to check modrinth for blocked projects
|
// job to check modrinth for blocked projects
|
||||||
m_checkJob.reset(new NetJob("Modrinth check", m_network));
|
|
||||||
m_checkJob->setAskRetry(false);
|
|
||||||
blockedProjects = QMap<File*, std::shared_ptr<QByteArray>>();
|
|
||||||
|
|
||||||
QJsonDocument doc;
|
QJsonDocument doc;
|
||||||
QJsonArray array;
|
QJsonArray array;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doc = Json::requireDocument(*result);
|
doc = Json::requireDocument(*m_result);
|
||||||
array = Json::requireArray(doc.object()["data"]);
|
array = Json::requireArray(doc.object()["data"]);
|
||||||
} catch (Json::JsonException& e) {
|
} catch (Json::JsonException& e) {
|
||||||
qCritical() << "Non-JSON data returned from the CF API";
|
qCritical() << "Non-JSON data returned from the CF API";
|
||||||
@ -92,125 +106,157 @@ void Flame::FileResolvingTask::netJobFinished()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList hashes;
|
||||||
for (QJsonValueRef file : array) {
|
for (QJsonValueRef file : array) {
|
||||||
auto fileid = Json::requireInteger(Json::requireObject(file)["id"]);
|
|
||||||
auto& out = m_toProcess.files[fileid];
|
|
||||||
try {
|
try {
|
||||||
out.parseFromObject(Json::requireObject(file));
|
auto obj = Json::requireObject(file);
|
||||||
} catch ([[maybe_unused]] const JSONValidationError& e) {
|
auto version = FlameMod::loadIndexedPackVersion(obj);
|
||||||
qDebug() << "Blocked mod on curseforge" << out.fileName;
|
auto fileid = version.fileId.toInt();
|
||||||
auto hash = out.hash;
|
m_manifest.files[fileid].version = version;
|
||||||
if (!hash.isEmpty()) {
|
auto url = QUrl(version.downloadUrl, QUrl::TolerantMode);
|
||||||
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
|
if (!url.isValid() && "sha1" == version.hash_type && !version.hash.isEmpty()) {
|
||||||
auto output = std::make_shared<QByteArray>();
|
hashes.push_back(version.hash);
|
||||||
auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output);
|
}
|
||||||
QObject::connect(dl.get(), &Task::succeeded, [&out]() { out.resolved = true; });
|
} catch (Json::JsonException& e) {
|
||||||
|
qCritical() << "Non-JSON data returned from the CF API";
|
||||||
|
qCritical() << e.cause();
|
||||||
|
|
||||||
m_checkJob->addNetAction(dl);
|
emitFailed(tr("Invalid data returned from the API."));
|
||||||
blockedProjects.insert(&out, output);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hashes.isEmpty()) {
|
||||||
|
getFlameProjects();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
m_result.reset(new QByteArray());
|
||||||
|
m_task = modrinthAPI.currentVersions(hashes, "sha1", m_result);
|
||||||
|
(dynamic_cast<NetJob*>(m_task.get()))->setAskRetry(false);
|
||||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||||
connect(m_checkJob.get(), &NetJob::finished, this, [this, step_progress]() {
|
connect(m_task.get(), &Task::finished, this, [this, step_progress]() {
|
||||||
step_progress->state = TaskStepState::Succeeded;
|
step_progress->state = TaskStepState::Succeeded;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
modrinthCheckFinished();
|
QJsonParseError parse_error{};
|
||||||
});
|
QJsonDocument doc = QJsonDocument::fromJson(*m_result, &parse_error);
|
||||||
connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
step_progress->state = TaskStepState::Failed;
|
qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at " << parse_error.offset
|
||||||
stepProgress(*step_progress);
|
<< " reason: " << parse_error.errorString();
|
||||||
});
|
qWarning() << *m_result;
|
||||||
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
|
||||||
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
|
||||||
qDebug() << "Resolve slug progress" << current << total;
|
|
||||||
step_progress->update(current, total);
|
|
||||||
stepProgress(*step_progress);
|
|
||||||
});
|
|
||||||
connect(m_checkJob.get(), &NetJob::status, this, [this, step_progress](QString status) {
|
|
||||||
step_progress->status = status;
|
|
||||||
stepProgress(*step_progress);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_checkJob->start();
|
failed(parse_error.errorString());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flame::FileResolvingTask::modrinthCheckFinished()
|
try {
|
||||||
{
|
auto entries = Json::requireObject(doc);
|
||||||
setProgress(2, 3);
|
for (auto& out : m_manifest.files) {
|
||||||
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
|
auto url = QUrl(out.version.downloadUrl, QUrl::TolerantMode);
|
||||||
|
if (!url.isValid() && "sha1" == out.version.hash_type && !out.version.hash.isEmpty()) {
|
||||||
|
try {
|
||||||
|
auto entry = Json::requireObject(entries, out.version.hash);
|
||||||
|
|
||||||
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
|
auto file = Modrinth::loadIndexedPackVersion(entry);
|
||||||
auto& out = *it;
|
|
||||||
auto bytes = blockedProjects[out];
|
|
||||||
if (!out->resolved) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(*bytes);
|
|
||||||
auto obj = doc.object();
|
|
||||||
auto file = Modrinth::loadIndexedPackVersion(obj);
|
|
||||||
|
|
||||||
// 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 || hasSingleModLoaderSelected(file.loaders)) {
|
if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) {
|
||||||
out->url = file.downloadUrl;
|
out.version.downloadUrl = file.downloadUrl;
|
||||||
qDebug() << "Found alternative on modrinth " << out->fileName;
|
qDebug() << "Found alternative on modrinth " << out.version.fileName;
|
||||||
} else {
|
}
|
||||||
out->resolved = false;
|
} catch (Json::JsonException& e) {
|
||||||
|
qDebug() << e.cause();
|
||||||
|
qDebug() << entries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// copy to an output list and filter out projects found on modrinth
|
}
|
||||||
auto block = std::make_shared<QList<File*>>();
|
} catch (Json::JsonException& e) {
|
||||||
auto it = blockedProjects.keys();
|
qDebug() << e.cause();
|
||||||
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
|
qDebug() << doc;
|
||||||
// Display not found mods early
|
}
|
||||||
if (!block->empty()) {
|
getFlameProjects();
|
||||||
// blocked mods found, we need the slug for displaying.... we need another job :D !
|
|
||||||
m_slugJob.reset(new NetJob("Slug Job", m_network));
|
|
||||||
int index = 0;
|
|
||||||
for (auto mod : *block) {
|
|
||||||
auto projectId = mod->projectId;
|
|
||||||
auto output = std::make_shared<QByteArray>();
|
|
||||||
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
|
|
||||||
auto dl = Net::ApiDownload::makeByteArray(url, output);
|
|
||||||
qDebug() << "Fetching url slug for file:" << mod->fileName;
|
|
||||||
QObject::connect(dl.get(), &Task::succeeded, [block, index, output]() {
|
|
||||||
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
|
||||||
auto json = QJsonDocument::fromJson(*output);
|
|
||||||
auto base =
|
|
||||||
Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
|
|
||||||
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
|
|
||||||
mod->websiteUrl = link;
|
|
||||||
});
|
});
|
||||||
m_slugJob->addNetAction(dl);
|
connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) {
|
||||||
index++;
|
|
||||||
}
|
|
||||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
|
||||||
connect(m_slugJob.get(), &NetJob::succeeded, this, [this, step_progress]() {
|
|
||||||
step_progress->state = TaskStepState::Succeeded;
|
|
||||||
stepProgress(*step_progress);
|
|
||||||
emitSucceeded();
|
|
||||||
});
|
|
||||||
connect(m_slugJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) {
|
|
||||||
step_progress->state = TaskStepState::Failed;
|
step_progress->state = TaskStepState::Failed;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
emitFailed(reason);
|
|
||||||
});
|
});
|
||||||
connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||||
connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||||
qDebug() << "Resolve slug progress" << current << total;
|
qDebug() << "Resolve slug progress" << current << total;
|
||||||
step_progress->update(current, total);
|
step_progress->update(current, total);
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
});
|
});
|
||||||
connect(m_slugJob.get(), &NetJob::status, this, [this, step_progress](QString status) {
|
connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) {
|
||||||
step_progress->status = status;
|
step_progress->status = status;
|
||||||
stepProgress(*step_progress);
|
stepProgress(*step_progress);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_slugJob->start();
|
m_task->start();
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
void Flame::FileResolvingTask::getFlameProjects()
|
||||||
|
{
|
||||||
|
setProgress(2, 3);
|
||||||
|
m_result.reset(new QByteArray());
|
||||||
|
QStringList addonIds;
|
||||||
|
for (auto file : m_manifest.files) {
|
||||||
|
addonIds.push_back(QString::number(file.projectId));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_task = flameAPI.getProjects(addonIds, m_result);
|
||||||
|
|
||||||
|
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||||
|
connect(m_task.get(), &Task::succeeded, this, [this, step_progress] {
|
||||||
|
QJsonParseError parse_error{};
|
||||||
|
auto doc = QJsonDocument::fromJson(*m_result, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from Modrinth projects task at " << parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *m_result;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
QJsonArray entries;
|
||||||
|
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||||
|
|
||||||
|
for (auto entry : entries) {
|
||||||
|
auto entry_obj = Json::requireObject(entry);
|
||||||
|
auto id = Json::requireInteger(entry_obj, "id");
|
||||||
|
auto file = std::find_if(m_manifest.files.begin(), m_manifest.files.end(),
|
||||||
|
[id](const Flame::File& file) { return file.projectId == id; });
|
||||||
|
if (file == m_manifest.files.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(file->version.fileName));
|
||||||
|
FlameMod::loadIndexedPack(file->pack, entry_obj);
|
||||||
|
}
|
||||||
|
} catch (Json::JsonException& e) {
|
||||||
|
qDebug() << e.cause();
|
||||||
|
qDebug() << doc;
|
||||||
|
}
|
||||||
|
step_progress->state = TaskStepState::Succeeded;
|
||||||
|
stepProgress(*step_progress);
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
});
|
||||||
|
|
||||||
|
connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) {
|
||||||
|
step_progress->state = TaskStepState::Failed;
|
||||||
|
stepProgress(*step_progress);
|
||||||
|
emitFailed(reason);
|
||||||
|
});
|
||||||
|
connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||||
|
connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||||
|
qDebug() << "Resolve slug progress" << current << total;
|
||||||
|
step_progress->update(current, total);
|
||||||
|
stepProgress(*step_progress);
|
||||||
|
});
|
||||||
|
connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) {
|
||||||
|
step_progress->status = status;
|
||||||
|
stepProgress(*step_progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_task->start();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2024 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
#include "PackManifest.h"
|
#include "PackManifest.h"
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
namespace Flame {
|
namespace Flame {
|
||||||
@ -9,12 +27,12 @@ class FileResolvingTask : public Task {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess);
|
explicit FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess);
|
||||||
virtual ~FileResolvingTask() {};
|
virtual ~FileResolvingTask() = default;
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
bool canAbort() const override { return true; }
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
|
|
||||||
const Flame::Manifest& getResults() const { return m_toProcess; }
|
const Flame::Manifest& getResults() const { return m_manifest; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
@ -22,16 +40,13 @@ class FileResolvingTask : public Task {
|
|||||||
protected slots:
|
protected slots:
|
||||||
void netJobFinished();
|
void netJobFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void getFlameProjects();
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
Flame::Manifest m_toProcess;
|
Flame::Manifest m_manifest;
|
||||||
std::shared_ptr<QByteArray> result;
|
std::shared_ptr<QByteArray> m_result;
|
||||||
NetJob::Ptr m_dljob;
|
Task::Ptr m_task;
|
||||||
NetJob::Ptr m_checkJob;
|
|
||||||
NetJob::Ptr m_slugJob;
|
|
||||||
|
|
||||||
void modrinthCheckFinished();
|
|
||||||
|
|
||||||
QMap<File*, std::shared_ptr<QByteArray>> blockedProjects;
|
|
||||||
};
|
};
|
||||||
} // namespace Flame
|
} // namespace Flame
|
||||||
|
@ -35,8 +35,11 @@
|
|||||||
|
|
||||||
#include "FlameInstanceCreationTask.h"
|
#include "FlameInstanceCreationTask.h"
|
||||||
|
|
||||||
|
#include "QObjectPtr.h"
|
||||||
|
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||||
#include "modplatform/flame/FileResolvingTask.h"
|
#include "modplatform/flame/FileResolvingTask.h"
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
#include "modplatform/flame/FlameModIndex.h"
|
||||||
#include "modplatform/flame/PackManifest.h"
|
#include "modplatform/flame/PackManifest.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
@ -51,6 +54,7 @@
|
|||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
|
#include "tasks/ConcurrentTask.h"
|
||||||
#include "ui/dialogs/BlockedModsDialog.h"
|
#include "ui/dialogs/BlockedModsDialog.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
@ -58,7 +62,6 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/VersionList.h"
|
|
||||||
#include "minecraft/World.h"
|
#include "minecraft/World.h"
|
||||||
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
@ -208,8 +211,7 @@ bool FlameCreationTask::updateInstance()
|
|||||||
|
|
||||||
Flame::File file;
|
Flame::File file;
|
||||||
// We don't care about blocked mods, we just need local data to delete the file
|
// We don't care about blocked mods, we just need local data to delete the file
|
||||||
file.parseFromObject(entry_obj, false);
|
file.version = FlameMod::loadIndexedPackVersion(entry_obj);
|
||||||
|
|
||||||
auto id = Json::requireInteger(entry_obj, "id");
|
auto id = Json::requireInteger(entry_obj, "id");
|
||||||
old_files.insert(id, file);
|
old_files.insert(id, file);
|
||||||
}
|
}
|
||||||
@ -219,10 +221,10 @@ bool FlameCreationTask::updateInstance()
|
|||||||
|
|
||||||
// Delete the files
|
// Delete the files
|
||||||
for (auto& file : old_files) {
|
for (auto& file : old_files) {
|
||||||
if (file.fileName.isEmpty() || file.targetFolder.isEmpty())
|
if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QString relative_path(FS::PathCombine(file.targetFolder, file.fileName));
|
QString relative_path(FS::PathCombine(file.targetFolder, file.version.fileName));
|
||||||
qDebug() << "Scheduling" << relative_path << "for removal";
|
qDebug() << "Scheduling" << relative_path << "for removal";
|
||||||
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
|
m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path));
|
||||||
}
|
}
|
||||||
@ -471,15 +473,15 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||||||
QList<BlockedMod> blocked_mods;
|
QList<BlockedMod> blocked_mods;
|
||||||
auto anyBlocked = false;
|
auto anyBlocked = false;
|
||||||
for (const auto& result : results.files.values()) {
|
for (const auto& result : results.files.values()) {
|
||||||
if (result.fileName.endsWith(".zip")) {
|
if (result.version.fileName.endsWith(".zip")) {
|
||||||
m_ZIP_resources.append(std::make_pair(result.fileName, result.targetFolder));
|
m_ZIP_resources.append(std::make_pair(result.version.fileName, result.targetFolder));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.resolved || result.url.isEmpty()) {
|
if (result.version.downloadUrl.isEmpty()) {
|
||||||
BlockedMod blocked_mod;
|
BlockedMod blocked_mod;
|
||||||
blocked_mod.name = result.fileName;
|
blocked_mod.name = result.version.fileName;
|
||||||
blocked_mod.websiteUrl = result.websiteUrl;
|
blocked_mod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId));
|
||||||
blocked_mod.hash = result.hash;
|
blocked_mod.hash = result.version.hash;
|
||||||
blocked_mod.matched = false;
|
blocked_mod.matched = false;
|
||||||
blocked_mod.localPath = "";
|
blocked_mod.localPath = "";
|
||||||
blocked_mod.targetFolder = result.targetFolder;
|
blocked_mod.targetFolder = result.targetFolder;
|
||||||
@ -521,7 +523,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
QStringList optionalFiles;
|
QStringList optionalFiles;
|
||||||
for (auto& result : results) {
|
for (auto& result : results) {
|
||||||
if (!result.required) {
|
if (!result.required) {
|
||||||
optionalFiles << FS::PathCombine(result.targetFolder, result.fileName);
|
optionalFiles << FS::PathCombine(result.targetFolder, result.version.fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,7 +539,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
selectedOptionalMods = optionalModDialog.getResult();
|
selectedOptionalMods = optionalModDialog.getResult();
|
||||||
}
|
}
|
||||||
for (const auto& result : results) {
|
for (const auto& result : results) {
|
||||||
auto fileName = result.fileName;
|
auto fileName = result.version.fileName;
|
||||||
fileName = FS::RemoveInvalidPathChars(fileName);
|
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||||
auto relpath = FS::PathCombine(result.targetFolder, fileName);
|
auto relpath = FS::PathCombine(result.targetFolder, fileName);
|
||||||
|
|
||||||
@ -548,36 +550,16 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
relpath = FS::PathCombine("minecraft", relpath);
|
relpath = FS::PathCombine("minecraft", relpath);
|
||||||
auto path = FS::PathCombine(m_stagingPath, relpath);
|
auto path = FS::PathCombine(m_stagingPath, relpath);
|
||||||
|
|
||||||
switch (result.type) {
|
if (!result.version.downloadUrl.isEmpty()) {
|
||||||
case Flame::File::Type::Folder: {
|
qDebug() << "Will download" << result.version.downloadUrl << "to" << path;
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
auto dl = Net::ApiDownload::makeFile(result.version.downloadUrl, path);
|
||||||
// fallthrough intentional, we treat these as plain old mods and dump them wherever.
|
|
||||||
}
|
|
||||||
/* fallthrough */
|
|
||||||
case Flame::File::Type::SingleFile:
|
|
||||||
case Flame::File::Type::Mod: {
|
|
||||||
if (!result.url.isEmpty()) {
|
|
||||||
qDebug() << "Will download" << result.url << "to" << path;
|
|
||||||
auto dl = Net::ApiDownload::makeFile(result.url, path);
|
|
||||||
m_files_job->addNetAction(dl);
|
m_files_job->addNetAction(dl);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Flame::File::Type::Modpack:
|
|
||||||
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
case Flame::File::Type::Cmod2:
|
|
||||||
case Flame::File::Type::Ctoc:
|
|
||||||
case Flame::File::Type::Unknown:
|
|
||||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mod_id_resolver.reset();
|
connect(m_files_job.get(), &NetJob::finished, this, [this, &loop]() {
|
||||||
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() {
|
|
||||||
m_files_job.reset();
|
m_files_job.reset();
|
||||||
validateZIPResources();
|
validateZIPResources(loop);
|
||||||
});
|
});
|
||||||
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
|
connect(m_files_job.get(), &NetJob::failed, [&](QString reason) {
|
||||||
m_files_job.reset();
|
m_files_job.reset();
|
||||||
@ -588,7 +570,6 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
setProgress(current, total);
|
setProgress(current, total);
|
||||||
});
|
});
|
||||||
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress);
|
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress);
|
||||||
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
|
||||||
|
|
||||||
setStatus(tr("Downloading mods..."));
|
setStatus(tr("Downloading mods..."));
|
||||||
m_files_job->start();
|
m_files_job->start();
|
||||||
@ -626,9 +607,10 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
|||||||
setAbortable(true);
|
setAbortable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlameCreationTask::validateZIPResources()
|
void FlameCreationTask::validateZIPResources(QEventLoop& loop)
|
||||||
{
|
{
|
||||||
qDebug() << "Validating whether resources stored as .zip are in the right place";
|
qDebug() << "Validating whether resources stored as .zip are in the right place";
|
||||||
|
QStringList zipMods;
|
||||||
for (auto [fileName, targetFolder] : m_ZIP_resources) {
|
for (auto [fileName, targetFolder] : m_ZIP_resources) {
|
||||||
qDebug() << "Checking" << fileName << "...";
|
qDebug() << "Checking" << fileName << "...";
|
||||||
auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName);
|
auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName);
|
||||||
@ -668,6 +650,7 @@ void FlameCreationTask::validateZIPResources()
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case PackedResourceType::Mod:
|
case PackedResourceType::Mod:
|
||||||
validatePath(fileName, targetFolder, "mods");
|
validatePath(fileName, targetFolder, "mods");
|
||||||
|
zipMods.push_back(fileName);
|
||||||
break;
|
break;
|
||||||
case PackedResourceType::ResourcePack:
|
case PackedResourceType::ResourcePack:
|
||||||
validatePath(fileName, targetFolder, "resourcepacks");
|
validatePath(fileName, targetFolder, "resourcepacks");
|
||||||
@ -693,4 +676,16 @@ void FlameCreationTask::validateZIPResources()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto task = makeShared<ConcurrentTask>(this, "CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||||
|
auto results = m_mod_id_resolver->getResults().files;
|
||||||
|
auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index");
|
||||||
|
for (auto file : results) {
|
||||||
|
if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
task->addTask(makeShared<LocalModUpdateTask>(folder, file.pack, file.version));
|
||||||
|
}
|
||||||
|
connect(task.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||||
|
m_process_update_file_info_job = task;
|
||||||
|
task->start();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
|||||||
void idResolverSucceeded(QEventLoop&);
|
void idResolverSucceeded(QEventLoop&);
|
||||||
void setupDownloadJob(QEventLoop&);
|
void setupDownloadJob(QEventLoop&);
|
||||||
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||||
void validateZIPResources();
|
void validateZIPResources(QEventLoop& loop);
|
||||||
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -68,35 +68,3 @@ void Flame::loadManifest(Flame::Manifest& m, const QString& filepath)
|
|||||||
}
|
}
|
||||||
loadManifestV1(m, obj);
|
loadManifestV1(m, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked)
|
|
||||||
{
|
|
||||||
fileName = Json::requireString(obj, "fileName");
|
|
||||||
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
|
|
||||||
// It is also optional
|
|
||||||
type = File::Type::SingleFile;
|
|
||||||
|
|
||||||
targetFolder = "mods";
|
|
||||||
|
|
||||||
// get the hash
|
|
||||||
hash = QString();
|
|
||||||
auto hashes = Json::ensureArray(obj, "hashes");
|
|
||||||
for (QJsonValueRef item : hashes) {
|
|
||||||
auto hobj = Json::requireObject(item);
|
|
||||||
auto algo = Json::requireInteger(hobj, "algo");
|
|
||||||
auto value = Json::requireString(hobj, "value");
|
|
||||||
if (algo == 1) {
|
|
||||||
hash = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// may throw, if the project is blocked
|
|
||||||
QString rawUrl = Json::ensureString(obj, "downloadUrl");
|
|
||||||
url = QUrl(rawUrl, QUrl::TolerantMode);
|
|
||||||
if (!url.isValid() && throw_on_blocked) {
|
|
||||||
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
resolved = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
@ -40,26 +40,20 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
namespace Flame {
|
namespace Flame {
|
||||||
struct File {
|
struct File {
|
||||||
// NOTE: throws JSONValidationError
|
|
||||||
bool parseFromObject(const QJsonObject& object, bool throw_on_blocked = true);
|
|
||||||
|
|
||||||
int projectId = 0;
|
int projectId = 0;
|
||||||
int fileId = 0;
|
int fileId = 0;
|
||||||
// NOTE: the opposite to 'optional'
|
// NOTE: the opposite to 'optional'
|
||||||
bool required = true;
|
bool required = true;
|
||||||
QString hash;
|
|
||||||
// NOTE: only set on blocked files ! Empty otherwise.
|
ModPlatform::IndexedPack pack;
|
||||||
QString websiteUrl;
|
ModPlatform::IndexedVersion version;
|
||||||
|
|
||||||
// our
|
// our
|
||||||
bool resolved = false;
|
|
||||||
QString fileName;
|
|
||||||
QUrl url;
|
|
||||||
QString targetFolder = QStringLiteral("mods");
|
QString targetFolder = QStringLiteral("mods");
|
||||||
enum class Type { Unknown, Folder, Ctoc, SingleFile, Cmod2, Modpack, Mod } type = Type::Mod;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Modloader {
|
struct Modloader {
|
||||||
|
@ -5,8 +5,12 @@
|
|||||||
#include "InstanceList.h"
|
#include "InstanceList.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
|
#include "QObjectPtr.h"
|
||||||
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
|
|
||||||
|
#include "minecraft/mod/Mod.h"
|
||||||
|
#include "modplatform/EnsureMetadataTask.h"
|
||||||
#include "modplatform/helpers/OverrideUtils.h"
|
#include "modplatform/helpers/OverrideUtils.h"
|
||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||||
@ -21,6 +25,7 @@
|
|||||||
|
|
||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QHash>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
bool ModrinthCreationTask::abort()
|
bool ModrinthCreationTask::abort()
|
||||||
@ -29,8 +34,8 @@ bool ModrinthCreationTask::abort()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_abort = true;
|
m_abort = true;
|
||||||
if (m_files_job)
|
if (m_task)
|
||||||
m_files_job->abort();
|
m_task->abort();
|
||||||
return Task::abort();
|
return Task::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,11 +239,11 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
instance.setName(name());
|
instance.setName(name());
|
||||||
instance.saveNow();
|
instance.saveNow();
|
||||||
|
|
||||||
m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network()));
|
auto downloadMods = makeShared<NetJob>(tr("Mod Download Modrinth"), APPLICATION->network());
|
||||||
|
|
||||||
auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path);
|
auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path);
|
||||||
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
||||||
|
QHash<QString, Mod*> mods;
|
||||||
for (auto file : m_files) {
|
for (auto file : m_files) {
|
||||||
auto fileName = file.path;
|
auto fileName = file.path;
|
||||||
fileName = FS::RemoveInvalidPathChars(fileName);
|
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||||
@ -249,20 +254,27 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
.arg(fileName));
|
.arg(fileName));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (fileName.startsWith("mods/")) {
|
||||||
|
auto mod = new Mod(file_path);
|
||||||
|
ModDetails d;
|
||||||
|
d.mod_id = file_path;
|
||||||
|
mod->setDetails(d);
|
||||||
|
mods[file.hash.toHex()] = mod;
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
|
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
|
||||||
auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
|
auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
|
||||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
m_files_job->addNetAction(dl);
|
downloadMods->addNetAction(dl);
|
||||||
|
|
||||||
if (!file.downloads.empty()) {
|
if (!file.downloads.empty()) {
|
||||||
// FIXME: This really needs to be put into a ConcurrentTask of
|
// FIXME: This really needs to be put into a ConcurrentTask of
|
||||||
// MultipleOptionsTask's , once those exist :)
|
// MultipleOptionsTask's , once those exist :)
|
||||||
auto param = dl.toWeakRef();
|
auto param = dl.toWeakRef();
|
||||||
connect(dl.get(), &Task::failed, [this, &file, file_path, param] {
|
connect(dl.get(), &Task::failed, [&file, file_path, param, downloadMods] {
|
||||||
auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
|
auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
|
||||||
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||||
m_files_job->addNetAction(ndl);
|
downloadMods->addNetAction(ndl);
|
||||||
if (auto shared = param.lock())
|
if (auto shared = param.lock())
|
||||||
shared->succeeded();
|
shared->succeeded();
|
||||||
});
|
});
|
||||||
@ -271,23 +283,44 @@ bool ModrinthCreationTask::createInstance()
|
|||||||
|
|
||||||
bool ended_well = false;
|
bool ended_well = false;
|
||||||
|
|
||||||
connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { ended_well = true; });
|
connect(downloadMods.get(), &NetJob::succeeded, this, [&]() { ended_well = true; });
|
||||||
connect(m_files_job.get(), &NetJob::failed, [&](const QString& reason) {
|
connect(downloadMods.get(), &NetJob::failed, [&](const QString& reason) {
|
||||||
ended_well = false;
|
ended_well = false;
|
||||||
setError(reason);
|
setError(reason);
|
||||||
});
|
});
|
||||||
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
||||||
connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
connect(downloadMods.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||||
setProgress(current, total);
|
setProgress(current, total);
|
||||||
});
|
});
|
||||||
connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
|
connect(downloadMods.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
|
||||||
|
|
||||||
setStatus(tr("Downloading mods..."));
|
setStatus(tr("Downloading mods..."));
|
||||||
m_files_job->start();
|
downloadMods->start();
|
||||||
|
m_task = downloadMods;
|
||||||
|
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
|
QEventLoop ensureMetaLoop;
|
||||||
|
QDir folder = FS::PathCombine(instance.modsRoot(), ".index");
|
||||||
|
auto ensureMetadataTask = makeShared<EnsureMetadataTask>(mods, folder, ModPlatform::ResourceProvider::MODRINTH);
|
||||||
|
connect(ensureMetadataTask.get(), &Task::succeeded, this, [&]() { ended_well = true; });
|
||||||
|
connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit);
|
||||||
|
connect(ensureMetadataTask.get(), &Task::progress, [&](qint64 current, qint64 total) {
|
||||||
|
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||||
|
setProgress(current, total);
|
||||||
|
});
|
||||||
|
connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
|
||||||
|
|
||||||
|
ensureMetadataTask->start();
|
||||||
|
m_task = ensureMetadataTask;
|
||||||
|
|
||||||
|
ensureMetaLoop.exec();
|
||||||
|
for (auto m : mods) {
|
||||||
|
delete m;
|
||||||
|
}
|
||||||
|
mods.clear();
|
||||||
|
|
||||||
// Update information of the already installed instance, if any.
|
// Update information of the already installed instance, if any.
|
||||||
if (m_instance && ended_well) {
|
if (m_instance && ended_well) {
|
||||||
setAbortable(false);
|
setAbortable(false);
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include "BaseInstance.h"
|
||||||
#include "InstanceCreationTask.h"
|
#include "InstanceCreationTask.h"
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
|
|
||||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||||
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
|
|
||||||
class ModrinthCreationTask final : public InstanceCreationTask {
|
class ModrinthCreationTask final : public InstanceCreationTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -43,7 +39,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
|||||||
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;
|
||||||
NetJob::Ptr m_files_job;
|
Task::Ptr m_task;
|
||||||
|
|
||||||
std::optional<InstancePtr> m_instance;
|
std::optional<InstancePtr> m_instance;
|
||||||
|
|
||||||
|
@ -208,9 +208,9 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
|||||||
auto tbl = toml::table{ { "name", mod.name.toStdString() },
|
auto tbl = toml::table{ { "name", mod.name.toStdString() },
|
||||||
{ "filename", mod.filename.toStdString() },
|
{ "filename", mod.filename.toStdString() },
|
||||||
{ "side", sideToString(mod.side).toStdString() },
|
{ "side", sideToString(mod.side).toStdString() },
|
||||||
{ "loaders", loaders },
|
{ "x-prismlauncher-loaders", loaders },
|
||||||
{ "mcVersions", mcVersions },
|
{ "x-prismlauncher-mc-versions", mcVersions },
|
||||||
{ "releaseType", mod.releaseType.toString().toStdString() },
|
{ "x-prismlauncher-release-type", mod.releaseType.toString().toStdString() },
|
||||||
{ "download",
|
{ "download",
|
||||||
toml::table{
|
toml::table{
|
||||||
{ "mode", mod.mode.toStdString() },
|
{ "mode", mod.mode.toStdString() },
|
||||||
@ -295,15 +295,15 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
|||||||
mod.name = stringEntry(table, "name");
|
mod.name = stringEntry(table, "name");
|
||||||
mod.filename = stringEntry(table, "filename");
|
mod.filename = stringEntry(table, "filename");
|
||||||
mod.side = stringToSide(stringEntry(table, "side"));
|
mod.side = stringToSide(stringEntry(table, "side"));
|
||||||
mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "releaseType"));
|
mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "x-prismlauncher-release-type"));
|
||||||
if (auto loaders = table["loaders"]; loaders && loaders.is_array()) {
|
if (auto loaders = table["x-prismlauncher-loaders"]; loaders && loaders.is_array()) {
|
||||||
for (auto&& loader : *loaders.as_array()) {
|
for (auto&& loader : *loaders.as_array()) {
|
||||||
if (loader.is_string()) {
|
if (loader.is_string()) {
|
||||||
mod.loaders |= ModPlatform::getModLoaderFromString(QString::fromStdString(loader.as_string()->value_or("")));
|
mod.loaders |= ModPlatform::getModLoaderFromString(QString::fromStdString(loader.as_string()->value_or("")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto versions = table["mcVersions"]; versions && versions.is_array()) {
|
if (auto versions = table["x-prismlauncher-mc-versions"]; versions && versions.is_array()) {
|
||||||
for (auto&& version : *versions.as_array()) {
|
for (auto&& version : *versions.as_array()) {
|
||||||
if (version.is_string()) {
|
if (version.is_string()) {
|
||||||
auto ver = QString::fromStdString(version.as_string()->value_or(""));
|
auto ver = QString::fromStdString(version.as_string()->value_or(""));
|
||||||
|
@ -550,7 +550,7 @@ void TranslationsModel::downloadIndex()
|
|||||||
d->m_index_job.reset(new NetJob("Translations Index", APPLICATION->network()));
|
d->m_index_job.reset(new NetJob("Translations Index", APPLICATION->network()));
|
||||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json");
|
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json");
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
auto task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + "index_v2.json"), entry);
|
auto task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + "index_v2.json"), entry);
|
||||||
d->m_index_task = task.get();
|
d->m_index_task = task.get();
|
||||||
d->m_index_job->addNetAction(task);
|
d->m_index_job->addNetAction(task);
|
||||||
d->m_index_job->setAskRetry(false);
|
d->m_index_job->setAskRetry(false);
|
||||||
@ -591,7 +591,7 @@ void TranslationsModel::downloadTranslation(QString key)
|
|||||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "mmc_" + key + ".qm");
|
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "mmc_" + key + ".qm");
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
|
|
||||||
auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + lang->file_name), entry);
|
auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + lang->file_name), entry);
|
||||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->file_sha1));
|
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->file_sha1));
|
||||||
dl->setProgress(dl->getProgress(), lang->file_size);
|
dl->setProgress(dl->getProgress(), lang->file_size);
|
||||||
|
|
||||||
|
@ -233,6 +233,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
|
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
|
||||||
ui->mainToolBar->addAction(ui->actionCloseWindow);
|
ui->mainToolBar->addAction(ui->actionCloseWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui->actionViewJavaFolder->setEnabled(BuildConfig.JAVA_DOWNLOADER_ENABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the toolbar toggles to the view menu
|
// add the toolbar toggles to the view menu
|
||||||
@ -1223,6 +1225,11 @@ void MainWindow::on_actionViewLogsFolder_triggered()
|
|||||||
DesktopServices::openPath("logs", true);
|
DesktopServices::openPath("logs", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionViewJavaFolder_triggered()
|
||||||
|
{
|
||||||
|
DesktopServices::openPath(APPLICATION->javaPath(), true);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::refreshInstances()
|
void MainWindow::refreshInstances()
|
||||||
{
|
{
|
||||||
APPLICATION->instances()->loadList();
|
APPLICATION->instances()->loadList();
|
||||||
|
@ -48,7 +48,6 @@
|
|||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "minecraft/auth/MinecraftAccount.h"
|
#include "minecraft/auth/MinecraftAccount.h"
|
||||||
#include "net/NetJob.h"
|
|
||||||
|
|
||||||
class LaunchController;
|
class LaunchController;
|
||||||
class NewsChecker;
|
class NewsChecker;
|
||||||
@ -119,6 +118,7 @@ class MainWindow : public QMainWindow {
|
|||||||
void on_actionViewCatPackFolder_triggered();
|
void on_actionViewCatPackFolder_triggered();
|
||||||
void on_actionViewIconsFolder_triggered();
|
void on_actionViewIconsFolder_triggered();
|
||||||
void on_actionViewLogsFolder_triggered();
|
void on_actionViewLogsFolder_triggered();
|
||||||
|
void on_actionViewJavaFolder_triggered();
|
||||||
|
|
||||||
void on_actionViewSkinsFolder_triggered();
|
void on_actionViewSkinsFolder_triggered();
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>800</width>
|
<width>800</width>
|
||||||
<height>22</height>
|
<height>27</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="fileMenu">
|
<widget class="QMenu" name="fileMenu">
|
||||||
@ -192,6 +192,7 @@
|
|||||||
<addaction name="actionViewInstanceFolder"/>
|
<addaction name="actionViewInstanceFolder"/>
|
||||||
<addaction name="actionViewCentralModsFolder"/>
|
<addaction name="actionViewCentralModsFolder"/>
|
||||||
<addaction name="actionViewSkinsFolder"/>
|
<addaction name="actionViewSkinsFolder"/>
|
||||||
|
<addaction name="actionViewJavaFolder"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionViewIconThemeFolder"/>
|
<addaction name="actionViewIconThemeFolder"/>
|
||||||
<addaction name="actionViewWidgetThemeFolder"/>
|
<addaction name="actionViewWidgetThemeFolder"/>
|
||||||
@ -788,6 +789,18 @@
|
|||||||
<string>Open the cat packs folder in a file browser.</string>
|
<string>Open the cat packs folder in a file browser.</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionViewJavaFolder">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="viewfolder">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Java</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Open the Java folder in a file browser. Only available if the built-in Java downloader is used.</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -164,7 +164,12 @@ void ExportToModListDialog::done(int result)
|
|||||||
|
|
||||||
if (output.isEmpty())
|
if (output.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
FS::write(output, ui->finalText->toPlainText().toUtf8());
|
FS::write(output, ui->finalText->toPlainText().toUtf8());
|
||||||
|
} catch (const FS::FileSystemException& e) {
|
||||||
|
qCritical() << "Failed to save mod list file :" << e.cause();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialog::done(result);
|
QDialog::done(result);
|
||||||
|
@ -116,7 +116,7 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, QItemSelection
|
|||||||
return;
|
return;
|
||||||
m_selected_skin = key;
|
m_selected_skin = key;
|
||||||
auto skin = m_list.skin(key);
|
auto skin = m_list.skin(key);
|
||||||
if (!skin)
|
if (!skin || !skin->isValid())
|
||||||
return;
|
return;
|
||||||
ui->selectedModel->setPixmap(skin->getTexture().scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation));
|
ui->selectedModel->setPixmap(skin->getTexture().scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
ui->capeCombo->setCurrentIndex(m_capes_idx.value(skin->getCapeId()));
|
ui->capeCombo->setCurrentIndex(m_capes_idx.value(skin->getCapeId()));
|
||||||
@ -212,7 +212,10 @@ void SkinManageDialog::setupCapes()
|
|||||||
void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
auto id = ui->capeCombo->currentData();
|
auto id = ui->capeCombo->currentData();
|
||||||
ui->capeImage->setPixmap(m_capes.value(id.toString(), {}).scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation));
|
auto cape = m_capes.value(id.toString(), {});
|
||||||
|
if (!cape.isNull()) {
|
||||||
|
ui->capeImage->setPixmap(cape.scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
|
}
|
||||||
if (auto skin = m_list.skin(m_selected_skin); skin) {
|
if (auto skin = m_list.skin(m_selected_skin); skin) {
|
||||||
skin->setCapeId(id.toString());
|
skin->setCapeId(id.toString());
|
||||||
}
|
}
|
||||||
@ -505,8 +508,13 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event)
|
|||||||
QSize s = size() * (1. / 3);
|
QSize s = size() * (1. / 3);
|
||||||
|
|
||||||
if (auto skin = m_list.skin(m_selected_skin); skin) {
|
if (auto skin = m_list.skin(m_selected_skin); skin) {
|
||||||
|
if (skin->isValid()) {
|
||||||
ui->selectedModel->setPixmap(skin->getTexture().scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation));
|
ui->selectedModel->setPixmap(skin->getTexture().scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
}
|
}
|
||||||
auto id = ui->capeCombo->currentData();
|
}
|
||||||
ui->capeImage->setPixmap(m_capes.value(id.toString(), {}).scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation));
|
auto id = ui->capeCombo->currentData();
|
||||||
|
auto cape = m_capes.value(id.toString(), {});
|
||||||
|
if (!cape.isNull()) {
|
||||||
|
ui->capeImage->setPixmap(cape.scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,12 @@
|
|||||||
#include "Filter.h"
|
#include "Filter.h"
|
||||||
#include "java/download/ArchiveDownloadTask.h"
|
#include "java/download/ArchiveDownloadTask.h"
|
||||||
#include "java/download/ManifestDownloadTask.h"
|
#include "java/download/ManifestDownloadTask.h"
|
||||||
|
#include "java/download/SymlinkTask.h"
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/VersionList.h"
|
#include "meta/VersionList.h"
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui/java/VersionList.h"
|
#include "ui/java/VersionList.h"
|
||||||
@ -55,13 +57,13 @@ class InstallJavaPage : public QWidget, public BasePage {
|
|||||||
|
|
||||||
majorVersionSelect = new VersionSelectWidget(this);
|
majorVersionSelect = new VersionSelectWidget(this);
|
||||||
majorVersionSelect->selectCurrent();
|
majorVersionSelect->selectCurrent();
|
||||||
majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta."));
|
majorVersionSelect->setEmptyString(tr("No Java versions are currently available in the meta."));
|
||||||
majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!"));
|
majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!"));
|
||||||
horizontalLayout->addWidget(majorVersionSelect, 1);
|
horizontalLayout->addWidget(majorVersionSelect, 1);
|
||||||
|
|
||||||
javaVersionSelect = new VersionSelectWidget(this);
|
javaVersionSelect = new VersionSelectWidget(this);
|
||||||
javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS."));
|
javaVersionSelect->setEmptyString(tr("No Java versions are currently available for your OS."));
|
||||||
javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!"));
|
javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!"));
|
||||||
horizontalLayout->addWidget(javaVersionSelect, 4);
|
horizontalLayout->addWidget(javaVersionSelect, 4);
|
||||||
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion);
|
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion);
|
||||||
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
|
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
|
||||||
@ -313,6 +315,12 @@ void InstallDialog::done(int result)
|
|||||||
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
||||||
deletePath();
|
deletePath();
|
||||||
}
|
}
|
||||||
|
#if defined(Q_OS_MACOS)
|
||||||
|
auto seq = makeShared<SequentialTask>(this, tr("Install Java"));
|
||||||
|
seq->addTask(task);
|
||||||
|
seq->addTask(makeShared<Java::SymlinkTask>(final_path));
|
||||||
|
task = seq;
|
||||||
|
#endif
|
||||||
connect(task.get(), &Task::failed, this, [this, &deletePath](QString reason) {
|
connect(task.get(), &Task::failed, this, [this, &deletePath](QString reason) {
|
||||||
QString error = QString("Java download failed: %1").arg(reason);
|
QString error = QString("Java download failed: %1").arg(reason);
|
||||||
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
||||||
|
@ -143,6 +143,7 @@ void APIPage::loadSettings()
|
|||||||
ui->modrinthToken->setText(modrinthToken);
|
ui->modrinthToken->setText(modrinthToken);
|
||||||
QString customUserAgent = s->get("UserAgentOverride").toString();
|
QString customUserAgent = s->get("UserAgentOverride").toString();
|
||||||
ui->userAgentLineEdit->setText(customUserAgent);
|
ui->userAgentLineEdit->setText(customUserAgent);
|
||||||
|
ui->technicClientID->setText(s->get("TechnicClientID").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIPage::applySettings()
|
void APIPage::applySettings()
|
||||||
@ -172,6 +173,7 @@ void APIPage::applySettings()
|
|||||||
QString modrinthToken = ui->modrinthToken->text();
|
QString modrinthToken = ui->modrinthToken->text();
|
||||||
s->set("ModrinthToken", modrinthToken);
|
s->set("ModrinthToken", modrinthToken);
|
||||||
s->set("UserAgentOverride", ui->userAgentLineEdit->text());
|
s->set("UserAgentOverride", ui->userAgentLineEdit->text());
|
||||||
|
s->set("TechnicClientID", ui->technicClientID->text());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APIPage::apply()
|
bool APIPage::apply()
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>800</width>
|
<width>841</width>
|
||||||
<height>600</height>
|
<height>620</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
@ -288,6 +288,36 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Technic Client ID</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>Note: you only need to set this to access private data.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="technicClientID">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>(None)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enter a custom GUID client ID for Technic here.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -67,8 +67,8 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage)
|
|||||||
ui->managedJavaList->initialize(new JavaInstallList(this, true));
|
ui->managedJavaList->initialize(new JavaInstallList(this, true));
|
||||||
ui->managedJavaList->setResizeOn(2);
|
ui->managedJavaList->setResizeOn(2);
|
||||||
ui->managedJavaList->selectCurrent();
|
ui->managedJavaList->selectCurrent();
|
||||||
ui->managedJavaList->setEmptyString(tr("No managed java versions are installed"));
|
ui->managedJavaList->setEmptyString(tr("No managed Java versions are installed"));
|
||||||
ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed java list!"));
|
ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed Java list!"));
|
||||||
connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] {
|
connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] {
|
||||||
ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked());
|
ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked());
|
||||||
if (!ui->autodetectJavaCheckBox->isChecked())
|
if (!ui->autodetectJavaCheckBox->isChecked())
|
||||||
|
@ -234,7 +234,7 @@ bool LogPage::apply()
|
|||||||
|
|
||||||
bool LogPage::shouldDisplay() const
|
bool LogPage::shouldDisplay() const
|
||||||
{
|
{
|
||||||
return m_instance->isRunning() || m_proxy->rowCount() > 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogPage::on_btnPaste_clicked()
|
void LogPage::on_btnPaste_clicked()
|
||||||
|
@ -154,6 +154,10 @@ void Technic::ListModel::performSearch()
|
|||||||
QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
|
QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
|
||||||
searchMode = List;
|
searchMode = List;
|
||||||
}
|
}
|
||||||
|
auto clientId = APPLICATION->settings()->get("TechnicClientID").toString();
|
||||||
|
if (!clientId.isEmpty()) {
|
||||||
|
searchUrl += "?cid=" + clientId;
|
||||||
|
}
|
||||||
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response));
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response));
|
||||||
jobPtr = netJob;
|
jobPtr = netJob;
|
||||||
jobPtr->start();
|
jobPtr->start();
|
||||||
|
@ -83,6 +83,6 @@ void JavaWizardPage::retranslate()
|
|||||||
{
|
{
|
||||||
setTitle(tr("Java"));
|
setTitle(tr("Java"));
|
||||||
setSubTitle(
|
setSubTitle(
|
||||||
tr("Please select how much memory to allocate to instances and if Prism Launcher should manage java automatically or manually."));
|
tr("Please select how much memory to allocate to instances and if Prism Launcher should manage Java automatically or manually."));
|
||||||
m_java_widget->retranslate();
|
m_java_widget->retranslate();
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,10 @@ void LoginWizardPage::on_pushButton_clicked()
|
|||||||
if (account) {
|
if (account) {
|
||||||
APPLICATION->accounts()->addAccount(account);
|
APPLICATION->accounts()->addAccount(account);
|
||||||
APPLICATION->accounts()->setDefaultAccount(account);
|
APPLICATION->accounts()->setDefaultAccount(account);
|
||||||
}
|
|
||||||
|
|
||||||
if (wizard()->currentId() == wizard()->pageIds().last()) {
|
if (wizard()->currentId() == wizard()->pageIds().last()) {
|
||||||
wizard()->accept();
|
wizard()->accept();
|
||||||
} else {
|
} else {
|
||||||
wizard()->next();
|
wizard()->next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QLoggingCategory>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "IconTheme.h"
|
#include "IconTheme.h"
|
||||||
#include "ui/MainWindow.h"
|
|
||||||
#include "ui/themes/CatPack.h"
|
#include "ui/themes/CatPack.h"
|
||||||
#include "ui/themes/ITheme.h"
|
#include "ui/themes/ITheme.h"
|
||||||
|
|
||||||
|
@ -1210,7 +1210,7 @@ std::optional<QDir> PrismUpdaterApp::unpackArchive(QFileInfo archive)
|
|||||||
QProcess proc = QProcess();
|
QProcess proc = QProcess();
|
||||||
proc.start(cmd, args);
|
proc.start(cmd, args);
|
||||||
if (!proc.waitForStarted(5000)) { // wait 5 seconds to start
|
if (!proc.waitForStarted(5000)) { // wait 5 seconds to start
|
||||||
auto msg = tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" "));
|
auto msg = tr("Failed to launch child process \"%1 %2\".").arg(cmd).arg(args.join(" "));
|
||||||
logUpdate(msg);
|
logUpdate(msg);
|
||||||
showFatalErrorMessage(tr("Failed extract archive"), msg);
|
showFatalErrorMessage(tr("Failed extract archive"), msg);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@ -1241,7 +1241,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path)
|
|||||||
proc.setReadChannel(QProcess::StandardOutput);
|
proc.setReadChannel(QProcess::StandardOutput);
|
||||||
proc.start(exe_path, { "--version" });
|
proc.start(exe_path, { "--version" });
|
||||||
if (!proc.waitForStarted(5000)) {
|
if (!proc.waitForStarted(5000)) {
|
||||||
showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version."));
|
showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launch child process to read version."));
|
||||||
return false;
|
return false;
|
||||||
} // wait 5 seconds to start
|
} // wait 5 seconds to start
|
||||||
if (!proc.waitForFinished(5000)) {
|
if (!proc.waitForFinished(5000)) {
|
||||||
|
@ -8,8 +8,8 @@ See [Package variants](#package-variants) for a list of available packages.
|
|||||||
|
|
||||||
## Installing a development release (flake)
|
## Installing a development release (flake)
|
||||||
|
|
||||||
We use [garnix](https://garnix.io/) to build and cache our development builds.
|
We use [cachix](https://cachix.org/) to cache our development and release builds.
|
||||||
If you want to avoid rebuilds you may add the garnix cache to your substitutors, or use `--accept-flake-config`
|
If you want to avoid rebuilds you may add the Cachix bucket to your substitutors, or use `--accept-flake-config`
|
||||||
to temporarily enable it when using `nix` commands.
|
to temporarily enable it when using `nix` commands.
|
||||||
|
|
||||||
Example (NixOS):
|
Example (NixOS):
|
||||||
@ -17,12 +17,10 @@ Example (NixOS):
|
|||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
nix.settings = {
|
nix.settings = {
|
||||||
trusted-substituters = [
|
trusted-substituters = [ "https://prismlauncher.cachix.org" ];
|
||||||
"https://cache.garnix.io"
|
|
||||||
];
|
|
||||||
|
|
||||||
trusted-public-keys = [
|
trusted-public-keys = [
|
||||||
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
|
"prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c="
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -137,20 +135,18 @@ nix profile install github:PrismLauncher/PrismLauncher
|
|||||||
|
|
||||||
## Installing a development release (without flakes)
|
## Installing a development release (without flakes)
|
||||||
|
|
||||||
We use [garnix](https://garnix.io/) to build and cache our development builds.
|
We use [Cachix](https://cachix.org/) to cache our development and release builds.
|
||||||
If you want to avoid rebuilds you may add the garnix cache to your substitutors.
|
If you want to avoid rebuilds you may add the Cachix bucket to your substitutors.
|
||||||
|
|
||||||
Example (NixOS):
|
Example (NixOS):
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
nix.settings = {
|
nix.settings = {
|
||||||
trusted-substituters = [
|
trusted-substituters = [ "https://prismlauncher.cachix.org" ];
|
||||||
"https://cache.garnix.io"
|
|
||||||
];
|
|
||||||
|
|
||||||
trusted-public-keys = [
|
trusted-public-keys = [
|
||||||
"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
|
"prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c="
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
cd ${self}
|
cd ${self}
|
||||||
|
|
||||||
echo "Running clang-format...."
|
echo "Running clang-format...."
|
||||||
clang-format -i --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp}
|
clang-format --dry-run --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp}
|
||||||
|
|
||||||
echo "Running deadnix..."
|
echo "Running deadnix..."
|
||||||
deadnix --fail
|
deadnix --fail
|
||||||
|
Loading…
Reference in New Issue
Block a user