From 1c91d2f24255272a5526d70ff06f8907f65012a6 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 30 May 2023 23:03:44 -0700 Subject: [PATCH 01/72] feat: paliminary updater - can check for need to update - can select a version to update to - perform update: TODO Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 +- launcher/CMakeLists.txt | 59 +- launcher/Version.h | 1 + launcher/filelink/FileLink.cpp | 3 + launcher/filelink/FileLink.h | 11 + launcher/filelink/main.cpp | 14 +- launcher/updater/windows/GitHubRelease.h | 34 + .../updater/windows/SelectReleaseDialog.ui | 89 +++ launcher/updater/windows/UpdaterDialogs.cpp | 78 +++ launcher/updater/windows/UpdaterDialogs.h | 33 + launcher/updater/windows/WindowsUpdater.cpp | 647 ++++++++++++++++++ launcher/updater/windows/WindowsUpdater.h | 110 +++ launcher/updater/windows/windows_main.cpp | 44 ++ 13 files changed, 1122 insertions(+), 3 deletions(-) create mode 100644 launcher/updater/windows/GitHubRelease.h create mode 100644 launcher/updater/windows/SelectReleaseDialog.ui create mode 100644 launcher/updater/windows/UpdaterDialogs.cpp create mode 100644 launcher/updater/windows/UpdaterDialogs.h create mode 100644 launcher/updater/windows/WindowsUpdater.cpp create mode 100644 launcher/updater/windows/WindowsUpdater.h create mode 100644 launcher/updater/windows/windows_main.cpp diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1659eb445..4b0ba368b 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -469,7 +469,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) { - qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT; + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 273b5449a..5a979b8d9 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -567,6 +567,25 @@ set(LINKEXE_SOURCES DesktopServices.cpp ) +set(WINDOWSUPDATEREXE_SOURCES + updater/windows/WindowsUpdater.h + updater/windows/WindowsUpdater.cpp + updater/windows/UpdaterDialogs.h + updater/windows/UpdaterDialogs.cpp + updater/windows/GitHubRelease.h + Json.h + Json.cpp + FileSystem.h + FileSystem.cpp + StringUtils.h + StringUtils.cpp + DesktopServices.h + DesktopServices.cpp + Version.h + Version.cpp + Markdown.h +) + ######## Logging categories ######## ecm_qt_declare_logging_category(CORE_SOURCES @@ -1075,6 +1094,10 @@ qt_add_resources(LAUNCHER_RESOURCES ../${Launcher_Branding_LogoQRC} ) +qt_wrap_ui(WINDOWSUPDATER_UI + updater/windows/SelectReleaseDialog.ui +) + ######## Windows resource files ######## if(WIN32) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) @@ -1158,7 +1181,41 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(WIN32) +if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) + # Updater + add_library(windows_updater_logic STATIC ${WINDOWSUPDATEREXE_SOURCES} ${WINDOWSUPDATER_UI}) + target_include_directories(windows_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(windows_updater_logic + systeminfo + BuildConfig + ghcFilesystem::ghc_filesystem + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Network + ${Launcher_QT_LIBS} + cmark::cmark + ) + + add_executable("${Launcher_Name}_updater" WIN32 updater/windows/windows_main.cpp) + target_link_libraries("${Launcher_Name}_updater" windows_updater_logic) + + if(DEFINED Launcher_APP_BINARY_NAME) + set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") + endif() + if(DEFINED Launcher_BINARY_RPATH) + SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}") + endif() + + install(TARGETS "${Launcher_Name}_updater" + BUNDLE DESTINATION "." COMPONENT Runtime + LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime + RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime + FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime + ) +endif() + +if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) + # File link add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(filelink_logic diff --git a/launcher/Version.h b/launcher/Version.h index 659f8e54e..1bba44126 100644 --- a/launcher/Version.h +++ b/launcher/Version.h @@ -56,6 +56,7 @@ class Version { bool operator!=(const Version &other) const; QString toString() const { return m_string; } + bool isEmpty() const { return m_string.isEmpty(); } friend QDebug operator<<(QDebug debug, const Version& v); diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index c9599b820..79b30c665 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -111,6 +111,7 @@ FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), joinServer(serverToJoin); } else { qDebug() << "no server to join"; + m_status = Failed; exit(); } } @@ -126,6 +127,7 @@ void FileLinkApp::joinServer(QString server) connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs); connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) { + m_status = Failed; switch (socketError) { case QLocalSocket::ServerNotFoundError: qDebug() @@ -150,6 +152,7 @@ void FileLinkApp::joinServer(QString server) connect(&socket, &QLocalSocket::disconnected, this, [&]() { qDebug() << "disconnected from server, should exit"; + m_status = Succeeded; exit(); }); diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h index 4c47d9bbb..ab3e9d360 100644 --- a/launcher/filelink/FileLink.h +++ b/launcher/filelink/FileLink.h @@ -41,8 +41,17 @@ class FileLinkApp : public QCoreApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT public: + enum Status { + Starting, + Failed, + Succeeded, + Initialized + }; FileLinkApp(int& argc, char** argv); virtual ~FileLinkApp(); + Status status() const { + return m_status; + } private: void joinServer(QString server); @@ -50,6 +59,8 @@ class FileLinkApp : public QCoreApplication { void runLink(); void sendResults(); + Status m_status = Status::Starting; + bool m_useHardLinks = false; QDateTime m_startTime; diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp index 83566a3c6..a656a9c96 100644 --- a/launcher/filelink/main.cpp +++ b/launcher/filelink/main.cpp @@ -26,5 +26,17 @@ int main(int argc, char* argv[]) { FileLinkApp ldh(argc, argv); - return ldh.exec(); + switch(ldh.status()) { + case FileLinkApp::Starting: + case FileLinkApp::Initialized: + { + return ldh.exec(); + } + case FileLinkApp::Failed: + return 1; + case FileLinkApp::Succeeded: + return 0; + default: + return -1; + } } diff --git a/launcher/updater/windows/GitHubRelease.h b/launcher/updater/windows/GitHubRelease.h new file mode 100644 index 000000000..1326a69f1 --- /dev/null +++ b/launcher/updater/windows/GitHubRelease.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +#include "Version.h" + +struct GitHubReleaseAsset { + int id = -1; + QString name; + QString label; + QString content_type; + int size; + QDateTime created_at; + QDateTime updated_at; + QString browser_download_url; + + bool isValid() { return id > 0; } +}; + +struct GitHubRelease { + int id = -1; + QString name; + QString tag_name; + QDateTime created_at; + QDateTime published_at; + bool prerelease; + bool draft; + QString body; + QList assets; + Version version; + + bool isValid() const { return id > 0; } +}; diff --git a/launcher/updater/windows/SelectReleaseDialog.ui b/launcher/updater/windows/SelectReleaseDialog.ui new file mode 100644 index 000000000..9d3613727 --- /dev/null +++ b/launcher/updater/windows/SelectReleaseDialog.ui @@ -0,0 +1,89 @@ + + + SelectReleaseDialog + + + + 0 + 0 + 478 + 517 + + + + Select Release to Install + + + true + + + + + + Please select the release you wish to update to. + + + + + + + true + + + + 1 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SelectReleaseDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SelectReleaseDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/launcher/updater/windows/UpdaterDialogs.cpp b/launcher/updater/windows/UpdaterDialogs.cpp new file mode 100644 index 000000000..5949194b2 --- /dev/null +++ b/launcher/updater/windows/UpdaterDialogs.cpp @@ -0,0 +1,78 @@ +#include "UpdaterDialogs.h" + +#include "ui_SelectReleaseDialog.h" + +#include +#include "Markdown.h" + +SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const QList& releases, QWidget* parent) + : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) +{ + ui->setupUi(this); + + ui->changelogTextBrowser->setOpenExternalLinks(true); + ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); + ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + + ui->versionsTree->setColumnCount(2); + + ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); + ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setStretchLastSection(false); + + ui->eplainLabel->setText(tr("Select a version to install.\n" + "\n" + "Currently installed version: %1") + .arg(m_currentVersion.toString())); + + loadReleases(); + + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseDialog::selectionChanged); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseDialog::reject); +} + +SelectReleaseDialog::~SelectReleaseDialog() +{ + delete ui; +} + +void SelectReleaseDialog::loadReleases() +{ + for (auto rls : m_releases) { + appendRelease(rls); + } +} + +void SelectReleaseDialog::appendRelease(GitHubRelease const& release) +{ + auto rls_item = new QTreeWidgetItem(ui->versionsTree); + rls_item->setText(0, release.tag_name); + rls_item->setExpanded(true); + rls_item->setText(1, release.published_at.toString()); + rls_item->setData(0, Qt::UserRole, QVariant(release.id)); + + ui->versionsTree->addTopLevelItem(rls_item); +} + +GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) { + int id = item->data(0, Qt::UserRole).toInt(); + GitHubRelease release; + for (auto rls: m_releases) { + if (rls.id == id) + release = rls; + } + return release; +} + +void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) +{ + GitHubRelease release = getRelease(current); + QString body = markdownToHTML(release.body.toUtf8()); + m_selectedRelease = release; + + ui->changelogTextBrowser->setHtml(body); +} + diff --git a/launcher/updater/windows/UpdaterDialogs.h b/launcher/updater/windows/UpdaterDialogs.h new file mode 100644 index 000000000..7481e499a --- /dev/null +++ b/launcher/updater/windows/UpdaterDialogs.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include "Version.h" +#include "updater/windows/GitHubRelease.h" + +namespace Ui { +class SelectReleaseDialog; +} + +class SelectReleaseDialog : public QDialog { + Q_OBJECT + + public: + explicit SelectReleaseDialog(const Version& cur_version, const QList& releases, QWidget* parent = 0); + ~SelectReleaseDialog(); + + void loadReleases(); + void appendRelease(GitHubRelease const& release); + GitHubRelease selectedRelease() { return m_selectedRelease; } + private slots: + GitHubRelease getRelease(QTreeWidgetItem* item); + void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); + + protected: + QList m_releases; + GitHubRelease m_selectedRelease; + Version m_currentVersion; + + Ui::SelectReleaseDialog* ui; +}; diff --git a/launcher/updater/windows/WindowsUpdater.cpp b/launcher/updater/windows/WindowsUpdater.cpp new file mode 100644 index 000000000..f0ea9998c --- /dev/null +++ b/launcher/updater/windows/WindowsUpdater.cpp @@ -0,0 +1,647 @@ +// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#include "WindowsUpdater.h" +#include "BuildConfig.h" + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#if defined Q_OS_WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif + +// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header + +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif // __APPLE__ + +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif // MacOS min version check +#endif // Other OSes version check + +#ifndef GHC_USE_STD_FS +#include +namespace fs = ghc::filesystem; +#endif + +#include + +#include "UpdaterDialogs.h" + +#include "FileSystem.h" +#include "Json.h" +#include "StringUtils.h" + +/** output to the log file */ +void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + static std::mutex loggerMutex; + const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe + + QString out = qFormatLogMessage(type, context, msg); + out += QChar::LineFeed; + + WindowsUpdaterApp* app = static_cast(QCoreApplication::instance()); + app->logFile->write(out.toUtf8()); + app->logFile->flush(); + if (app->logToConsole) { + QTextStream(stderr) << out.toLocal8Bit(); + fflush(stderr); + } +} + +WindowsUpdaterApp::WindowsUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) +{ +#if defined Q_OS_WIN32 + // attach the parent console + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // if attach succeeds, reopen and sync all the i/o + if (freopen("CON", "w", stdout)) { + std::cout.sync_with_stdio(); + } + if (freopen("CON", "w", stderr)) { + std::cerr.sync_with_stdio(); + } + if (freopen("CON", "r", stdin)) { + std::cin.sync_with_stdio(); + } + auto out = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD written; + const char* endline = "\n"; + WriteConsole(out, endline, strlen(endline), &written, NULL); + consoleAttached = true; + } +#endif + setOrganizationName(BuildConfig.LAUNCHER_NAME); + setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); + setApplicationName(BuildConfig.LAUNCHER_NAME + "Updater"); + setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); + + // Commandline parsing + QCommandLineParser parser; + parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); + + parser.addOptions({ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory).", "directory" }, + { { "I", "install-version" }, "Install a spesfic version.", "version name" }, + { { "U", "update-url" }, "Update from the spesified repo.", "github repo url" }, + { { "e", "executable" }, "Path to the prismluancher executable.", "path" }, + { { "c", "check-only" }, + "Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error)." }, + { { "F", "force" }, "Force an update, even if one is not needed." }, + { { "l", "list" }, "List avalible releases." }, + { "debug", "Log debug to console." }, + { { "L", "latest" }, "Update to the latest avalible version." }, + { { "D", "allow-downgrade" }, "Allow the updater to downgrade to previous verisons." } }); + + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(arguments()); + + logToConsole = parser.isSet("debug"); + + auto prism_executable = parser.value("executable"); + if (prism_executable.isEmpty()) { + prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; +#if defined(Q_OS_WIN32) + prism_executable += ".exe"; +#endif + } + m_prismExecutable = prism_executable; + + auto prism_update_url = parser.value("update-url"); + if (prism_update_url.isEmpty()) + prism_update_url = "https://github.com/PrismLauncher/PrismLauncher"; + m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); + + m_checkOnly = parser.isSet("check-only"); + m_forceUpdate = parser.isSet("force"); + m_printOnly = parser.isSet("list"); + auto user_version = parser.value("install-version"); + if (!user_version.isEmpty()) { + m_userSelectedVersion = Version(user_version); + } + m_updateLatest = parser.isSet("latest"); + m_allowDowngrade = parser.isSet("allow-downgrade"); + + QString origcwdPath = QDir::currentPath(); + QString binPath = applicationDirPath(); + + { // find data director + // Root path is used for updates and portable data +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr + m_rootPath = foo.absolutePath(); +#elif defined(Q_OS_WIN32) + m_rootPath = binPath; +#elif defined(Q_OS_MAC) + QDir foo(FS::PathCombine(binPath, "../..")); + m_rootPath = foo.absolutePath(); + // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues) + FS::updateTimestamp(m_rootPath); +#endif + } + + QString adjustedBy; + QString dataPath; + // change folder + QString dirParam = parser.value("dir"); + if (!dirParam.isEmpty()) { + // the dir param. it makes multimc data path point to whatever the user specified + // on command line + adjustedBy = "Command line"; + dataPath = dirParam; + } else { + QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); + dataPath = foo.absolutePath(); + adjustedBy = "Persistent data path"; + +#ifndef Q_OS_MACOS + if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { + dataPath = m_rootPath; + adjustedBy = "Portable data path"; + m_portable = true; + } +#endif + } + m_network = new QNetworkAccessManager(); + + { // setup logging + static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; + auto moveFile = [](const QString& oldName, const QString& newName) { + QFile::remove(newName); + QFile::copy(oldName, newName); + QFile::remove(oldName); + }; + + moveFile(logBase.arg(3), logBase.arg(4)); + moveFile(logBase.arg(2), logBase.arg(3)); + moveFile(logBase.arg(1), logBase.arg(2)); + moveFile(logBase.arg(0), logBase.arg(1)); + + logFile = std::unique_ptr(new QFile(logBase.arg(0))); + if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + showFatalErrorMessage("The launcher data folder is not writable!", + QString("The launcher couldn't create a log file - the data folder is not writable.\n" + "\n" + "Make sure you have write permissions to the data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); + return; + } + qInstallMessageHandler(appDebugOutput); + + qSetMessagePattern( + "%{time process}" + " " + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + " " + "|" + " " + "%{if-category}[%{category}]: %{endif}" + "%{message}"); + + bool foundLoggingRules = false; + + auto logRulesFile = QStringLiteral("qtlogging.ini"); + auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); + + qDebug() << "Testing" << logRulesPath << "..."; + foundLoggingRules = QFile::exists(logRulesPath); + + // search the dataPath() + // seach app data standard path + if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { + logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); + if (!logRulesPath.isEmpty()) { + qDebug() << "Found" << logRulesPath << "..."; + foundLoggingRules = true; + } + } + // seach root path + if (!foundLoggingRules) { + logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); + qDebug() << "Testing" << logRulesPath << "..."; + foundLoggingRules = QFile::exists(logRulesPath); + } + + if (foundLoggingRules) { + // load and set logging rules + qDebug() << "Loading logging rules from:" << logRulesPath; + QSettings loggingRules(logRulesPath, QSettings::IniFormat); + loggingRules.beginGroup("Rules"); + QStringList rule_names = loggingRules.childKeys(); + QStringList rules; + qDebug() << "Setting log rules:"; + for (auto rule_name : rule_names) { + auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString()); + rules.append(rule); + qDebug() << " " << rule; + } + auto rules_str = rules.join("\n"); + QLoggingCategory::setFilterRules(rules_str); + } + + qDebug() << "<> Log initialized."; + } + + { // log debug program info + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" + << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); + qDebug() << "Version : " << BuildConfig.printableVersionString(); + qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; + qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; + if (adjustedBy.size()) { + qDebug() << "Work dir before adjustment : " << origcwdPath; + qDebug() << "Work dir after adjustment : " << QDir::currentPath(); + qDebug() << "Adjusted by : " << adjustedBy; + } else { + qDebug() << "Work dir : " << QDir::currentPath(); + } + qDebug() << "Binary path : " << binPath; + qDebug() << "Application root path : " << m_rootPath; + qDebug() << "<> Paths set."; + } + + loadReleaseList(); +} + +WindowsUpdaterApp::~WindowsUpdaterApp() +{ + qDebug() << "updater shutting down"; + // Shut down logger by setting the logger function to nothing + qInstallMessageHandler(nullptr); + +#if defined Q_OS_WIN32 + // Detach from Windows console + if (consoleAttached) { + fclose(stdout); + fclose(stdin); + fclose(stderr); + FreeConsole(); + } +#endif + + m_network->deleteLater(); + if (m_reply) + m_reply->deleteLater(); +} + +void WindowsUpdaterApp::fail(const QString& reason) +{ + qCritical() << qPrintable(reason); + m_status = Failed; + exit(1); +} + +void WindowsUpdaterApp::abort(const QString& reason) +{ + qCritical() << qPrintable(reason); + m_status = Aborted; + exit(2); +} + +void WindowsUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) +{ + m_status = Failed; + auto msgBox = new QMessageBox(); + msgBox->setWindowTitle(title); + msgBox->setText(content); + msgBox->setStandardButtons(QMessageBox::Ok); + msgBox->setDefaultButton(QMessageBox::Ok); + msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); + msgBox->setIcon(QMessageBox::Critical); + msgBox->exec(); + exit(1); +} + +void WindowsUpdaterApp::run() +{ + qDebug() << "found" << m_releases.length() << "releases on github"; + qDebug() << "loading exe at " << m_prismExecutable; + + if (m_printOnly) { + printReleases(); + m_status = Succeeded; + return exit(0); + } + + loadPrismVersionFromExe(m_prismExecutable); + m_status = Succeeded; + + qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVerison; + qDebug() << "Version major:" << m_prismVersionMajor; + qDebug() << "Verison minor:" << m_prismVersionMinor; + qDebug() << "Verison channel:" << m_prsimVersionChannel; + qDebug() << "Git Commit:" << m_prismGitCommit; + + auto latest = getLatestRelease(); + qDebug() << "Latest release" << latest.version; + auto need_update = needUpdate(latest); + + if (m_checkOnly) { + if (need_update) + return exit(100); + else + return exit(0); + } + + if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) { + GitHubRelease update_release = latest; + if (!m_userSelectedVersion.isEmpty()) { + bool found = false; + for (auto rls : m_releases) { + if (rls.version == m_userSelectedVersion) { + found = true; + update_release = rls; + break; + } + } + if (!found) { + showFatalErrorMessage( + "No release for version!", + QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString())); + return; + } + } else if (!m_updateLatest) { + update_release = selectRelease(); + if (!update_release.isValid()) { + showFatalErrorMessage("No version selected.", "No version was selected."); + return; + } + } + + performUpdate(update_release); + } + + exit(0); +} + +void WindowsUpdaterApp::printReleases() +{ + for (auto release : m_releases) { + std::cout << release.name.toStdString() << " Version: " << release.tag_name.toStdString() << std::endl; + } +} + +QList WindowsUpdaterApp::nonDraftReleases() +{ + QList nonDraft; + for (auto rls : m_releases) { + if (rls.isValid() && !rls.draft) + nonDraft.append(rls); + } + return nonDraft; +} + +QList WindowsUpdaterApp::newerReleases() +{ + QList newer; + for (auto rls : nonDraftReleases()) { + if (rls.version > m_prismVerison) + newer.append(rls); + } + return newer; +} + +GitHubRelease WindowsUpdaterApp::selectRelease() +{ + QList releases; + + if (m_allowDowngrade) { + releases = nonDraftReleases(); + } else { + releases = newerReleases(); + } + + if (releases.isEmpty()) + return {}; + + SelectReleaseDialog dlg(Version(m_prismVerison), releases); + auto result = dlg.exec(); + + GitHubRelease release = dlg.selectedRelease(); + if (result == QDialog::Rejected) { + return {}; + } + + return release; +} + +void WindowsUpdaterApp::performUpdate(const GitHubRelease& release) +{ + qDebug() << "Updating to" << release.tag_name; +} + +void WindowsUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) +{ + QProcess proc = QProcess(); + proc.start(exe_path, { "-v" }); + proc.waitForFinished(); + auto out = proc.readAll(); + auto lines = out.split('\n'); + if (lines.length() < 2) + return; + auto first = lines.takeFirst(); + auto first_parts = first.split(' '); + if (first_parts.length() < 2) + return; + m_prismBinaryName = first_parts.takeFirst(); + auto version = first_parts.takeFirst(); + m_prismVerison = version; + if (version.contains('-')) { + auto index = version.indexOf('-'); + m_prsimVersionChannel = version.mid(index + 1); + version = version.left(index); + } else { + m_prsimVersionChannel = "stable"; + } + auto version_parts = version.split('.'); + m_prismVersionMajor = version_parts.takeFirst().toInt(); + m_prismVersionMinor = version_parts.takeFirst().toInt(); + m_prismGitCommit = lines.takeFirst().simplified(); +} + +void WindowsUpdaterApp::loadReleaseList() +{ + auto github_repo = m_prismRepoUrl; + if (github_repo.host() != "github.com") + return fail("updating from a non github url is not supported"); + + auto path_parts = github_repo.path().split('/'); + path_parts.removeFirst(); // empty segment from leading / + auto repo_owner = path_parts.takeFirst(); + auto repo_name = path_parts.takeFirst(); + auto api_url = QString("https://api.github.com/repos/%1/%2/releases").arg(repo_owner, repo_name); + + qDebug() << "Fetching release list from" << api_url; + + downloadReleasePage(api_url, 1); +} + +void WindowsUpdaterApp::downloadReleasePage(const QString& api_url, int page) +{ + int per_page = 30; + auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); + QNetworkRequest request(page_url); + request.setRawHeader("Accept", "application/vnd.github+json"); + request.setRawHeader("X-GitHub-Api-Version", "2022-11-28"); + + QNetworkReply* rep = m_network->get(request); + m_reply = rep; + auto responce = new QByteArray(); + + connect(rep, &QNetworkReply::finished, this, [this, responce, per_page, api_url, page]() { + int num_found = parseReleasePage(responce); + delete responce; + + if (!(num_found < per_page)) { // there may be more, fetch next page + downloadReleasePage(api_url, page + 1); + } else { + run(); + } + }); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 + connect(rep, &QNetworkReply::errorOccurred, this, &WindowsUpdaterApp::downloadError); +#else + connect(rep, QOverload::of(&QNetworkReply::error), this, &WindowsUpdaterApp::downloadError); +#endif + connect(rep, &QNetworkReply::sslErrors, this, &WindowsUpdaterApp::sslErrors); + connect(rep, &QNetworkReply::readyRead, this, [this, responce]() { + auto data = m_reply->readAll(); + responce->append(data); + }); +} + +int WindowsUpdaterApp::parseReleasePage(const QByteArray* responce) +{ + if (responce->isEmpty()) // empty page + return 0; + int num_releases = 0; + try { + auto doc = Json::requireDocument(*responce); + auto release_list = Json::requireArray(doc); + for (auto release_json : release_list) { + auto release_obj = Json::requireObject(release_json); + + GitHubRelease release = {}; + release.id = Json::requireInteger(release_obj, "id"); + release.name = Json::ensureString(release_obj, "name"); + release.tag_name = Json::requireString(release_obj, "tag_name"); + release.created_at = QDateTime::fromString(Json::requireString(release_obj, "created_at"), Qt::ISODate); + release.published_at = QDateTime::fromString(Json::ensureString(release_obj, "published_at"), Qt::ISODate); + release.draft = Json::requireBoolean(release_obj, "draft"); + release.prerelease = Json::requireBoolean(release_obj, "prerelease"); + release.body = Json::ensureString(release_obj, "body"); + release.version = Version(release.tag_name); + + auto release_assets_obj = Json::requireArray(release_obj, "assets"); + for (auto asset_json : release_assets_obj) { + auto asset_obj = Json::requireObject(asset_json); + GitHubReleaseAsset asset = {}; + asset.id = Json::requireInteger(asset_obj, "id"); + asset.name = Json::requireString(asset_obj, "name"); + asset.label = Json::ensureString(asset_obj, "label"); + asset.content_type = Json::requireString(asset_obj, "content_type"); + asset.size = Json::requireInteger(asset_obj, "size"); + asset.created_at = QDateTime::fromString(Json::requireString(asset_obj, "created_at"), Qt::ISODate); + asset.updated_at = QDateTime::fromString(Json::requireString(asset_obj, "updated_at"), Qt::ISODate); + asset.browser_download_url = Json::requireString(asset_obj, "browser_download_url"); + release.assets.append(asset); + } + m_releases.append(release); + num_releases++; + } + } catch (Json::JsonException& e) { + auto err_msg = + QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(responce->toStdString())); + fail(err_msg); + } + return num_releases; +} + +GitHubRelease WindowsUpdaterApp::getLatestRelease() +{ + GitHubRelease latest; + for (auto release : m_releases) { + if (!latest.isValid() || (!release.draft && release.version > latest.version)) { + latest = release; + } + } + return latest; +} + +bool WindowsUpdaterApp::needUpdate(const GitHubRelease& release) +{ + auto current_ver = Version(QString("%1.%2").arg(QString::number(m_prismVersionMajor)).arg(QString::number(m_prismVersionMinor))); + return current_ver < release.version; +} + +void WindowsUpdaterApp::downloadError(QNetworkReply::NetworkError error) +{ + if (error == QNetworkReply::OperationCanceledError) { + abort(QString("Aborted %1").arg(m_reply->url().toString())); + } else { + fail(QString("Network request Failed: %1 with reason %2").arg(m_reply->url().toString()).arg(error)); + } +} + +void WindowsUpdaterApp::sslErrors(const QList& errors) +{ + int i = 1; + QString err_msg; + for (auto error : errors) { + err_msg.append(QString("Network request %1 SSL Error %2: %3\n").arg(m_reply->url().toString()).arg(i).arg(error.errorString())); + auto cert = error.certificate(); + err_msg.append(QString("Certificate in question:\n%1").arg(cert.toText())); + i++; + } + fail(err_msg); +} diff --git a/launcher/updater/windows/WindowsUpdater.h b/launcher/updater/windows/WindowsUpdater.h new file mode 100644 index 000000000..b79a86271 --- /dev/null +++ b/launcher/updater/windows/WindowsUpdater.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRISM_EXTERNAL_EXE +#include "FileSystem.h" + +#include "updater/windows/GitHubRelease.h" + +class WindowsUpdaterApp : public QApplication { + // friends for the purpose of limiting access to deprecated stuff + Q_OBJECT + public: + enum Status { Starting, Failed, Succeeded, Initialized, Aborted }; + WindowsUpdaterApp(int& argc, char** argv); + virtual ~WindowsUpdaterApp(); + void loadReleaseList(); + void run(); + Status status() const { return m_status; } + + private: + void fail(const QString& reason); + void abort(const QString& reason); + void showFatalErrorMessage(const QString& title, const QString& content); + + void loadPrismVersionFromExe(const QString& exe_path); + + void downloadReleasePage(const QString& api_url, int page); + int parseReleasePage(const QByteArray* responce); + GitHubRelease getLatestRelease(); + bool needUpdate(const GitHubRelease& release); + GitHubRelease selectRelease(); + void performUpdate(const GitHubRelease& release); + void printReleases(); + QList newerReleases(); + QList nonDraftReleases(); + + void downloadError(QNetworkReply::NetworkError error); + void sslErrors(const QList& errors); + + const QString& root() { return m_rootPath; } + + bool isPortable() { return m_portable; } + + QString m_rootPath; + bool m_portable = false; + QString m_prismExecutable; + QUrl m_prismRepoUrl; + Version m_userSelectedVersion; + bool m_checkOnly; + bool m_forceUpdate; + bool m_printOnly; + bool m_updateLatest; + bool m_allowDowngrade; + + QString m_prismBinaryName; + QString m_prismVerison; + int m_prismVersionMajor; + int m_prismVersionMinor; + QString m_prsimVersionChannel; + QString m_prismGitCommit; + + Status m_status = Status::Starting; + QNetworkAccessManager* m_network; + QNetworkReply* m_reply; + QList m_releases; + + public: + std::unique_ptr logFile; + bool logToConsole = false; + +#if defined Q_OS_WIN32 + // used on Windows to attach the standard IO streams + bool consoleAttached = false; +#endif +}; diff --git a/launcher/updater/windows/windows_main.cpp b/launcher/updater/windows/windows_main.cpp new file mode 100644 index 000000000..31d7646b5 --- /dev/null +++ b/launcher/updater/windows/windows_main.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + + + +#include "updater/windows/WindowsUpdater.h" +int main(int argc, char* argv[]) +{ + WindowsUpdaterApp wUpApp(argc, argv); + + switch(wUpApp.status()) { + case WindowsUpdaterApp::Starting: + case WindowsUpdaterApp::Initialized: + { + return wUpApp.exec(); + } + case WindowsUpdaterApp::Failed: + return 1; + case WindowsUpdaterApp::Succeeded: + return 0; + default: + return -1; + } + +} From 0b2044e9a434ba3d2724630399bc41ebb321b039 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 13:52:23 -0700 Subject: [PATCH 02/72] fix: typo - no space before comment Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/minecraft/ComponentUpdateTask_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/minecraft/ComponentUpdateTask_p.h b/launcher/minecraft/ComponentUpdateTask_p.h index 5b02431b2..84f6f9a63 100644 --- a/launcher/minecraft/ComponentUpdateTask_p.h +++ b/launcher/minecraft/ComponentUpdateTask_p.h @@ -4,6 +4,7 @@ #include #include #include "net/Mode.h" +#include "ComponentUpdateTask.h" class PackProfile; From f5729d9a7c953d88ae60e106a787b1d7c35ede8b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 13:53:44 -0700 Subject: [PATCH 03/72] Revert "fix: typo - no space before comment" This reverts commit 0b2044e9a434ba3d2724630399bc41ebb321b039. --- launcher/minecraft/ComponentUpdateTask_p.h | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/ComponentUpdateTask_p.h b/launcher/minecraft/ComponentUpdateTask_p.h index 84f6f9a63..5b02431b2 100644 --- a/launcher/minecraft/ComponentUpdateTask_p.h +++ b/launcher/minecraft/ComponentUpdateTask_p.h @@ -4,7 +4,6 @@ #include #include #include "net/Mode.h" -#include "ComponentUpdateTask.h" class PackProfile; From 2dce08caf16ac80452ccaebed5e4f24c0ec7418a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 13:54:13 -0700 Subject: [PATCH 04/72] fix: typo - space before comment Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- program_info/win_install.nsi.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index 27c400395..8389e2468 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -293,7 +293,7 @@ Function RunUninstall stringloop: ; get string length StrCpy $2 $1 1 $3 ; get next char IntOp $3 $3 + 1 ; index += 1 - StrCmp $2 "" +2 stringloop; if empty exit loop + StrCmp $2 "" +2 stringloop ; if empty exit loop IntOp $3 $3 - 1 ; index -= 1 Goto run quoteloop: ; get string length with quotes removed From 7c85462ff371f2400bd9cae8a80904aa6175ac73 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 14:31:38 -0700 Subject: [PATCH 05/72] packaging: include file manifest in portable install Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 691e257bf..838eddef0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -411,12 +411,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + cat ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From dbe14c6be5ff3ae015c783a406a0d13759475aa0 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 14:54:06 -0700 Subject: [PATCH 06/72] packaging: ensure all files are in manifest Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 838eddef0..f309a3d06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,11 +375,13 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} + Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} + Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -418,7 +420,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - cat ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From 68c28335520ca4a4a17d47f863dd92a0b9a00904 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 14:55:13 -0700 Subject: [PATCH 07/72] packaging: use powershell name Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f309a3d06..dcc891c62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -413,14 +413,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - cat ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From 4cbae0a751f54aa77a5e49cce23a3e4b5bea9a37 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 15:36:25 -0700 Subject: [PATCH 08/72] packaging: trim manfest paths to be relative Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dcc891c62..338f71a05 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,13 +375,13 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - Copy-Item ${{ env.BUILD_DIR }}/install_manifest.txt ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -413,14 +413,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' From 9b1e0eb57d4e59421bb019b866688c4138b15d7e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 16:01:31 -0700 Subject: [PATCH 09/72] packaging: msys2 shell is bash Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 338f71a05..fa1081c5c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,7 +375,7 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -413,7 +413,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' From 4e0a08345d7049fe9785a88d66e8216d3bc573b7 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 16:07:29 -0700 Subject: [PATCH 10/72] packaging: add manifest.txt to linux tarballs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa1081c5c..ef4becc08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -441,6 +441,7 @@ jobs: if: runner.os == 'Linux' run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} + while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} tar --owner root --group root -czf ../PrismLauncher.tar.gz * @@ -450,6 +451,8 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable + while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt + while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt cd ${{ env.INSTALL_PORTABLE_DIR }} tar -czf ../PrismLauncher-portable.tar.gz * From b48540dc322ab9eb5f0f270598f2720b13599fae Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 16:27:53 -0700 Subject: [PATCH 11/72] packaging: trim leading working dir from manifest paths Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef4becc08..2fde49226 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,13 +375,13 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart("$pwd/") } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -413,14 +413,14 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt + Get-Content ${{ env.BUILD_DIR }}/install_manifest_portable.txt | %{ $_.TrimStart("$pwd/") } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows, installer) if: runner.os == 'Windows' @@ -441,7 +441,7 @@ jobs: if: runner.os == 'Linux' run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} - while read l; do l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} tar --owner root --group root -czf ../PrismLauncher.tar.gz * @@ -451,8 +451,8 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt - while read l; do l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt + while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR_PORTABLE }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR_PORTABLE }}/manifest.txt cd ${{ env.INSTALL_PORTABLE_DIR }} tar -czf ../PrismLauncher-portable.tar.gz * From 3bbe33132af3d979200d03a16d625ecf7e9cfc51 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 19:39:14 -0700 Subject: [PATCH 12/72] packaging: convert msys paths Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2fde49226..4f59354b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,7 +375,7 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l;=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -413,7 +413,7 @@ jobs: run: | cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable - while read l; do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest_portable.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC, portable) if: runner.os == 'Windows' && matrix.msystem == '' From 46a13c876771bf521dbaf1902623f080b4843bd1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 31 May 2023 20:16:58 -0700 Subject: [PATCH 13/72] packaging: fix typo ; in middle of line Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f59354b5..f103ecc1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -375,7 +375,7 @@ jobs: shell: msys2 {0} run: | cmake --install ${{ env.BUILD_DIR }} - while read l; do l;=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt + while read l; do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}}; l=${l#./}; echo $l; done < ${{ env.BUILD_DIR }}/install_manifest.txt >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' From 5627b4a9c555a6e2b3fe781500a4c103e9deee0b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:45:36 -0700 Subject: [PATCH 14/72] refactor: rename updater files Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/{windows => prismupdater}/GitHubRelease.h | 0 .../{windows/WindowsUpdater.cpp => prismupdater/PrismUpdater.cpp} | 0 .../{windows/WindowsUpdater.h => prismupdater/PrismUpdater.h} | 0 launcher/updater/{windows => prismupdater}/SelectReleaseDialog.ui | 0 launcher/updater/{windows => prismupdater}/UpdaterDialogs.cpp | 0 launcher/updater/{windows => prismupdater}/UpdaterDialogs.h | 0 launcher/updater/{windows => prismupdater}/windows_main.cpp | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename launcher/updater/{windows => prismupdater}/GitHubRelease.h (100%) rename launcher/updater/{windows/WindowsUpdater.cpp => prismupdater/PrismUpdater.cpp} (100%) rename launcher/updater/{windows/WindowsUpdater.h => prismupdater/PrismUpdater.h} (100%) rename launcher/updater/{windows => prismupdater}/SelectReleaseDialog.ui (100%) rename launcher/updater/{windows => prismupdater}/UpdaterDialogs.cpp (100%) rename launcher/updater/{windows => prismupdater}/UpdaterDialogs.h (100%) rename launcher/updater/{windows => prismupdater}/windows_main.cpp (100%) diff --git a/launcher/updater/windows/GitHubRelease.h b/launcher/updater/prismupdater/GitHubRelease.h similarity index 100% rename from launcher/updater/windows/GitHubRelease.h rename to launcher/updater/prismupdater/GitHubRelease.h diff --git a/launcher/updater/windows/WindowsUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp similarity index 100% rename from launcher/updater/windows/WindowsUpdater.cpp rename to launcher/updater/prismupdater/PrismUpdater.cpp diff --git a/launcher/updater/windows/WindowsUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h similarity index 100% rename from launcher/updater/windows/WindowsUpdater.h rename to launcher/updater/prismupdater/PrismUpdater.h diff --git a/launcher/updater/windows/SelectReleaseDialog.ui b/launcher/updater/prismupdater/SelectReleaseDialog.ui similarity index 100% rename from launcher/updater/windows/SelectReleaseDialog.ui rename to launcher/updater/prismupdater/SelectReleaseDialog.ui diff --git a/launcher/updater/windows/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp similarity index 100% rename from launcher/updater/windows/UpdaterDialogs.cpp rename to launcher/updater/prismupdater/UpdaterDialogs.cpp diff --git a/launcher/updater/windows/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h similarity index 100% rename from launcher/updater/windows/UpdaterDialogs.h rename to launcher/updater/prismupdater/UpdaterDialogs.h diff --git a/launcher/updater/windows/windows_main.cpp b/launcher/updater/prismupdater/windows_main.cpp similarity index 100% rename from launcher/updater/windows/windows_main.cpp rename to launcher/updater/prismupdater/windows_main.cpp From f619a04fe7ce1e7649152e383f0415f59de55d5d Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:55:20 -0700 Subject: [PATCH 15/72] refactor: rename app class Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 28 +++++------ .../filelink/{main.cpp => filelink_main.cpp} | 0 .../updater/prismupdater/PrismUpdater.cpp | 48 +++++++++---------- launcher/updater/prismupdater/PrismUpdater.h | 8 ++-- .../updater/prismupdater/UpdaterDialogs.h | 2 +- .../{windows_main.cpp => updater_main.cpp} | 12 ++--- 6 files changed, 49 insertions(+), 49 deletions(-) rename launcher/filelink/{main.cpp => filelink_main.cpp} (100%) rename launcher/updater/prismupdater/{windows_main.cpp => updater_main.cpp} (80%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 5a979b8d9..89dea0d11 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -567,12 +567,12 @@ set(LINKEXE_SOURCES DesktopServices.cpp ) -set(WINDOWSUPDATEREXE_SOURCES - updater/windows/WindowsUpdater.h - updater/windows/WindowsUpdater.cpp - updater/windows/UpdaterDialogs.h - updater/windows/UpdaterDialogs.cpp - updater/windows/GitHubRelease.h +set(PRISMUPDATER_SOURCES + updater/prismupdater/PrismUpdater.h + updater/prismupdater/PrismUpdater.cpp + updater/prismupdater/UpdaterDialogs.h + updater/prismupdater/UpdaterDialogs.cpp + updater/prismupdater/GitHubRelease.h Json.h Json.cpp FileSystem.h @@ -1094,8 +1094,8 @@ qt_add_resources(LAUNCHER_RESOURCES ../${Launcher_Branding_LogoQRC} ) -qt_wrap_ui(WINDOWSUPDATER_UI - updater/windows/SelectReleaseDialog.ui +qt_wrap_ui(PRISMUPDATER_UI + updater/prismupdater/SelectReleaseDialog.ui ) ######## Windows resource files ######## @@ -1183,9 +1183,9 @@ install(TARGETS ${Launcher_Name} if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) # Updater - add_library(windows_updater_logic STATIC ${WINDOWSUPDATEREXE_SOURCES} ${WINDOWSUPDATER_UI}) - target_include_directories(windows_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries(windows_updater_logic + add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${PRISMUPDATER_UI}) + target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(prism_updater_logic systeminfo BuildConfig ghcFilesystem::ghc_filesystem @@ -1196,8 +1196,8 @@ if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) cmark::cmark ) - add_executable("${Launcher_Name}_updater" WIN32 updater/windows/windows_main.cpp) - target_link_libraries("${Launcher_Name}_updater" windows_updater_logic) + add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) + target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) if(DEFINED Launcher_APP_BINARY_NAME) set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") @@ -1229,7 +1229,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) ${Launcher_QT_LIBS} ) - add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp) + add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp) target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) diff --git a/launcher/filelink/main.cpp b/launcher/filelink/filelink_main.cpp similarity index 100% rename from launcher/filelink/main.cpp rename to launcher/filelink/filelink_main.cpp diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index f0ea9998c..923b891f9 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -20,7 +20,7 @@ * */ -#include "WindowsUpdater.h" +#include "PrismUpdater.h" #include "BuildConfig.h" #include @@ -68,7 +68,7 @@ namespace fs = ghc::filesystem; #include -#include "UpdaterDialogs.h" +#include "updater/prismupdater/UpdaterDialogs.h" #include "FileSystem.h" #include "Json.h" @@ -83,7 +83,7 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt QString out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; - WindowsUpdaterApp* app = static_cast(QCoreApplication::instance()); + PrismUpdaterApp* app = static_cast(QCoreApplication::instance()); app->logFile->write(out.toUtf8()); app->logFile->flush(); if (app->logToConsole) { @@ -92,7 +92,7 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt } } -WindowsUpdaterApp::WindowsUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) +PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 // attach the parent console @@ -313,7 +313,7 @@ WindowsUpdaterApp::WindowsUpdaterApp(int& argc, char** argv) : QApplication(argc loadReleaseList(); } -WindowsUpdaterApp::~WindowsUpdaterApp() +PrismUpdaterApp::~PrismUpdaterApp() { qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing @@ -334,21 +334,21 @@ WindowsUpdaterApp::~WindowsUpdaterApp() m_reply->deleteLater(); } -void WindowsUpdaterApp::fail(const QString& reason) +void PrismUpdaterApp::fail(const QString& reason) { qCritical() << qPrintable(reason); m_status = Failed; exit(1); } -void WindowsUpdaterApp::abort(const QString& reason) +void PrismUpdaterApp::abort(const QString& reason) { qCritical() << qPrintable(reason); m_status = Aborted; exit(2); } -void WindowsUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) +void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) { m_status = Failed; auto msgBox = new QMessageBox(); @@ -362,7 +362,7 @@ void WindowsUpdaterApp::showFatalErrorMessage(const QString& title, const QStrin exit(1); } -void WindowsUpdaterApp::run() +void PrismUpdaterApp::run() { qDebug() << "found" << m_releases.length() << "releases on github"; qDebug() << "loading exe at " << m_prismExecutable; @@ -424,14 +424,14 @@ void WindowsUpdaterApp::run() exit(0); } -void WindowsUpdaterApp::printReleases() +void PrismUpdaterApp::printReleases() { for (auto release : m_releases) { std::cout << release.name.toStdString() << " Version: " << release.tag_name.toStdString() << std::endl; } } -QList WindowsUpdaterApp::nonDraftReleases() +QList PrismUpdaterApp::nonDraftReleases() { QList nonDraft; for (auto rls : m_releases) { @@ -441,7 +441,7 @@ QList WindowsUpdaterApp::nonDraftReleases() return nonDraft; } -QList WindowsUpdaterApp::newerReleases() +QList PrismUpdaterApp::newerReleases() { QList newer; for (auto rls : nonDraftReleases()) { @@ -451,7 +451,7 @@ QList WindowsUpdaterApp::newerReleases() return newer; } -GitHubRelease WindowsUpdaterApp::selectRelease() +GitHubRelease PrismUpdaterApp::selectRelease() { QList releases; @@ -475,12 +475,12 @@ GitHubRelease WindowsUpdaterApp::selectRelease() return release; } -void WindowsUpdaterApp::performUpdate(const GitHubRelease& release) +void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { qDebug() << "Updating to" << release.tag_name; } -void WindowsUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) +void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.start(exe_path, { "-v" }); @@ -509,7 +509,7 @@ void WindowsUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) m_prismGitCommit = lines.takeFirst().simplified(); } -void WindowsUpdaterApp::loadReleaseList() +void PrismUpdaterApp::loadReleaseList() { auto github_repo = m_prismRepoUrl; if (github_repo.host() != "github.com") @@ -526,7 +526,7 @@ void WindowsUpdaterApp::loadReleaseList() downloadReleasePage(api_url, 1); } -void WindowsUpdaterApp::downloadReleasePage(const QString& api_url, int page) +void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); @@ -549,18 +549,18 @@ void WindowsUpdaterApp::downloadReleasePage(const QString& api_url, int page) } }); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &WindowsUpdaterApp::downloadError); + connect(rep, &QNetworkReply::errorOccurred, this, &PrismUpdaterApp::downloadError); #else connect(rep, QOverload::of(&QNetworkReply::error), this, &WindowsUpdaterApp::downloadError); #endif - connect(rep, &QNetworkReply::sslErrors, this, &WindowsUpdaterApp::sslErrors); + connect(rep, &QNetworkReply::sslErrors, this, &PrismUpdaterApp::sslErrors); connect(rep, &QNetworkReply::readyRead, this, [this, responce]() { auto data = m_reply->readAll(); responce->append(data); }); } -int WindowsUpdaterApp::parseReleasePage(const QByteArray* responce) +int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) { if (responce->isEmpty()) // empty page return 0; @@ -607,7 +607,7 @@ int WindowsUpdaterApp::parseReleasePage(const QByteArray* responce) return num_releases; } -GitHubRelease WindowsUpdaterApp::getLatestRelease() +GitHubRelease PrismUpdaterApp::getLatestRelease() { GitHubRelease latest; for (auto release : m_releases) { @@ -618,13 +618,13 @@ GitHubRelease WindowsUpdaterApp::getLatestRelease() return latest; } -bool WindowsUpdaterApp::needUpdate(const GitHubRelease& release) +bool PrismUpdaterApp::needUpdate(const GitHubRelease& release) { auto current_ver = Version(QString("%1.%2").arg(QString::number(m_prismVersionMajor)).arg(QString::number(m_prismVersionMinor))); return current_ver < release.version; } -void WindowsUpdaterApp::downloadError(QNetworkReply::NetworkError error) +void PrismUpdaterApp::downloadError(QNetworkReply::NetworkError error) { if (error == QNetworkReply::OperationCanceledError) { abort(QString("Aborted %1").arg(m_reply->url().toString())); @@ -633,7 +633,7 @@ void WindowsUpdaterApp::downloadError(QNetworkReply::NetworkError error) } } -void WindowsUpdaterApp::sslErrors(const QList& errors) +void PrismUpdaterApp::sslErrors(const QList& errors) { int i = 1; QString err_msg; diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index b79a86271..da6b4bf71 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -39,15 +39,15 @@ #define PRISM_EXTERNAL_EXE #include "FileSystem.h" -#include "updater/windows/GitHubRelease.h" +#include "updater/prismupdater/GitHubRelease.h" -class WindowsUpdaterApp : public QApplication { +class PrismUpdaterApp : public QApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT public: enum Status { Starting, Failed, Succeeded, Initialized, Aborted }; - WindowsUpdaterApp(int& argc, char** argv); - virtual ~WindowsUpdaterApp(); + PrismUpdaterApp(int& argc, char** argv); + virtual ~PrismUpdaterApp(); void loadReleaseList(); void run(); Status status() const { return m_status; } diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index 7481e499a..1449befbd 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -4,7 +4,7 @@ #include #include "Version.h" -#include "updater/windows/GitHubRelease.h" +#include "updater/prismupdater/GitHubRelease.h" namespace Ui { class SelectReleaseDialog; diff --git a/launcher/updater/prismupdater/windows_main.cpp b/launcher/updater/prismupdater/updater_main.cpp similarity index 80% rename from launcher/updater/prismupdater/windows_main.cpp rename to launcher/updater/prismupdater/updater_main.cpp index 31d7646b5..6bc7dd208 100644 --- a/launcher/updater/prismupdater/windows_main.cpp +++ b/launcher/updater/prismupdater/updater_main.cpp @@ -22,20 +22,20 @@ -#include "updater/windows/WindowsUpdater.h" +#include "updater/prismupdater/PrismUpdater.h" int main(int argc, char* argv[]) { - WindowsUpdaterApp wUpApp(argc, argv); + PrismUpdaterApp wUpApp(argc, argv); switch(wUpApp.status()) { - case WindowsUpdaterApp::Starting: - case WindowsUpdaterApp::Initialized: + case PrismUpdaterApp::Starting: + case PrismUpdaterApp::Initialized: { return wUpApp.exec(); } - case WindowsUpdaterApp::Failed: + case PrismUpdaterApp::Failed: return 1; - case WindowsUpdaterApp::Succeeded: + case PrismUpdaterApp::Succeeded: return 0; default: return -1; From 74d4a98864ba2deb2449e7e3ec7632054ece4823 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:39:04 -0700 Subject: [PATCH 16/72] refactor: split out setting api headers for downloads Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 4 ++ launcher/InstanceImportTask.cpp | 4 +- launcher/ResourceDownloadTask.cpp | 4 +- launcher/meta/BaseEntity.cpp | 4 +- launcher/minecraft/AssetsUtils.cpp | 4 +- launcher/minecraft/Library.cpp | 6 +-- launcher/minecraft/update/AssetUpdateTask.cpp | 4 +- .../minecraft/update/FMLLibrariesTask.cpp | 4 +- .../atlauncher/ATLPackInstallTask.cpp | 12 +++-- .../modplatform/flame/FileResolvingTask.cpp | 5 +- launcher/modplatform/flame/FlameAPI.cpp | 7 +-- .../modplatform/flame/FlameCheckUpdate.cpp | 6 ++- .../flame/FlameInstanceCreationTask.cpp | 4 +- .../helpers/NetworkResourceAPI.cpp | 8 +-- .../modplatform/legacy_ftb/PackFetchTask.cpp | 8 +-- .../legacy_ftb/PackInstallTask.cpp | 4 +- launcher/modplatform/modrinth/ModrinthAPI.cpp | 5 +- .../modrinth/ModrinthInstanceCreationTask.cpp | 5 +- .../technic/SingleZipPackInstallTask.cpp | 4 +- .../technic/SolderPackInstallTask.cpp | 5 +- launcher/net/ApiDownload.h | 39 ++++++++++++++ launcher/net/ApiHeaderProxy.h | 51 +++++++++++++++++++ launcher/net/Download.cpp | 12 ++--- launcher/net/HeaderProxy.h | 49 ++++++++++++++++++ launcher/net/NetAction.h | 9 +++- launcher/net/RawHeaderProxy.h | 44 ++++++++++++++++ .../ui/pages/instance/ManagedPackPage.cpp | 6 ++- .../ui/pages/modplatform/ResourceModel.cpp | 4 +- .../modplatform/atlauncher/AtlListModel.cpp | 6 ++- .../atlauncher/AtlOptionalModDialog.cpp | 4 +- .../ui/pages/modplatform/flame/FlameModel.cpp | 6 ++- .../ui/pages/modplatform/flame/FlamePage.cpp | 4 +- .../modplatform/legacy_ftb/ListModel.cpp | 3 +- .../modplatform/modrinth/ModrinthModel.cpp | 6 ++- .../modplatform/modrinth/ModrinthPage.cpp | 6 ++- .../modplatform/technic/TechnicModel.cpp | 6 ++- .../pages/modplatform/technic/TechnicPage.cpp | 6 ++- .../ui/widgets/VariableSizedImageObject.cpp | 3 +- 38 files changed, 303 insertions(+), 68 deletions(-) create mode 100644 launcher/net/ApiDownload.h create mode 100644 launcher/net/ApiHeaderProxy.h create mode 100644 launcher/net/HeaderProxy.h create mode 100644 launcher/net/RawHeaderProxy.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 273b5449a..bce885ca8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -136,6 +136,10 @@ set(NET_SOURCES net/Validator.h net/Upload.cpp net/Upload.h + net/HeaderProxy.h + net/RawHeaderProxy.h + net/ApiHeaderProxy.h + net/ApiDownload.h ) # Game launch logic diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 352848f02..7332e917f 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -51,6 +51,8 @@ #include "settings/INISettingsObject.h" +#include "net/ApiDownload.h" + #include #include @@ -95,7 +97,7 @@ void InstanceImportTask::executeTask() m_archivePath = entry->getFullPath(); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index 61b918aaf..03e103f13 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -24,6 +24,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, ModPlatform::IndexedVersion version, const std::shared_ptr packs, @@ -50,7 +52,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, } } - m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); + m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress); diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 97815eba8..fd810a91b 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,7 +15,7 @@ #include "BaseEntity.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" #include "Json.h" @@ -130,7 +130,7 @@ void Meta::BaseEntity::load(Net::Mode loadType) auto url = this->url(); auto entry = APPLICATION->metacache()->resolveEntry("meta", localFilename()); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); /* * The validator parses the file and loads it into the object. * If that fails, the file is not written to storage. diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 16fdfdb1c..65ad6da69 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -45,7 +45,7 @@ #include "AssetsUtils.h" #include "FileSystem.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/ChecksumValidator.h" #include "BuildConfig.h" @@ -311,7 +311,7 @@ NetAction::Ptr AssetObject::getDownloadAction() QFileInfo objectFile(getLocalPath()); if ((!objectFile.isFile()) || (objectFile.size() != size)) { - auto objectDL = Net::Download::makeFile(getUrl(), objectFile.filePath()); + auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath()); if(hash.size()) { auto rawHash = QByteArray::fromHex(hash.toLatin1()); diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index cb2b5254d..e0318ef22 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -36,7 +36,7 @@ #include "Library.h" #include "MinecraftInstance.h" -#include +#include #include #include #include @@ -129,14 +129,14 @@ QList Library::getDownloads( if(sha1.size()) { auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); - auto dl = Net::Download::makeCached(url, entry, options); + auto dl = Net::ApiDownload::makeCached(url, entry, options); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; out.append(dl); } else { - out.append(Net::Download::makeCached(url, entry, options)); + out.append(Net::ApiDownload::makeCached(url, entry, options)); qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; } return true; diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp index 31fd5eb11..bafbe75f1 100644 --- a/launcher/minecraft/update/AssetUpdateTask.cpp +++ b/launcher/minecraft/update/AssetUpdateTask.cpp @@ -7,6 +7,8 @@ #include "Application.h" +#include "net/ApiDownload.h" + AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst) { m_inst = inst; @@ -34,7 +36,7 @@ void AssetUpdateTask::executeTask() entry->setStale(true); auto hexSha1 = assets->sha1.toLatin1(); qDebug() << "Asset index SHA1:" << hexSha1; - auto dl = Net::Download::makeCached(indexUrl, entry); + auto dl = Net::ApiDownload::makeCached(indexUrl, entry); auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); job->addNetAction(dl); diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp index 75e5c5720..3651f4f40 100644 --- a/launcher/minecraft/update/FMLLibrariesTask.cpp +++ b/launcher/minecraft/update/FMLLibrariesTask.cpp @@ -8,6 +8,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst) { m_inst = inst; @@ -68,7 +70,7 @@ void FMLLibrariesTask::executeTask() { auto entry = metacache->resolveEntry("fmllibs", lib.filename); QString urlString = BuildConfig.FMLLIBS_BASE_URL + lib.filename; - dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry, options)); + dljob->addNetAction(Net::ApiDownload::makeCached(QUrl(urlString), entry, options)); } connect(dljob.get(), &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 96cea7b7d..966e2665f 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -53,6 +53,8 @@ #include "meta/Version.h" #include "meta/VersionList.h" +#include "net/ApiDownload.h" + #include "BuildConfig.h" #include "Application.h" @@ -84,7 +86,7 @@ void PackInstallTask::executeTask() NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json") .arg(m_pack_safe_name).arg(m_version_name); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), &response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); @@ -658,7 +660,7 @@ void PackInstallTask::installConfigs() auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!m_version.configs.sha1.isEmpty()) { auto rawSha1 = QByteArray::fromHex(m_version.configs.sha1.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); @@ -781,7 +783,7 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToExtract.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -793,7 +795,7 @@ void PackInstallTask::downloadMods() entry->setStale(true); modsToDecomp.insert(entry->getFullPath(), mod); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); @@ -807,7 +809,7 @@ void PackInstallTask::downloadMods() auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); - auto dl = Net::Download::makeCached(url, entry); + auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 83db642e7..011c4cdf2 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -2,6 +2,7 @@ #include "Json.h" #include "net/Upload.h" +#include "net/ApiDownload.h" #include "modplatform/modrinth/ModrinthPackIndex.h" @@ -94,7 +95,7 @@ void Flame::FileResolvingTask::netJobFinished() if(!hash.isEmpty()) { auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto output = std::make_shared(); - auto dl = Net::Download::makeByteArray(QUrl(url), output.get()); + auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output.get()); QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; }); @@ -169,7 +170,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() { auto projectId = mod->projectId; auto output = std::make_shared(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); - auto dl = Net::Download::makeByteArray(url, output.get()); + auto dl = Net::ApiDownload::makeByteArray(url, output.get()); qDebug() << "Fetching url slug for file:" << mod->fileName; QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index 92590a084..49ab06db2 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -9,6 +9,7 @@ #include "BuildConfig.h" #include "Json.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "net/Upload.h" Task::Ptr FlameAPI::matchFingerprints(const QList& fingerprints, QByteArray* response) @@ -40,7 +41,7 @@ auto FlameAPI::getModFileChangelog(int modId, int fileId) -> QString auto netJob = makeShared(QString("Flame::FileChangelog"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString("https://api.curseforge.com/v1/mods/%1/files/%2/changelog") .arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId))), response.get())); @@ -75,7 +76,7 @@ auto FlameAPI::getModDescription(int modId) -> QString auto netJob = makeShared(QString("Flame::ModDescription"), APPLICATION->network()); auto response = std::make_shared(); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString("https://api.curseforge.com/v1/mods/%1/description").arg(QString::number(modId)), response.get())); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { @@ -115,7 +116,7 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe auto response = std::make_shared(); ModPlatform::IndexedVersion ver; - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response.get())); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response.get())); QObject::connect(netJob.get(), &NetJob::succeeded, [response, args, &ver] { QJsonParseError parse_error{}; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 06a895027..5736f16e0 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -12,6 +12,8 @@ #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourceFolderModel.h" +#include "net/ApiDownload.h" + static FlameAPI api; bool FlameCheckUpdate::abort() @@ -32,7 +34,7 @@ ModPlatform::IndexedPack getProjectInfo(ModPlatform::IndexedVersion& ver_info) auto response = new QByteArray(); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(ver_info.addonId.toString()); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_project_job->addNetAction(dl); QObject::connect(get_project_job, &NetJob::succeeded, [response, &pack]() { @@ -76,7 +78,7 @@ ModPlatform::IndexedVersion getFileInfo(int addonId, int fileId) auto response = new QByteArray(); auto url = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(QString::number(addonId), QString::number(fileId)); - auto dl = Net::Download::makeByteArray(url, response); + auto dl = Net::ApiDownload::makeByteArray(url, response); get_file_info_job->addNetAction(dl); QObject::connect(get_file_info_job, &NetJob::succeeded, [response, &ver]() { diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index dae93d1c6..099e3ae4f 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -60,6 +60,8 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" +#include "net/ApiDownload.h" + const static QMap forgemap = { { "1.2.5", "3.4.9.171" }, { "1.4.2", "6.0.1.355" }, @@ -473,7 +475,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) case Flame::File::Type::Mod: { if (!result.url.isEmpty()) { qDebug() << "Will download" << result.url << "to" << path; - auto dl = Net::Download::makeFile(result.url, path); + auto dl = Net::ApiDownload::makeFile(result.url, path); m_files_job->addNetAction(dl); } break; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index a3c592fdc..0ff8ac8a4 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -9,6 +9,8 @@ #include "modplatform/ModIndex.h" +#include "net/ApiDownload.h" + Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks&& callbacks) const { auto search_url_optional = getSearchURL(args); @@ -22,7 +24,7 @@ Task::Ptr NetworkResourceAPI::searchProjects(SearchArgs&& args, SearchCallbacks& auto response = new QByteArray(); auto netJob = makeShared(QString("%1::Search").arg(debugName()), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(search_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(search_url), response)); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks]{ QJsonParseError parse_error{}; @@ -90,7 +92,7 @@ Task::Ptr NetworkResourceAPI::getProjectVersions(VersionSearchArgs&& args, Versi auto netJob = makeShared(QString("%1::Versions").arg(args.pack.name), APPLICATION->network()); auto response = new QByteArray(); - netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response)); QObject::connect(netJob.get(), &NetJob::succeeded, [response, callbacks, args] { QJsonParseError parse_error{}; @@ -122,7 +124,7 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, QByteArray* response) auto netJob = makeShared(QString("%1::GetProject").arg(addonId), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(project_url), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(project_url), response)); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; diff --git a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp index e8768c5cd..320ddd3e8 100644 --- a/launcher/modplatform/legacy_ftb/PackFetchTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackFetchTask.cpp @@ -40,6 +40,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { void PackFetchTask::fetch() @@ -51,11 +53,11 @@ void PackFetchTask::fetch() QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); - jobPtr->addNetAction(Net::Download::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData)); + jobPtr->addNetAction(Net::ApiDownload::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData)); QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml"); qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString(); - jobPtr->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData)); + jobPtr->addNetAction(Net::ApiDownload::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData)); QObject::connect(jobPtr.get(), &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished); QObject::connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed); @@ -72,7 +74,7 @@ void PackFetchTask::fetchPrivate(const QStringList & toFetch) { QByteArray *data = new QByteArray(); NetJob *job = new NetJob("Fetching private pack", m_network); - job->addNetAction(Net::Download::makeByteArray(privatePackBaseUrl.arg(packCode), data)); + job->addNetAction(Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode), data)); QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode] { diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 36c142acb..6da8fc882 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -48,6 +48,8 @@ #include "BuildConfig.h" #include "Application.h" +#include "net/ApiDownload.h" + namespace LegacyFTB { PackInstallTask::PackInstallTask(shared_qobject_ptr network, Modpack pack, QString version) @@ -76,7 +78,7 @@ void PackInstallTask::downloadPack() } else { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(archivePath); } - netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath)); + netJobContainer->addNetAction(Net::ApiDownload::makeFile(url, archivePath)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 29e3d129d..ab77ef119 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -8,12 +8,13 @@ #include "Json.h" #include "net/NetJob.h" #include "net/Upload.h" +#include "net/ApiDownload.h" Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, QByteArray* response) { auto netJob = makeShared(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); - netJob->addNetAction(Net::Download::makeByteArray( + netJob->addNetAction(Net::ApiDownload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format), response)); QObject::connect(netJob.get(), &NetJob::finished, [response] { delete response; }); @@ -111,7 +112,7 @@ Task::Ptr ModrinthAPI::getProjects(QStringList addonIds, QByteArray* response) c auto netJob = makeShared(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); QObject::connect(netJob.get(), &NetJob::finished, [response, netJob] { delete response; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index bb8227aa2..0edd696f7 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -12,6 +12,7 @@ #include "net/ChecksumValidator.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" @@ -238,7 +239,7 @@ bool ModrinthCreationTask::createInstance() } qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path; - auto dl = Net::Download::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)); m_files_job->addNetAction(dl); @@ -247,7 +248,7 @@ bool ModrinthCreationTask::createInstance() // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); connect(dl.get(), &NetAction::failed, [this, &file, file_path, param] { - auto ndl = Net::Download::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)); m_files_job->addNetAction(ndl); if (auto shared = param.lock()) shared->succeeded(); diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp index f07ca24af..21b0defc0 100644 --- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -23,6 +23,8 @@ #include "Application.h" +#include "net/ApiDownload.h" + Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion) { m_sourceUrl = sourceUrl; @@ -45,7 +47,7 @@ void Technic::SingleZipPackInstallTask::executeTask() auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); m_archivePath = entry->getFullPath(); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp index c26d6a5a6..0035c8808 100644 --- a/launcher/modplatform/technic/SolderPackInstallTask.cpp +++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -43,6 +43,7 @@ #include "TechnicPackProcessor.h" #include "SolderPackManifest.h" #include "net/ChecksumValidator.h" +#include "net/ApiDownload.h" Technic::SolderPackInstallTask::SolderPackInstallTask( shared_qobject_ptr network, @@ -72,7 +73,7 @@ void Technic::SolderPackInstallTask::executeTask() m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); - m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response)); + m_filesNetJob->addNetAction(Net::ApiDownload::makeByteArray(sourceUrl, &m_response)); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded); @@ -113,7 +114,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() for (const auto &mod : build.mods) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - auto dl = Net::Download::makeFile(mod.url, path); + auto dl = Net::ApiDownload::makeFile(mod.url, path); if (!mod.md5.isEmpty()) { auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1()); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5)); diff --git a/launcher/net/ApiDownload.h b/launcher/net/ApiDownload.h new file mode 100644 index 000000000..3ffda445a --- /dev/null +++ b/launcher/net/ApiDownload.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#pragma once + +#include "ApiHeaderProxy.h" +#include "Download.h" + +namespace Net { + +class ApiDownload : public Download { + public: + ApiDownload() : Download() + { + auto api_headers = new ApiHeaderProxy(); + addHeaderProxy(api_headers); + } + virtual ~ApiDownload() = default; +}; + +} // namespace Net diff --git a/launcher/net/ApiHeaderProxy.h b/launcher/net/ApiHeaderProxy.h new file mode 100644 index 000000000..0da92a745 --- /dev/null +++ b/launcher/net/ApiHeaderProxy.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#pragma once + +#include "Application.h" +#include "BuildConfig.h" +#include "net/HeaderProxy.h" + +namespace Net { + +class ApiHeaderProxy : public HeaderProxy { + public: + ApiHeaderProxy() : HeaderProxy(){}; + virtual ~ApiHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest& request) const override + { + QList hdrs; + if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { + hdrs.append({ "x-api-key", APPLICATION->getFlameAPIKey().toUtf8() }); + } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || + request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { + QString token = APPLICATION->getModrinthAPIToken(); + if (!token.isNull()) + hdrs.append({ "Authorization", token.toUtf8() }); + } + return hdrs; + }; +}; + +} // namespace Net diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 7f8d3a067..6c2173793 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -124,15 +124,11 @@ void Download::executeTask() } request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - // TODO remove duplication - if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { - request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || - request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { - QString token = APPLICATION->getModrinthAPIToken(); - if (!token.isNull()) - request.setRawHeader("Authorization", token.toUtf8()); + for ( auto header_proxy : m_headerProxies ) { + header_proxy->writeHeaders(request); } + // TODO remove duplication + #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) request.setTransferTimeout(); diff --git a/launcher/net/HeaderProxy.h b/launcher/net/HeaderProxy.h new file mode 100644 index 000000000..e94579e74 --- /dev/null +++ b/launcher/net/HeaderProxy.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#pragma once + +#include + +namespace Net { + +struct HeaderPair { + QByteArray headerName; + QByteArray headerValue; +}; + +class HeaderProxy { + public: + HeaderProxy(){}; + virtual ~HeaderProxy(){}; + + public: + virtual QList headers(const QNetworkRequest& request) const = 0; + + public: + void writeHeaders(QNetworkRequest& request) { + for (auto header : headers(request)) { + request.setRawHeader(header.headerName, header.headerValue); + } + }; +}; + +} // namespace Net diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index ab9322c26..acb0672fc 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -42,6 +42,8 @@ #include "QObjectPtr.h" #include "tasks/Task.h" +#include "HeaderProxy.h" + class NetAction : public Task { Q_OBJECT protected: @@ -56,13 +58,16 @@ class NetAction : public Task { void setNetwork(shared_qobject_ptr network) { m_network = network; } + void addHeaderProxy(Net::HeaderProxy* proxy) { m_headerProxies.push_back(std::shared_ptr(proxy)); } + protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; - virtual void sslErrors(const QList& errors) { + virtual void sslErrors(const QList& errors) + { int i = 1; for (auto error : errors) { qCritical() << "Network SSL Error #" << i << " : " << error.errorString(); @@ -70,7 +75,6 @@ class NetAction : public Task { qCritical() << "Certificate in question:\n" << cert.toText(); i++; } - }; public slots: @@ -91,4 +95,5 @@ class NetAction : public Task { /// source URL QUrl m_url; + std::vector> m_headerProxies; }; diff --git a/launcher/net/RawHeaderProxy.h b/launcher/net/RawHeaderProxy.h new file mode 100644 index 000000000..ffe6a81a9 --- /dev/null +++ b/launcher/net/RawHeaderProxy.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2022 flowln + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#pragma once + +#include "net/HeaderProxy.h" + +namespace Net { + +class ApiHeaderProxy : public HeaderProxy { + public: + ApiHeaderProxy() : HeaderProxy(){}; + virtual ~ApiHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest&) const override { return m_headers; }; + + void addHeader(const HeaderPair& header) { m_headers.append(header); } + void addHeader(const QByteArray& headerName, const QByteArray& headerValue) { m_headers.append({ headerName, headerValue }); } + void addHeaders(const QList& headers) { m_headers.append(headers); } + + private: + QList m_headers; +}; + +} // namespace Net diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index d0701a7ad..68119f06b 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -23,6 +23,8 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" +#include "net/ApiDownload.h" + /** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen. * ... thanks Qt. */ @@ -226,7 +228,7 @@ void ModrinthManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get())); + m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get())); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; @@ -369,7 +371,7 @@ void FlameManagedPackPage::parseManagedPack() QString id = m_inst->getManagedPackID(); - m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get())); + m_fetch_job->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get())); QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] { QJsonParseError parse_error{}; diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 472aa8515..ed741c881 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -15,7 +15,7 @@ #include "BuildConfig.h" #include "Json.h" -#include "net/Download.h" +#include "net/ApiDownload.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" @@ -276,7 +276,7 @@ std::optional ResourceModel::getIcon(QModelIndex& index, const QUrl& url) auto cache_entry = APPLICATION->metacache()->resolveEntry( metaEntryBase(), QString("logos/%1").arg(QString(QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); - auto icon_fetch_action = Net::Download::makeCached(url, cache_entry); + auto icon_fetch_action = Net::ApiDownload::makeCached(url, cache_entry); auto full_file_path = cache_entry->getFullPath(); connect(icon_fetch_action.get(), &NetAction::succeeded, this, [=] { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp index 9ad26f472..e064919eb 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlListModel.cpp @@ -20,6 +20,8 @@ #include #include +#include "net/ApiDownload.h" + namespace Atl { ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) @@ -88,7 +90,7 @@ void ListModel::request() auto netJob = makeShared("Atl::Request", APPLICATION->network()); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json"); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), &response)); jobPtr = netJob; jobPtr->start(); @@ -184,7 +186,7 @@ void ListModel::requestLogo(QString file, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0))); NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath] diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index cdb4532c8..edc73345f 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -43,6 +43,8 @@ #include "modplatform/atlauncher/ATLShareCode.h" #include "Application.h" +#include "net/ApiDownload.h" + AtlOptionalModListModel::AtlOptionalModListModel(QWidget* parent, ATLauncher::PackVersion version, QVector mods) : QAbstractListModel(parent) , m_version(version) @@ -152,7 +154,7 @@ Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex &index) const { void AtlOptionalModListModel::useShareCode(const QString& code) { m_jobPtr.reset(new NetJob("Atl::Request", APPLICATION->network())); auto url = QString(BuildConfig.ATL_API_BASE_URL + "share-codes/" + code); - m_jobPtr->addNetAction(Net::Download::makeByteArray(QUrl(url), &m_response)); + m_jobPtr->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), &m_response)); connect(m_jobPtr.get(), &NetJob::succeeded, this, &AtlOptionalModListModel::shareCodeSuccess); diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 5961ea026..0205552fb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -3,6 +3,8 @@ #include "Application.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include #include @@ -102,7 +104,7 @@ void ListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FlamePacks", QString("logos/%1").arg(logo.section(".", 0, 0))); auto job = new NetJob(QString("Flame Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { @@ -169,7 +171,7 @@ void ListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort + 1); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f9ac4a789..c98e40600 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -46,6 +46,8 @@ #include "ui/widgets/ProjectItem.h" #include "modplatform/flame/FlameAPI.h" +#include "net/ApiDownload.h" + static FlameAPI api; FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::FlamePage), dialog(dialog) @@ -132,7 +134,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev) auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network()); auto response = new QByteArray(); int addonId = current.addonId; - netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, addonId, curr] { if (addonId != current.addonId) { diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 2343b79f2..c391c1288 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -37,6 +37,7 @@ #include "Application.h" #include "net/HttpMetaCache.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" #include "StringUtils.h" #include @@ -254,7 +255,7 @@ void ListModel::requestLogo(QString file) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0))); NetJob *job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::finished, this, [this, file, fullPath] diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 346a00b0e..02c43acee 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -42,6 +42,8 @@ #include "minecraft/PackProfile.h" #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include namespace Modrinth { @@ -140,7 +142,7 @@ void ModpackListModel::performPaginatedSearch() .arg(currentSearchTerm) .arg(currentSort); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchAllUrl), &m_all_response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] { QJsonParseError parse_error_all{}; @@ -233,7 +235,7 @@ void ModpackListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0))); auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 0bb11d834..c450395c3 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -46,6 +46,8 @@ #include "ui/widgets/ProjectItem.h" +#include "net/ApiDownload.h" + #include #include #include @@ -127,7 +129,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1/project/%2").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { @@ -179,7 +181,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev) QString id = current.id; netJob->addNetAction( - Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); + Net::ApiDownload::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response)); QObject::connect(netJob, &NetJob::succeeded, this, [this, response, id, curr] { if (id != current.id) { diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 50f0c72d1..0790fb0bc 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -38,6 +38,8 @@ #include "BuildConfig.h" #include "Json.h" +#include "net/ApiDownload.h" + #include Technic::ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) @@ -134,7 +136,7 @@ void Technic::ListModel::performSearch() ).arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); searchMode = List; } - netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished); @@ -286,7 +288,7 @@ void Technic::ListModel::requestLogo(QString logo, QString url) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo)); NetJob *job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + job->addNetAction(Net::ApiDownload::makeCached(QUrl(url), entry)); auto fullPath = entry->getFullPath(); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 859da97e9..5aabcf34f 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -49,6 +49,8 @@ #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" +#include "net/ApiDownload.h" + TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog) { @@ -143,7 +145,7 @@ void TechnicPage::suggestCurrent() auto netJob = makeShared(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network()); QString slug = current.slug; - netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug] { jobPtr.reset(); @@ -249,7 +251,7 @@ void TechnicPage::metadataLoaded() auto netJob = makeShared(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network()); auto url = QString("%1/modpack/%2").arg(current.url, current.slug); - netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response)); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(url), &response)); QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded); diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp index 991b4a047..3aa3343d0 100644 --- a/launcher/ui/widgets/VariableSizedImageObject.cpp +++ b/launcher/ui/widgets/VariableSizedImageObject.cpp @@ -26,6 +26,7 @@ #include "Application.h" #include "net/NetJob.h" +#include "net/ApiDownload.h" enum FormatProperties { ImageData = QTextFormat::UserProperty + 1 }; @@ -97,7 +98,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, QString("images/%1").arg(QString(QCryptographicHash::hash(source.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex()))); auto job = new NetJob(QString("Load Image: %1").arg(source.fileName()), APPLICATION->network()); - job->addNetAction(Net::Download::makeCached(source, entry)); + job->addNetAction(Net::ApiDownload::makeCached(source, entry)); auto full_entry_path = entry->getFullPath(); auto source_url = source; From 0bda885bbff4944b017862a75d91e6b128ece221 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 3 Jun 2023 21:53:29 -0700 Subject: [PATCH 17/72] packaging: add appimage update capability Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 15 +++++++++++++++ .github/workflows/trigger_release.yml | 2 ++ 2 files changed, 17 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 691e257bf..cb4f5ebe1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -249,6 +249,8 @@ jobs: wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" + wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage" + ${{ github.workspace }}/.github/scripts/prepare_JREs.sh - name: Add QT_HOST_PATH var (Windows MSVC arm64) @@ -479,6 +481,12 @@ jobs: LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib" export LD_LIBRARY_PATH + chmod +x AppImageUpdate-x86_64.AppImage + ./AppImageUpdate-x86_64.AppImage --appimage-extract + rsync --ignore-existing -ar squashfs_root/usr/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ + + export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-${{ runner.os }}-*-x86_64.AppImage.zsync" + ./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg ## @@ -547,6 +555,13 @@ jobs: with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage + + - name: Upload AppImage Zsync (Linux) + if: runner.os == 'Linux' && matrix.qt_ver != 5 + uses: actions/upload-artifact@v3 + with: + name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync + path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync - name: ccache stats (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 3c56a38ea..31e8bde40 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -44,6 +44,7 @@ jobs: mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz @@ -89,6 +90,7 @@ jobs: PrismLauncher-Linux-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage + PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip From 89d8f9b82977dab87aecfe7282ca2b88dbf0991e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:43:41 -0700 Subject: [PATCH 18/72] packaging(linux): use vars when refrencing qt install dir Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb4f5ebe1..25abab9eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -469,7 +469,7 @@ jobs: cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk - cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines + cp -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/ @@ -483,6 +483,7 @@ jobs: chmod +x AppImageUpdate-x86_64.AppImage ./AppImageUpdate-x86_64.AppImage --appimage-extract + rm -rf squashfs_root/usr/share rsync --ignore-existing -ar squashfs_root/usr/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-${{ runner.os }}-*-x86_64.AppImage.zsync" From 2c959734463d3efd3cd62566e155453de879c7ac Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 10:36:34 -0700 Subject: [PATCH 19/72] packaging(appimage): dont use rsync Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 25abab9eb..ba80885d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -483,8 +483,14 @@ jobs: chmod +x AppImageUpdate-x86_64.AppImage ./AppImageUpdate-x86_64.AppImage --appimage-extract - rm -rf squashfs_root/usr/share - rsync --ignore-existing -ar squashfs_root/usr/ ${{ env.INSTALL_APPIMAGE_DIR }}/usr/ + + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional + mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins + + cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin + cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib + cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional + cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-${{ runner.os }}-*-x86_64.AppImage.zsync" From ab418162657e95e510a15a4d64e04b2af9856a8a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 12:27:01 -0700 Subject: [PATCH 20/72] packaging: use runner.workspace when copying files Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba80885d4..6dc94dbd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -386,8 +386,8 @@ jobs: cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") { - Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll - Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll + Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll + Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll } - name: Fetch codesign certificate (Windows) From 3531c5bb8c29ede39371a4430da0f0f17c026bf5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 16:58:46 -0700 Subject: [PATCH 21/72] packaging(appimage): put zsync in relase:x Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/trigger_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 31e8bde40..2041747f9 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -44,7 +44,7 @@ jobs: mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage - mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync + mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz From 98174b7a377aea8466c6a5fae97ee0db9b2419ae Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 2 Jun 2023 15:15:25 -0700 Subject: [PATCH 22/72] refactor: use Net tasks for github api download Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 30 ++++++- launcher/net/Download.cpp | 9 +- launcher/net/Download.h | 1 + .../updater/prismupdater/PrismUpdater.cpp | 89 +++++++++---------- launcher/updater/prismupdater/PrismUpdater.h | 17 ++-- 5 files changed, 90 insertions(+), 56 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 68fd5f7b7..58bd57a87 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -584,6 +584,7 @@ set(PRISMUPDATER_SOURCES updater/prismupdater/UpdaterDialogs.h updater/prismupdater/UpdaterDialogs.cpp updater/prismupdater/GitHubRelease.h + Json.h Json.cpp FileSystem.h @@ -595,6 +596,31 @@ set(PRISMUPDATER_SOURCES Version.h Version.cpp Markdown.h + + # Time + MMCTime.h + MMCTime.cpp + + + net/ByteArraySink.h + net/ChecksumValidator.h + net/Download.cpp + net/Download.h + net/FileSink.cpp + net/FileSink.h + net/HttpMetaCache.cpp + net/HttpMetaCache.h + net/Logging.h + net/Logging.cpp + net/NetAction.h + net/NetJob.cpp + net/NetJob.h + net/NetUtils.h + net/Sink.h + net/Validator.h + net/HeaderProxy.h + net/RawHeaderProxy.h + ) ######## Logging categories ######## @@ -1125,6 +1151,7 @@ endif() add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_link_libraries(Launcher_logic systeminfo Launcher_murmur2 @@ -1202,7 +1229,7 @@ install(TARGETS ${Launcher_Name} if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) # Updater - add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${PRISMUPDATER_UI}) + add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(prism_updater_logic systeminfo @@ -1213,6 +1240,7 @@ if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) Qt${QT_VERSION_MAJOR}::Network ${Launcher_QT_LIBS} cmark::cmark + Katabasis ) add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index c86452137..de6f43380 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -131,13 +131,12 @@ void Download::executeTask() return; } -#if defined (LAUNCHER_APPLICATION) - auto user_agent = APPLICATION->getUserAgent(); +#if defined(LAUNCHER_APPLICATION) + auto user_agent = APPLICATION->getUserAgent().toUtf8(); #else - auto user_agent = BuildConfig.USER_AGENT; + auto user_agent = BuildConfig.USER_AGENT.toUtf8(); #endif - - request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8()); + request.setHeader(QNetworkRequest::UserAgentHeader, user_agent); for ( auto& header_proxy : m_headerProxies ) { header_proxy->writeHeaders(request); diff --git a/launcher/net/Download.h b/launcher/net/Download.h index 19f675adc..65141a04c 100644 --- a/launcher/net/Download.h +++ b/launcher/net/Download.h @@ -59,6 +59,7 @@ class Download : public NetAction { public: ~Download() override = default; + #if defined(LAUNCHER_APPLICATION) static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; static auto makeByteArray(QUrl url, std::shared_ptr output, Options options = Option::NoOptions) -> Download::Ptr; static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 923b891f9..0e6620085 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -34,7 +34,9 @@ #include #include +#include #include +#include #include #include @@ -74,6 +76,9 @@ namespace fs = ghc::filesystem; #include "Json.h" #include "StringUtils.h" +#include "net/Download.h" +#include "net/RawHeaderProxy.h" + /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -205,8 +210,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } #endif } - m_network = new QNetworkAccessManager(); - + { // setup logging static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; auto moveFile = [](const QString& oldName, const QString& newName) { @@ -292,6 +296,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Log initialized."; } + + { // log debug program info qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); @@ -310,7 +316,14 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Paths set."; } - loadReleaseList(); + { // network + m_network = makeShared(new QNetworkAccessManager()); + qDebug() << "Detecting proxy settings..."; + QNetworkProxy proxy = QNetworkProxy::applicationProxy(); + m_network->setProxy(proxy); + } + + QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); } PrismUpdaterApp::~PrismUpdaterApp() @@ -329,9 +342,6 @@ PrismUpdaterApp::~PrismUpdaterApp() } #endif - m_network->deleteLater(); - if (m_reply) - m_reply->deleteLater(); } void PrismUpdaterApp::fail(const QString& reason) @@ -460,7 +470,7 @@ GitHubRelease PrismUpdaterApp::selectRelease() } else { releases = newerReleases(); } - + if (releases.isEmpty()) return {}; @@ -530,34 +540,40 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); - QNetworkRequest request(page_url); - request.setRawHeader("Accept", "application/vnd.github+json"); - request.setRawHeader("X-GitHub-Api-Version", "2022-11-28"); + auto responce = std::make_shared(); + auto download = Net::Download::makeByteArray(page_url, responce.get()); + download->setNetwork(m_network); + m_current_url = page_url; - QNetworkReply* rep = m_network->get(request); - m_reply = rep; - auto responce = new QByteArray(); - - connect(rep, &QNetworkReply::finished, this, [this, responce, per_page, api_url, page]() { - int num_found = parseReleasePage(responce); - delete responce; + auto githup_api_headers = new Net::RawHeaderProxy(); + githup_api_headers->addHeaders({ + { "Accept", "application/vnd.github+json" }, + { "X-GitHub-Api-Version", "2022-11-28" }, + }); + download->addHeaderProxy(githup_api_headers); + connect(download.get(), &Net::Download::succeeded, this, [this, responce, per_page, api_url, page]() { + int num_found = parseReleasePage(responce.get()); if (!(num_found < per_page)) { // there may be more, fetch next page downloadReleasePage(api_url, page + 1); } else { run(); } }); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &PrismUpdaterApp::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &WindowsUpdaterApp::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &PrismUpdaterApp::sslErrors); - connect(rep, &QNetworkReply::readyRead, this, [this, responce]() { - auto data = m_reply->readAll(); - responce->append(data); + connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError); + + + m_current_task.reset(download); + connect(download.get(), &Net::Download::finished, this, [this](){ + qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; + m_current_task.reset(); + m_current_url = ""; }); + + + QCoreApplication::processEvents(); + + QMetaObject::invokeMethod(download.get(), &Task::start, Qt::QueuedConnection); } int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) @@ -624,24 +640,7 @@ bool PrismUpdaterApp::needUpdate(const GitHubRelease& release) return current_ver < release.version; } -void PrismUpdaterApp::downloadError(QNetworkReply::NetworkError error) +void PrismUpdaterApp::downloadError(QString reason) { - if (error == QNetworkReply::OperationCanceledError) { - abort(QString("Aborted %1").arg(m_reply->url().toString())); - } else { - fail(QString("Network request Failed: %1 with reason %2").arg(m_reply->url().toString()).arg(error)); - } -} - -void PrismUpdaterApp::sslErrors(const QList& errors) -{ - int i = 1; - QString err_msg; - for (auto error : errors) { - err_msg.append(QString("Network request %1 SSL Error %2: %3\n").arg(m_reply->url().toString()).arg(i).arg(error.errorString())); - auto cert = error.certificate(); - err_msg.append(QString("Certificate in question:\n%1").arg(cert.toText())); - i++; - } - fail(err_msg); + fail(QString("Network request Failed: %1 with reason %2").arg(m_current_url).arg(reason)); } diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index da6b4bf71..52bb7e71b 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -36,6 +36,10 @@ #include #include +#include "QObjectPtr.h" +#include "net/Download.h" + + #define PRISM_EXTERNAL_EXE #include "FileSystem.h" @@ -58,7 +62,7 @@ class PrismUpdaterApp : public QApplication { void showFatalErrorMessage(const QString& title, const QString& content); void loadPrismVersionFromExe(const QString& exe_path); - + void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* responce); GitHubRelease getLatestRelease(); @@ -69,8 +73,10 @@ class PrismUpdaterApp : public QApplication { QList newerReleases(); QList nonDraftReleases(); - void downloadError(QNetworkReply::NetworkError error); - void sslErrors(const QList& errors); + public slots: + void downloadError(QString reason); + + private: const QString& root() { return m_rootPath; } @@ -95,8 +101,9 @@ class PrismUpdaterApp : public QApplication { QString m_prismGitCommit; Status m_status = Status::Starting; - QNetworkAccessManager* m_network; - QNetworkReply* m_reply; + shared_qobject_ptr m_network; + QString m_current_url; + Task::Ptr m_current_task; QList m_releases; public: From bee88b1c7fbba6556e9a3f4a1644556274a0f1cb Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 3 Jun 2023 18:41:54 -0700 Subject: [PATCH 23/72] feat(updater) select valid asset Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 1 + .../updater/prismupdater/GitHubRelease.cpp | 36 +++++ launcher/updater/prismupdater/GitHubRelease.h | 6 + .../updater/prismupdater/PrismUpdater.cpp | 130 +++++++++++++----- launcher/updater/prismupdater/PrismUpdater.h | 18 ++- .../updater/prismupdater/UpdaterDialogs.cpp | 68 +++++++++ .../updater/prismupdater/UpdaterDialogs.h | 20 +++ 7 files changed, 243 insertions(+), 36 deletions(-) create mode 100644 launcher/updater/prismupdater/GitHubRelease.cpp diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 58bd57a87..d9bd577a0 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -584,6 +584,7 @@ set(PRISMUPDATER_SOURCES updater/prismupdater/UpdaterDialogs.h updater/prismupdater/UpdaterDialogs.cpp updater/prismupdater/GitHubRelease.h + updater/prismupdater/GitHubRelease.cpp Json.h Json.cpp diff --git a/launcher/updater/prismupdater/GitHubRelease.cpp b/launcher/updater/prismupdater/GitHubRelease.cpp new file mode 100644 index 000000000..25e60e885 --- /dev/null +++ b/launcher/updater/prismupdater/GitHubRelease.cpp @@ -0,0 +1,36 @@ +#include "GitHubRelease.h" + +QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "GitHubReleaseAsset( " + "id: " << asset.id << ", " + "name " << asset.name << ", " + "label: " << asset.label << ", " + "content_type: " << asset.content_type << ", " + "size: " << asset.size << ", " + "created_at: " << asset.created_at << ", " + "updated_at: " << asset.updated_at << ", " + "browser_download_url: " << asset.browser_download_url << " " + ")"; + return debug; +} + +QDebug operator<<(QDebug debug, const GitHubRelease& rls) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "GitHubRelease( " + "id: " << rls.id << ", " + "name " << rls.name << ", " + "tag_name: " << rls.tag_name << ", " + "created_at: " << rls.created_at << ", " + "published_at: " << rls.published_at << ", " + "prerelease: " << rls.prerelease << ", " + "draft: " << rls.draft << ", " + "version" << rls.version << ", " + "body: " << rls.body << ", " + "assets: " << rls.assets << " " + ")"; + return debug; +} + diff --git a/launcher/updater/prismupdater/GitHubRelease.h b/launcher/updater/prismupdater/GitHubRelease.h index 1326a69f1..0c190333f 100644 --- a/launcher/updater/prismupdater/GitHubRelease.h +++ b/launcher/updater/prismupdater/GitHubRelease.h @@ -3,6 +3,8 @@ #include #include +#include + #include "Version.h" struct GitHubReleaseAsset { @@ -32,3 +34,7 @@ struct GitHubRelease { bool isValid() const { return id > 0; } }; + +QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls); +QDebug operator<<(QDebug debug, const GitHubRelease& rls); + diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 0e6620085..70ff1f81d 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -33,8 +33,8 @@ #include #include -#include #include +#include #include #include @@ -128,17 +128,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); - parser.addOptions({ { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory).", "directory" }, - { { "I", "install-version" }, "Install a spesfic version.", "version name" }, - { { "U", "update-url" }, "Update from the spesified repo.", "github repo url" }, - { { "e", "executable" }, "Path to the prismluancher executable.", "path" }, + parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, + { { "I", "install-version" }, "Install a spesfic version.", tr("version name") }, + { { "U", "update-url" }, tr("Update from the spesified repo."), tr("github repo url") }, { { "c", "check-only" }, - "Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error)." }, - { { "F", "force" }, "Force an update, even if one is not needed." }, - { { "l", "list" }, "List avalible releases." }, - { "debug", "Log debug to console." }, - { { "L", "latest" }, "Update to the latest avalible version." }, - { { "D", "allow-downgrade" }, "Allow the updater to downgrade to previous verisons." } }); + tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, + { { "F", "force" }, tr("Force an update, even if one is not needed.") }, + { { "l", "list" }, tr("List avalible releases.") }, + { "debug", tr("Log debug to console.") }, + { { "L", "latest" }, tr("Update to the latest avalible version.") }, + { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } }); parser.addHelpOption(); parser.addVersionOption(); @@ -146,13 +145,25 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logToConsole = parser.isSet("debug"); - auto prism_executable = parser.value("executable"); - if (prism_executable.isEmpty()) { - prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; + auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; #if defined(Q_OS_WIN32) - prism_executable += ".exe"; + prism_executable += ".exe"; #endif + + if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") + showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS")); + + if (!QFileInfo(prism_executable).isFile()) + showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable.")); + + if (prism_executable.startsWith("/tmp/.mount_")) { + m_appimage = true; + m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); + if (m_appimagePath.isEmpty()) + showFatalErrorMessage(tr("Unsupprted Instaltion"), + tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } + m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); @@ -210,7 +221,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } #endif } - + { // setup logging static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; auto moveFile = [](const QString& oldName, const QString& newName) { @@ -226,13 +237,13 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { - showFatalErrorMessage("The launcher data folder is not writable!", - QString("The launcher couldn't create a log file - the data folder is not writable.\n" - "\n" - "Make sure you have write permissions to the data folder.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem.") + showFatalErrorMessage(tr("The launcher data folder is not writable!"), + tr("The launcher couldn't create a log file - the data folder is not writable.\n" + "\n" + "Make sure you have write permissions to the data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") .arg(dataPath)); return; } @@ -296,8 +307,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Log initialized."; } - - { // log debug program info qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << "Updater" << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); @@ -316,7 +325,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "<> Paths set."; } - { // network + { // network m_network = makeShared(new QNetworkAccessManager()); qDebug() << "Detecting proxy settings..."; QNetworkProxy proxy = QNetworkProxy::applicationProxy(); @@ -341,7 +350,6 @@ PrismUpdaterApp::~PrismUpdaterApp() FreeConsole(); } #endif - } void PrismUpdaterApp::fail(const QString& reason) @@ -477,17 +485,79 @@ GitHubRelease PrismUpdaterApp::selectRelease() SelectReleaseDialog dlg(Version(m_prismVerison), releases); auto result = dlg.exec(); + if (result == QDialog::Rejected) { + return {}; + } GitHubRelease release = dlg.selectedRelease(); + + return release; +} + +QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRelease& release) +{ + QList valid; + + qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM << "portable:" << m_portable; + if (BuildConfig.BUILD_PLATFORM.isEmpty()) + qWarning() << "Build platform is not set!"; + for (auto asset : release.assets) { + if (!m_appimage && asset.name.toLower().endsWith("appimage")) + continue; + else if (m_appimage && !asset.name.toLower().endsWith("appimage")) + continue; + + bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); + bool for_portable = asset.name.toLower().contains("portable"); + if (((m_portable && for_portable) || (!m_portable && !for_portable)) && for_platform) { + valid.append(asset); + } + } + return valid; +} + +GitHubReleaseAsset PrismUpdaterApp::selectAsset(const QList& assets) +{ + SelectReleaseAssetDialog dlg(assets); + auto result = dlg.exec(); + if (result == QDialog::Rejected) { return {}; } - return release; + GitHubReleaseAsset asset = dlg.selectedAsset(); + return asset; } void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { qDebug() << "Updating to" << release.tag_name; + auto valid_assets = validReleaseArtifacts(release); + qDebug() << "vallid release assets:" << valid_assets; + + GitHubReleaseAsset selected_asset; + if (valid_assets.isEmpty()) { + showFatalErrorMessage(tr("No Valid Release Assets"), + tr("Github release %1 has no valid assets for this platform: %2") + .arg(release.tag_name) + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_portable))); + return; + } else if (valid_assets.length() > 1) { + selected_asset = selectAsset(valid_assets); + } else { + selected_asset = valid_assets.takeFirst(); + } + + if (! selected_asset.isValid()) { + showFatalErrorMessage("No version selected.", "No version was selected."); + return; + } + + qDebug() << "will intall" << selected_asset; +} + +QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) +{ + // TODO! (researching what to do with appimages) } void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) @@ -562,14 +632,12 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) }); connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError); - m_current_task.reset(download); - connect(download.get(), &Net::Download::finished, this, [this](){ + connect(download.get(), &Net::Download::finished, this, [this]() { qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; m_current_task.reset(); m_current_url = ""; }); - QCoreApplication::processEvents(); diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 52bb7e71b..1a461fcc8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -39,7 +39,6 @@ #include "QObjectPtr.h" #include "net/Download.h" - #define PRISM_EXTERNAL_EXE #include "FileSystem.h" @@ -65,25 +64,34 @@ class PrismUpdaterApp : public QApplication { void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* responce); - GitHubRelease getLatestRelease(); + bool needUpdate(const GitHubRelease& release); + + GitHubRelease getLatestRelease(); GitHubRelease selectRelease(); - void performUpdate(const GitHubRelease& release); - void printReleases(); QList newerReleases(); QList nonDraftReleases(); + void printReleases(); + + QList validReleaseArtifacts(const GitHubRelease& release); + GitHubReleaseAsset selectAsset(const QList& assets); + void performUpdate(const GitHubRelease& release); + + QFileInfo downloadAsset(const GitHubReleaseAsset& asset); + public slots: void downloadError(QString reason); private: - const QString& root() { return m_rootPath; } bool isPortable() { return m_portable; } QString m_rootPath; bool m_portable = false; + bool m_appimage = false; + QString m_appimagePath; QString m_prismExecutable; QUrl m_prismRepoUrl; Version m_userSelectedVersion; diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 5949194b2..72fdf3404 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -76,3 +76,71 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget ui->changelogTextBrowser->setHtml(body); } + + +SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QList& assets, QWidget* parent) + : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) +{ + ui->setupUi(this); + + ui->changelogTextBrowser->setOpenExternalLinks(true); + ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); + ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + + ui->versionsTree->setColumnCount(2); + + ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); + ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setStretchLastSection(false); + + ui->eplainLabel->setText(tr("Select a version to install.")); + + ui->changelogTextBrowser->setHidden(true); + + loadAssets(); + + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseAssetDialog::reject); +} + +SelectReleaseAssetDialog::~SelectReleaseAssetDialog() +{ + delete ui; +} + +void SelectReleaseAssetDialog::loadAssets() +{ + for (auto rls : m_assets) { + appendAsset(rls); + } +} + +void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset) +{ + auto rls_item = new QTreeWidgetItem(ui->versionsTree); + rls_item->setText(0, asset.name); + rls_item->setExpanded(true); + rls_item->setText(1, asset.updated_at.toString()); + rls_item->setData(0, Qt::UserRole, QVariant(asset.id)); + + ui->versionsTree->addTopLevelItem(rls_item); +} + +GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) { + int id = item->data(0, Qt::UserRole).toInt(); + GitHubReleaseAsset selected_asset; + for (auto asset: m_assets) { + if (asset.id == id) + selected_asset = asset; + } + return selected_asset; +} + +void SelectReleaseAssetDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) +{ + GitHubReleaseAsset asset = getAsset(current); + m_selectedAsset = asset; +} diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index 1449befbd..c5e31b5a4 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -31,3 +31,23 @@ class SelectReleaseDialog : public QDialog { Ui::SelectReleaseDialog* ui; }; + +class SelectReleaseAssetDialog : public QDialog { + Q_OBJECT + public: + explicit SelectReleaseAssetDialog(const QList& assets, QWidget* parent = 0); + ~SelectReleaseAssetDialog(); + + void loadAssets(); + void appendAsset(GitHubReleaseAsset const& asset); + GitHubReleaseAsset selectedAsset() { return m_selectedAsset; } + private slots: + GitHubReleaseAsset getAsset(QTreeWidgetItem* item); + void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); + + protected: + QList m_assets; + GitHubReleaseAsset m_selectedAsset; + + Ui::SelectReleaseDialog* ui; +}; From 50d5eb0621b0523db175672a511fd711d4018bac Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 4 Jun 2023 20:58:28 -0700 Subject: [PATCH 24/72] feat(updater): download new & back up old Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 2 +- .../updater/prismupdater/PrismUpdater.cpp | 180 +++++++++++++++--- launcher/updater/prismupdater/PrismUpdater.h | 13 +- 3 files changed, 166 insertions(+), 29 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d9bd577a0..41a916d12 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1228,7 +1228,7 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) +if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) # Updater add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 70ff1f81d..8df19bf80 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -22,6 +22,7 @@ #include "PrismUpdater.h" #include "BuildConfig.h" +#include "ui/dialogs/ProgressDialog.h" #include #include @@ -136,7 +137,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar { { "F", "force" }, tr("Force an update, even if one is not needed.") }, { { "l", "list" }, tr("List avalible releases.") }, { "debug", tr("Log debug to console.") }, - { { "L", "latest" }, tr("Update to the latest avalible version.") }, + { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } }); parser.addHelpOption(); @@ -145,10 +146,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logToConsole = parser.isSet("debug"); - auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; -#if defined(Q_OS_WIN32) - prism_executable += ".exe"; -#endif + auto prism_executable = QCoreApplication::applicationFilePath(); if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS")); @@ -157,13 +155,15 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable.")); if (prism_executable.startsWith("/tmp/.mount_")) { - m_appimage = true; + m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); if (m_appimagePath.isEmpty()) showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } + m_isFlatpak = DesktopServices::isFlatpak(); + m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); @@ -178,7 +178,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar if (!user_version.isEmpty()) { m_userSelectedVersion = Version(user_version); } - m_updateLatest = parser.isSet("latest"); + m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); QString origcwdPath = QDir::currentPath(); @@ -217,7 +217,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { dataPath = m_rootPath; adjustedBy = "Portable data path"; - m_portable = true; + m_isPortable = true; } #endif } @@ -411,6 +411,24 @@ void PrismUpdaterApp::run() return exit(0); } + if (m_isFlatpak) { + showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is avalible are not " + "supported when running the flatpak verison of Prism Launcher.")); + return; + } + if (m_isAppimage) { + bool result = true; + if (need_update) + result = callAppimageUpdate(); + return exit(result ? 0 : 1); + } + + if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) { + showFatalErrorMessage(tr("Updating Not Supported"), + tr("Updating non-portable linux instalations is not supported. Please use your system package manager")); + return; + } + if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) { GitHubRelease update_release = latest; if (!m_userSelectedVersion.isEmpty()) { @@ -428,7 +446,7 @@ void PrismUpdaterApp::run() QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString())); return; } - } else if (!m_updateLatest) { + } else if (m_selectUI) { update_release = selectRelease(); if (!update_release.isValid()) { showFatalErrorMessage("No version selected.", "No version was selected."); @@ -497,18 +515,19 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel { QList valid; - qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM << "portable:" << m_portable; + qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM + << "portable:" << m_isPortable; if (BuildConfig.BUILD_PLATFORM.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { - if (!m_appimage && asset.name.toLower().endsWith("appimage")) + if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) continue; - else if (m_appimage && !asset.name.toLower().endsWith("appimage")) + else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) continue; bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); bool for_portable = asset.name.toLower().contains("portable"); - if (((m_portable && for_portable) || (!m_portable && !for_portable)) && for_platform) { + if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { valid.append(asset); } } @@ -536,35 +555,148 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release) GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { - showFatalErrorMessage(tr("No Valid Release Assets"), - tr("Github release %1 has no valid assets for this platform: %2") - .arg(release.tag_name) - .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_portable))); - return; + return showFatalErrorMessage(tr("No Valid Release Assets"), + tr("Github release %1 has no valid assets for this platform: %2") + .arg(release.tag_name) + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { selected_asset = valid_assets.takeFirst(); } - if (! selected_asset.isValid()) { - showFatalErrorMessage("No version selected.", "No version was selected."); - return; + if (!selected_asset.isValid()) { + return showFatalErrorMessage(tr("No version selected."), tr("No version was selected.")); } - qDebug() << "will intall" << selected_asset; + qDebug() << "will install" << selected_asset; + auto file = downloadAsset(selected_asset); + + if (!file.exists()) { + return showFatalErrorMessage(tr("Failed to Download"), tr("Failed to download the selected asset.")); + } + + performInstall(file); } QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) { - // TODO! (researching what to do with appimages) + auto temp_dir = QDir::tempPath(); + auto file_url = QUrl(asset.browser_download_url); + auto out_file_path = FS::PathCombine(temp_dir, file_url.fileName()); + + auto download = Net::Download::makeFile(file_url, out_file_path); + + auto progress_dialog = ProgressDialog(); + + if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) + showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); + + QFileInfo out_file(out_file_path); + return out_file; +} + +bool PrismUpdaterApp::callAppimageUpdate() +{ + QProcess proc = QProcess(); + proc.setProgram("AppImageUpdate"); + proc.setArguments({ QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")) }); + return proc.startDetached(); +} + +void PrismUpdaterApp::performInstall(QFileInfo file) +{ + // TODO setup marker file + if (m_isPortable) { + unpackAndInstall(file); + } else { + QProcess proc = QProcess(); + proc.setProgram(file.absoluteFilePath()); + bool result = proc.startDetached(); + exit(result ? 0 : 1); + } +} + +void PrismUpdaterApp::unpackAndInstall(QFileInfo to_install_file) +{ + backupAppDir(); + // TODO: uppack (rename access failures) + +} + +void PrismUpdaterApp::backupAppDir() +{ + auto manifest_path = FS::PathCombine(QCoreApplication::applicationDirPath(), "manifest.txt"); + QFileInfo manifest(manifest_path); + + QStringList file_list; + if (manifest.isFile()) { + // load manifest from file + try { + auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); + file_list.append(contents.split('\n')); + } catch (FS::FileSystemException) { + } + } + + if (file_list.isEmpty()) { + // best guess + if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") { + file_list.append({ "PrismLauncher", "bin/*", "share/*", "lib/*" }); + } else { // windows by process of elimination + file_list.append({ + "jars/*", + "prismlauncher.exe", + "prismlauncher_filelink.exe", + "qtlogging.ini", + "imageformats/*", + "iconengines/*", + "platforms/*", + "styles/*", + "styles/*", + "tls/*", + }); + } + file_list.append("portable.txt"); + qDebug() << "manifest.txt empty or missing. makking best guess at files to back up."; + } + qDebug() << "Backing up" << file_list; + + QDir app_dir = QCoreApplication::applicationDirPath(); + auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVerison + ":" + m_prismGitCommit); + FS::ensureFolderPathExists(backup_dir); + + for (auto glob : file_list) { + QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories); + while (iter.hasNext()) { + auto to_bak_file = iter.next(); + auto rel_path = app_dir.relativeFilePath(to_bak_file); + auto bak_path = FS::PathCombine(backup_dir, rel_path); + + if (QFileInfo(to_bak_file).isFile()) { + qDebug() << "Backing up and then removing" << to_bak_file; + FS::ensureFilePathExists(bak_path); + auto result = FS::copy(to_bak_file, bak_path)(); + if (!result) { + qWarning() << "Failed to backup" << to_bak_file << "to" << bak_path; + } else { + if (!FS::deletePath(to_bak_file)) + qWarning() << "Failed to remove" << to_bak_file; + } + } + } + } } void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.start(exe_path, { "-v" }); - proc.waitForFinished(); + if (!proc.waitForStarted(5000)) // wait 5 seconds to start + return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read verison.")); + if (!proc.waitForFinished(5000)) + return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); auto out = proc.readAll(); auto lines = out.split('\n'); if (lines.length() < 2) diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 1a461fcc8..8404969a5 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -77,8 +77,12 @@ class PrismUpdaterApp : public QApplication { QList validReleaseArtifacts(const GitHubRelease& release); GitHubReleaseAsset selectAsset(const QList& assets); void performUpdate(const GitHubRelease& release); + void performInstall(QFileInfo file); + void unpackAndInstall(QFileInfo file); + void backupAppDir(); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); + bool callAppimageUpdate(); public slots: void downloadError(QString reason); @@ -86,11 +90,12 @@ class PrismUpdaterApp : public QApplication { private: const QString& root() { return m_rootPath; } - bool isPortable() { return m_portable; } + bool isPortable() { return m_isPortable; } QString m_rootPath; - bool m_portable = false; - bool m_appimage = false; + bool m_isPortable = false; + bool m_isAppimage = false; + bool m_isFlatpak = false; QString m_appimagePath; QString m_prismExecutable; QUrl m_prismRepoUrl; @@ -98,7 +103,7 @@ class PrismUpdaterApp : public QApplication { bool m_checkOnly; bool m_forceUpdate; bool m_printOnly; - bool m_updateLatest; + bool m_selectUI; bool m_allowDowngrade; QString m_prismBinaryName; From 44bc60021d0e84b9a62e406c4ea69f0257573e3b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 18 Jun 2023 22:34:23 -0700 Subject: [PATCH 25/72] feat(updater): unpack portable archive Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 5 +- launcher/FileSystem.cpp | 34 ++++ launcher/FileSystem.h | 12 ++ .../updater/prismupdater/PrismUpdater.cpp | 181 ++++++++++++------ launcher/updater/prismupdater/PrismUpdater.h | 12 +- 5 files changed, 186 insertions(+), 58 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 41a916d12..5cdb0383e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -597,12 +597,15 @@ set(PRISMUPDATER_SOURCES Version.h Version.cpp Markdown.h + + # Zip + MMCZip.h + MMCZip.cpp # Time MMCTime.h MMCTime.cpp - net/ByteArraySink.h net/ChecksumValidator.h net/Download.cpp diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 835ad925d..2a0ca76b5 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -193,6 +193,40 @@ void write(const QString& filename, const QByteArray& data) } } +void appendSafe(const QString& filename, const QByteArray& data) +{ + ensureExists(QFileInfo(filename).dir()); + QByteArray buffer; + try { + buffer = read(filename); + } catch (FileSystemException) { + buffer = QByteArray(); + } + buffer.append(data); + QSaveFile file(filename); + if (!file.open(QSaveFile::WriteOnly)) { + throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); + } + if (buffer.size() != file.write(buffer)) { + throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); + } + if (!file.commit()) { + throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString()); + } +} + +void append(const QString& filename, const QByteArray& data) +{ + ensureExists(QFileInfo(filename).dir()); + QFile file(filename); + if (!file.open(QFile::Append)) { + throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); + } + if (data.size() != file.write(data)) { + throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); + } +} + QByteArray read(const QString& filename) { QFile file(filename); diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index cb581d0c5..ca26c1ee7 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -60,6 +60,18 @@ class FileSystemException : public ::Exception { */ void write(const QString& filename, const QByteArray& data); + +/** + * append data to a file safely + */ +void appendSafe(const QString& filename, const QByteArray& data); + +/** + * append data to a file + */ +void append(const QString& filename, const QByteArray& data); + + /** * read data from a file safely\ */ diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 8df19bf80..001ecb10c 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -80,6 +80,8 @@ namespace fs = ghc::filesystem; #include "net/Download.h" #include "net/RawHeaderProxy.h" +#include + /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -103,14 +105,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar #if defined Q_OS_WIN32 // attach the parent console if (AttachConsole(ATTACH_PARENT_PROCESS)) { + FILE* _stream; + errno_t err; // if attach succeeds, reopen and sync all the i/o - if (freopen("CON", "w", stdout)) { + if (err = freopen_s(&_stream, "CON", "w", stdout); err == 0) { std::cout.sync_with_stdio(); } - if (freopen("CON", "w", stderr)) { + if (err = freopen_s(&_stream, "CON", "w", stderr); err == 0) { std::cerr.sync_with_stdio(); } - if (freopen("CON", "r", stdin)) { + if (err = freopen_s(&_stream, "CON", "r", stdin); err == 0) { std::cin.sync_with_stdio(); } auto out = GetStdHandle(STD_OUTPUT_HANDLE); @@ -125,20 +129,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar setApplicationName(BuildConfig.LAUNCHER_NAME + "Updater"); setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); - // Commandline parsing + // Command line parsing QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, - { { "I", "install-version" }, "Install a spesfic version.", tr("version name") }, - { { "U", "update-url" }, tr("Update from the spesified repo."), tr("github repo url") }, + { { "I", "install-version" }, "Install a specific version.", tr("version name") }, + { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, { { "c", "check-only" }, tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, { { "F", "force" }, tr("Force an update, even if one is not needed.") }, - { { "l", "list" }, tr("List avalible releases.") }, + { { "l", "list" }, tr("List available releases.") }, { "debug", tr("Log debug to console.") }, { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, - { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } }); + { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); parser.addHelpOption(); parser.addVersionOption(); @@ -149,16 +153,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar auto prism_executable = QCoreApplication::applicationFilePath(); if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") - showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS")); + showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); if (!QFileInfo(prism_executable).isFile()) - showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable.")); + showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); if (prism_executable.startsWith("/tmp/.mount_")) { m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); if (m_appimagePath.isEmpty()) - showFatalErrorMessage(tr("Unsupprted Instaltion"), + showFatalErrorMessage(tr("Unsupported Installation"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } @@ -181,7 +185,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); - QString origcwdPath = QDir::currentPath(); + QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); { // find data director @@ -200,22 +204,21 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } QString adjustedBy; - QString dataPath; // change folder QString dirParam = parser.value("dir"); if (!dirParam.isEmpty()) { - // the dir param. it makes multimc data path point to whatever the user specified + // the dir param. it makes prism launcher data path point to whatever the user specified // on command line adjustedBy = "Command line"; - dataPath = dirParam; + m_dataPath = dirParam; } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); - dataPath = foo.absolutePath(); + m_dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; #ifndef Q_OS_MACOS if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { - dataPath = m_rootPath; + m_dataPath = m_rootPath; adjustedBy = "Portable data path"; m_isPortable = true; } @@ -230,8 +233,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QFile::remove(oldName); }; - moveFile(logBase.arg(3), logBase.arg(4)); - moveFile(logBase.arg(2), logBase.arg(3)); moveFile(logBase.arg(1), logBase.arg(2)); moveFile(logBase.arg(0), logBase.arg(1)); @@ -244,7 +245,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar "(%1)\n" "\n" "The launcher cannot continue until you fix this problem.") - .arg(dataPath)); + .arg(m_dataPath)); return; } qInstallMessageHandler(appDebugOutput); @@ -266,7 +267,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar bool foundLoggingRules = false; auto logRulesFile = QStringLiteral("qtlogging.ini"); - auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); + auto logRulesPath = FS::PathCombine(m_dataPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); @@ -314,7 +315,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; if (adjustedBy.size()) { - qDebug() << "Work dir before adjustment : " << origcwdPath; + qDebug() << "Work dir before adjustment : " << origCwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Adjusted by : " << adjustedBy; } else { @@ -394,7 +395,7 @@ void PrismUpdaterApp::run() loadPrismVersionFromExe(m_prismExecutable); m_status = Succeeded; - qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVerison; + qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVersion; qDebug() << "Version major:" << m_prismVersionMajor; qDebug() << "Verison minor:" << m_prismVersionMinor; qDebug() << "Verison channel:" << m_prsimVersionChannel; @@ -412,8 +413,8 @@ void PrismUpdaterApp::run() } if (m_isFlatpak) { - showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is avalible are not " - "supported when running the flatpak verison of Prism Launcher.")); + showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is available are not " + "supported when running the flatpak version of Prism Launcher.")); return; } if (m_isAppimage) { @@ -425,7 +426,7 @@ void PrismUpdaterApp::run() if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) { showFatalErrorMessage(tr("Updating Not Supported"), - tr("Updating non-portable linux instalations is not supported. Please use your system package manager")); + tr("Updating non-portable linux installations is not supported. Please use your system package manager")); return; } @@ -443,7 +444,7 @@ void PrismUpdaterApp::run() if (!found) { showFatalErrorMessage( "No release for version!", - QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString())); + QString("Can not find a github release for specified version %1").arg(m_userSelectedVersion.toString())); return; } } else if (m_selectUI) { @@ -481,7 +482,7 @@ QList PrismUpdaterApp::newerReleases() { QList newer; for (auto rls : nonDraftReleases()) { - if (rls.version > m_prismVerison) + if (rls.version > m_prismVersion) newer.append(rls); } return newer; @@ -500,7 +501,7 @@ GitHubRelease PrismUpdaterApp::selectRelease() if (releases.isEmpty()) return {}; - SelectReleaseDialog dlg(Version(m_prismVerison), releases); + SelectReleaseDialog dlg(Version(m_prismVersion), releases); auto result = dlg.exec(); if (result == QDialog::Rejected) { @@ -515,7 +516,7 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel { QList valid; - qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM + qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_PLATFORM << "portable:" << m_isPortable; if (BuildConfig.BUILD_PLATFORM.isEmpty()) qWarning() << "Build platform is not set!"; @@ -549,9 +550,10 @@ GitHubReleaseAsset PrismUpdaterApp::selectAsset(const QList& void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { + m_install_release = release; qDebug() << "Updating to" << release.tag_name; auto valid_assets = validReleaseArtifacts(release); - qDebug() << "vallid release assets:" << valid_assets; + qDebug() << "valid release assets:" << valid_assets; GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { @@ -604,12 +606,32 @@ bool PrismUpdaterApp::callAppimageUpdate() return proc.startDetached(); } +void PrismUpdaterApp::clearUpdateLog() +{ + auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + QFile::remove(update_log_path); +} + +void PrismUpdaterApp::logUpdate(const QString& msg) +{ + qDebug() << msg; + auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + FS::append(update_log_path, QStringLiteral("%1\n").arg(msg).toUtf8()); +} + void PrismUpdaterApp::performInstall(QFileInfo file) { + auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); + FS::write(update_lock_path, QStringLiteral("FROM=%1\nTO=%2\n").arg(m_prismVersion).arg(m_install_release.tag_name).toUtf8()); + clearUpdateLog(); + + logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); // TODO setup marker file if (m_isPortable) { + logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { + logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); QProcess proc = QProcess(); proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); @@ -617,21 +639,24 @@ void PrismUpdaterApp::performInstall(QFileInfo file) } } -void PrismUpdaterApp::unpackAndInstall(QFileInfo to_install_file) +void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) { + logUpdate(tr("Backing up install")); backupAppDir(); - // TODO: uppack (rename access failures) - + auto loc = unpackArchive(archive); + // TODO: unpack (rename access failures) } void PrismUpdaterApp::backupAppDir() { - auto manifest_path = FS::PathCombine(QCoreApplication::applicationDirPath(), "manifest.txt"); + auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); QFileInfo manifest(manifest_path); QStringList file_list; if (manifest.isFile()) { // load manifest from file + + logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); try { auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); file_list.append(contents.split('\n')); @@ -658,12 +683,12 @@ void PrismUpdaterApp::backupAppDir() }); } file_list.append("portable.txt"); - qDebug() << "manifest.txt empty or missing. makking best guess at files to back up."; + logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } - qDebug() << "Backing up" << file_list; + logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); QDir app_dir = QCoreApplication::applicationDirPath(); - auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVerison + ":" + m_prismGitCommit); + auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVersion + ":" + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); for (auto glob : file_list) { @@ -675,26 +700,72 @@ void PrismUpdaterApp::backupAppDir() auto bak_path = FS::PathCombine(backup_dir, rel_path); if (QFileInfo(to_bak_file).isFile()) { - qDebug() << "Backing up and then removing" << to_bak_file; + logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); FS::ensureFilePathExists(bak_path); auto result = FS::copy(to_bak_file, bak_path)(); if (!result) { - qWarning() << "Failed to backup" << to_bak_file << "to" << bak_path; + logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); } else { if (!FS::deletePath(to_bak_file)) - qWarning() << "Failed to remove" << to_bak_file; + logUpdate(tr("Failed to remove %1").arg(to_bak_file)); } } } } } +std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) +{ + auto temp_extract_path = FS::PathCombine(m_dataPath, "prism_launcher_update_release"); + FS::ensureFolderPathExists(temp_extract_path); + auto tmp_extract_dir = QDir(temp_extract_path); + + if (archive.fileName().endsWith(".zip")) { + auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath()); + if (result) { + logUpdate( + tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); + } else { + logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); + showFatalErrorMessage("Failed to extract archive", + tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); + return {}; + } + + } else if (archive.fileName().endsWith(".tar.gz")) { + QString cmd = "tar"; + QStringList args = { "-xvf", archive.absoluteFilePath(), "-C", tmp_extract_dir.absolutePath() }; + logUpdate(tr("Running: `%1 %2`").arg(cmd).arg(args.join(" "))); + QProcess proc = QProcess(); + proc.start(cmd, args); + if (!proc.waitForStarted(5000)) { // wait 5 seconds to start + showFatalErrorMessage(tr("Failed extract archive"), + tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" "))); + return {}; + } + auto result = proc.waitForFinished(5000); + auto out = proc.readAll(); + logUpdate(out); + if (!result) { + showFatalErrorMessage(tr("Failed to extract archive"), tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" "))); + return {}; + } + + } else { + logUpdate(tr("Unknown archive format for %1").arg(archive.absoluteFilePath())); + showFatalErrorMessage("Can not extract", QStringLiteral("Unknown archive format %1").arg(archive.absoluteFilePath())); + return {}; + } + + return tmp_extract_dir; +} + void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.start(exe_path, { "-v" }); if (!proc.waitForStarted(5000)) // wait 5 seconds to start - return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read verison.")); + return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); if (!proc.waitForFinished(5000)) return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); auto out = proc.readAll(); @@ -707,7 +778,7 @@ void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) return; m_prismBinaryName = first_parts.takeFirst(); auto version = first_parts.takeFirst(); - m_prismVerison = version; + m_prismVersion = version; if (version.contains('-')) { auto index = version.indexOf('-'); m_prsimVersionChannel = version.mid(index + 1); @@ -742,20 +813,20 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); - auto responce = std::make_shared(); - auto download = Net::Download::makeByteArray(page_url, responce.get()); + auto response = std::make_shared(); + auto download = Net::Download::makeByteArray(page_url, response.get()); download->setNetwork(m_network); m_current_url = page_url; - auto githup_api_headers = new Net::RawHeaderProxy(); - githup_api_headers->addHeaders({ + auto github_api_headers = new Net::RawHeaderProxy(); + github_api_headers->addHeaders({ { "Accept", "application/vnd.github+json" }, { "X-GitHub-Api-Version", "2022-11-28" }, }); - download->addHeaderProxy(githup_api_headers); + download->addHeaderProxy(github_api_headers); - connect(download.get(), &Net::Download::succeeded, this, [this, responce, per_page, api_url, page]() { - int num_found = parseReleasePage(responce.get()); + connect(download.get(), &Net::Download::succeeded, this, [this, response, per_page, api_url, page]() { + int num_found = parseReleasePage(response.get()); if (!(num_found < per_page)) { // there may be more, fetch next page downloadReleasePage(api_url, page + 1); } else { @@ -766,7 +837,7 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) m_current_task.reset(download); connect(download.get(), &Net::Download::finished, this, [this]() { - qDebug() << "Download" << m_current_task->getUid().toString() << "finsihed"; + qDebug() << "Download" << m_current_task->getUid().toString() << "finished"; m_current_task.reset(); m_current_url = ""; }); @@ -776,13 +847,13 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) QMetaObject::invokeMethod(download.get(), &Task::start, Qt::QueuedConnection); } -int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) +int PrismUpdaterApp::parseReleasePage(const QByteArray* response) { - if (responce->isEmpty()) // empty page + if (response->isEmpty()) // empty page return 0; int num_releases = 0; try { - auto doc = Json::requireDocument(*responce); + auto doc = Json::requireDocument(*response); auto release_list = Json::requireArray(doc); for (auto release_json : release_list) { auto release_obj = Json::requireObject(release_json); @@ -817,7 +888,7 @@ int PrismUpdaterApp::parseReleasePage(const QByteArray* responce) } } catch (Json::JsonException& e) { auto err_msg = - QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(responce->toStdString())); + QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(response->toStdString())); fail(err_msg); } return num_releases; diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 8404969a5..af7af7dee 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -63,7 +63,7 @@ class PrismUpdaterApp : public QApplication { void loadPrismVersionFromExe(const QString& exe_path); void downloadReleasePage(const QString& api_url, int page); - int parseReleasePage(const QByteArray* responce); + int parseReleasePage(const QByteArray* response); bool needUpdate(const GitHubRelease& release); @@ -80,6 +80,7 @@ class PrismUpdaterApp : public QApplication { void performInstall(QFileInfo file); void unpackAndInstall(QFileInfo file); void backupAppDir(); + std::optional unpackArchive(QFileInfo file); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); bool callAppimageUpdate(); @@ -92,7 +93,12 @@ class PrismUpdaterApp : public QApplication { bool isPortable() { return m_isPortable; } + void clearUpdateLog(); + void logUpdate(const QString& msg); + + QString m_rootPath; + QString m_dataPath; bool m_isPortable = false; bool m_isAppimage = false; bool m_isFlatpak = false; @@ -107,12 +113,14 @@ class PrismUpdaterApp : public QApplication { bool m_allowDowngrade; QString m_prismBinaryName; - QString m_prismVerison; + QString m_prismVersion; int m_prismVersionMajor; int m_prismVersionMinor; QString m_prsimVersionChannel; QString m_prismGitCommit; + GitHubRelease m_install_release; + Status m_status = Status::Starting; shared_qobject_ptr m_network; QString m_current_url; From 516dd6bd1ad8f9ff939b72f87e88c5803f82eaaf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 05:02:48 -0700 Subject: [PATCH 26/72] support windows console Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 764 +++++++++--------- launcher/CMakeLists.txt | 11 +- .../updater/prismupdater/PrismUpdater.cpp | 287 +++++-- launcher/updater/prismupdater/PrismUpdater.h | 10 +- .../updater/prismupdater/UpdaterDialogs.cpp | 8 +- 5 files changed, 602 insertions(+), 478 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 20eabc8af..f094a8ec9 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -48,27 +48,27 @@ #include "pathmatcher/MultiMatcher.h" #include "pathmatcher/SimplePrefixMatcher.h" #include "settings/INIFile.h" -#include "ui/MainWindow.h" #include "ui/InstanceWindow.h" +#include "ui/MainWindow.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/AccessibleInstanceView.h" #include "ui/pages/BasePageProvider.h" -#include "ui/pages/global/LauncherPage.h" -#include "ui/pages/global/MinecraftPage.h" +#include "ui/pages/global/APIPage.h" +#include "ui/pages/global/AccountListPage.h" +#include "ui/pages/global/CustomCommandsPage.h" +#include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/JavaPage.h" #include "ui/pages/global/LanguagePage.h" +#include "ui/pages/global/LauncherPage.h" +#include "ui/pages/global/MinecraftPage.h" #include "ui/pages/global/ProxyPage.h" -#include "ui/pages/global/ExternalToolsPage.h" -#include "ui/pages/global/AccountListPage.h" -#include "ui/pages/global/APIPage.h" -#include "ui/pages/global/CustomCommandsPage.h" -#include "ui/setupwizard/SetupWizard.h" -#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h" +#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/PasteWizardPage.h" +#include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/ThemeWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" @@ -82,20 +82,20 @@ #include #include -#include #include #include +#include #include #include -#include -#include +#include +#include #include #include +#include #include -#include #include +#include #include -#include #include "InstanceList.h" #include "MTPixmapCache.h" @@ -115,32 +115,35 @@ #include "settings/INISettingsObject.h" #include "settings/Setting.h" -#include "translations/TranslationsModel.h" #include "meta/Index.h" +#include "translations/TranslationsModel.h" -#include #include +#include #include #include #ifdef Q_OS_LINUX #include -#include "gamemode_client.h" #include "MangoHud.h" +#include "gamemode_client.h" #endif #ifdef Q_OS_MAC #include "updater/MacSparkleUpdater.h" #endif - #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif -#include +#include +#include #include +#include +#include + #endif #define STRINGIFY(x) #x @@ -153,10 +156,10 @@ PixmapCache* PixmapCache::s_instance = nullptr; namespace { /** This is used so that we can output to the log file in addition to the CLI. */ -void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { static std::mutex loggerMutex; - const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe + const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe QString out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; @@ -167,31 +170,110 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QSt fflush(stderr); } -} +} // namespace -Application::Application(int &argc, char **argv) : QApplication(argc, argv) +#if defined Q_OS_WIN32 + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apearently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "r"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdin)); + if (dup2Result == 0) { + setvbuf(stdin, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + setvbuf(stdout, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stderr)); + if (dup2Result == 0) { + setvbuf(stderr, NULL, _IONBF, 0); + } + } + } + } + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} +#endif + +Application::Application(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 // attach the parent console - if(AttachConsole(ATTACH_PARENT_PROCESS)) - { - // if attach succeeds, reopen and sync all the i/o - if(freopen("CON", "w", stdout)) - { - std::cout.sync_with_stdio(); - } - if(freopen("CON", "w", stderr)) - { - std::cerr.sync_with_stdio(); - } - if(freopen("CON", "r", stdin)) - { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle (STD_OUTPUT_HANDLE); - DWORD written; - const char * endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } #endif @@ -210,15 +292,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QCommandLineParser parser; parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME); - parser.addOptions({ - {{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"}, - {{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"}, - {{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"}, - {{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"}, - {"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"}, - {{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"}, - {"show", "Opens the window for the specified instance (by instance ID)", "show"} - }); + parser.addOptions( + { { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" }, + { { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" }, + { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, + { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, + { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, + { { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" }, + { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); parser.addHelpOption(); parser.addVersionOption(); @@ -231,7 +312,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_instanceIdToShowWindowOf = parser.value("show"); - for (auto zip_path : parser.values("import")){ + for (auto zip_path : parser.values("import")) { m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); } @@ -240,10 +321,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath())); } - // error if --launch is missing with --server or --profile - if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) - { + if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; @@ -255,7 +334,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) { // Root path is used for updates and portable data #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr + QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr m_rootPath = foo.absolutePath(); #elif defined(Q_OS_WIN32) m_rootPath = binPath; @@ -271,15 +350,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString dataPath; // change folder QString dirParam = parser.value("dir"); - if (!dirParam.isEmpty()) - { + if (!dirParam.isEmpty()) { // the dir param. it makes multimc data path point to whatever the user specified // on command line adjustedBy = "Command line"; dataPath = dirParam; - } - else - { + } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; @@ -293,34 +369,27 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) #endif } - if (!FS::ensureFolderPathExists(dataPath)) - { + if (!FS::ensureFolderPathExists(dataPath)) { showFatalErrorMessage( "The launcher data folder could not be created.", - QString( - "The launcher data folder could not be created.\n" - "\n" - "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem." - ).arg(dataPath) - ); + QString("The launcher data folder could not be created.\n" + "\n" + "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); return; } - if (!QDir::setCurrent(dataPath)) - { - showFatalErrorMessage( - "The launcher data folder could not be opened.", - QString( - "The launcher data folder could not be opened.\n" - "\n" - "Make sure you have the right permissions to the launcher data folder.\n" - "(%1)\n" - "\n" - "The launcher cannot continue until you fix this problem." - ).arg(dataPath) - ); + if (!QDir::setCurrent(dataPath)) { + showFatalErrorMessage("The launcher data folder could not be opened.", + QString("The launcher data folder could not be opened.\n" + "\n" + "Make sure you have the right permissions to the launcher data folder.\n" + "(%1)\n" + "\n" + "The launcher cannot continue until you fix this problem.") + .arg(dataPath)); return; } @@ -334,17 +403,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // FIXME: you can run the same binaries with multiple data dirs and they won't clash. This could cause issues for updates. m_peerInstance = new LocalPeer(this, appID); connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived); - if(m_peerInstance->isClient()) { + if (m_peerInstance->isClient()) { int timeout = 2000; - if(m_instanceIdToLaunch.isEmpty()) - { + if (m_instanceIdToLaunch.isEmpty()) { ApplicationMessage activate; activate.command = "activate"; m_peerInstance->sendMessage(activate.serialize(), timeout); - if(!m_zipsToImport.isEmpty()) - { + if (!m_zipsToImport.isEmpty()) { for (auto zip_url : m_zipsToImport) { ApplicationMessage import; import.command = "import"; @@ -352,19 +419,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_peerInstance->sendMessage(import.serialize(), timeout); } } - } - else - { + } else { ApplicationMessage launch; launch.command = "launch"; launch.args["id"] = m_instanceIdToLaunch; - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { launch.args["server"] = m_serverToJoin; } - if(!m_profileToUse.isEmpty()) - { + if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } m_peerInstance->sendMessage(launch.serialize(), timeout); @@ -408,40 +471,47 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qInstallMessageHandler(appDebugOutput); qSetMessagePattern( - "%{time process}" " " - "%{if-debug}D%{endif}" "%{if-info}I%{endif}" "%{if-warning}W%{endif}" "%{if-critical}C%{endif}" "%{if-fatal}F%{endif}" - " " "|" " " - "%{if-category}[%{category}]: %{endif}" - "%{message}"); - + "%{time process}" + " " + "%{if-debug}D%{endif}" + "%{if-info}I%{endif}" + "%{if-warning}W%{endif}" + "%{if-critical}C%{endif}" + "%{if-fatal}F%{endif}" + " " + "|" + " " + "%{if-category}[%{category}]: %{endif}" + "%{message}"); + bool foundLoggingRules = false; - + auto logRulesFile = QStringLiteral("qtlogging.ini"); auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); - - qDebug() << "Testing" << logRulesPath << "..."; + + qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); // search the dataPath() // seach app data standard path - if(!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { + if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); - if(!logRulesPath.isEmpty()) { + if (!logRulesPath.isEmpty()) { qDebug() << "Found" << logRulesPath << "..."; foundLoggingRules = true; } } // seach root path - if(!foundLoggingRules) { - logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); + if (!foundLoggingRules) { + logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); } - - if(foundLoggingRules) { + + if (foundLoggingRules) { // load and set logging rules qDebug() << "Loading logging rules from:" << logRulesPath; - QSettings loggingRules(logRulesPath, QSettings::IniFormat); + QSettings loggingRules(logRulesPath, QSettings::IniFormat); loggingRules.beginGroup("Rules"); QStringList rule_names = loggingRules.childKeys(); QStringList rules; @@ -462,48 +532,44 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool migrated = false; if (!migrated) - migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg"); + migrated = handleDataMigration( + dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", + "polymc.cfg"); if (!migrated) - migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", "multimc.cfg"); + migrated = handleDataMigration( + dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", + "multimc.cfg"); } { - - qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); + qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME) << ", (c) 2022-2023 " + << qPrintable(QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; - if (adjustedBy.size()) - { + if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); qDebug() << "Adjusted by : " << adjustedBy; - } - else - { + } else { qDebug() << "Work dir : " << QDir::currentPath(); } qDebug() << "Binary path : " << binPath; qDebug() << "Application root path : " << m_rootPath; - if(!m_instanceIdToLaunch.isEmpty()) - { + if (!m_instanceIdToLaunch.isEmpty()) { qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; } - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { qDebug() << "Address of server to join :" << m_serverToJoin; } qDebug() << "<> Paths set."; } - if(m_liveCheck) - { + if (m_liveCheck) { QFile check(liveCheckFile); - if(check.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { + if (check.open(QIODevice::WriteOnly | QIODevice::Truncate)) { auto payload = appID.toString().toUtf8(); - if(check.write(payload) == payload.size()) - { + if (check.write(payload) == payload.size()) { check.close(); } else { qWarning() << "Could not write into" << liveCheckFile << "!"; @@ -549,7 +615,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString resolvedDefaultMonospace = consoleFontInfo.family(); QFont resolvedFont(resolvedDefaultMonospace); qDebug() << "Detected default console font:" << resolvedDefaultMonospace - << ", substitutions:" << resolvedFont.substitutions().join(','); + << ", substitutions:" << resolvedFont.substitutions().join(','); m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace); m_settings->registerSetting("ConsoleFontSize", defaultSize); @@ -558,7 +624,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Folders m_settings->registerSetting("InstanceDir", "instances"); - m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods"); + m_settings->registerSetting({ "CentralModsDir", "ModsDir" }, "mods"); m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); @@ -576,20 +642,20 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("LogPrePostOutput", true); // Window Size - m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false); - m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854); - m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480); + m_settings->registerSetting({ "LaunchMaximized", "MCWindowMaximize" }, false); + m_settings->registerSetting({ "MinecraftWinWidth", "MCWindowWidth" }, 854); + m_settings->registerSetting({ "MinecraftWinHeight", "MCWindowHeight" }, 480); // Proxy Settings m_settings->registerSetting("ProxyType", "None"); - m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1"); + m_settings->registerSetting({ "ProxyAddr", "ProxyHostName" }, "127.0.0.1"); m_settings->registerSetting("ProxyPort", 8080); - m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, ""); - m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, ""); + m_settings->registerSetting({ "ProxyUser", "ProxyUsername" }, ""); + m_settings->registerSetting({ "ProxyPass", "ProxyPassword" }, ""); // Memory - m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512); - m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem()); + m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); + m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, suitableMaxMem()); m_settings->registerSetting("PermGen", 128); // Java Settings @@ -631,8 +697,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("WrapperCommand", ""); // Custom Commands - m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, ""); - m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, ""); + m_settings->registerSetting({ "PreLaunchCommand", "PreLaunchCmd" }, ""); + m_settings->registerSetting({ "PostExitCommand", "PostExitCmd" }, ""); // The cat m_settings->registerSetting("TheCat", false); @@ -671,8 +737,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) QString pastebinURL = m_settings->get("PastebinURL").toString(); bool userHadDefaultPastebin = pastebinURL == "https://0x0.st"; - if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) - { + if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) { m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer); m_settings->set("PastebinCustomAPIBase", pastebinURL); m_settings->reset("PastebinURL"); @@ -681,8 +746,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool ok; int pasteType = m_settings->get("PastebinType").toInt(&ok); // If PastebinType is invalid then reset the related settings. - if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) - { + if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) { m_settings->reset("PastebinType"); m_settings->reset("PastebinCustomAPIBase"); } @@ -755,8 +819,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } // initialize the updater - if(BuildConfig.UPDATER_ENABLED) - { + if (BuildConfig.UPDATER_ENABLED) { qDebug() << "Initializing updater"; #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); @@ -767,18 +830,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Instance icons { auto setting = APPLICATION->settings()->getSetting("IconsDir"); - QStringList instFolders = - { - ":/icons/multimc/32x32/instances/", - ":/icons/multimc/50x50/instances/", - ":/icons/multimc/128x128/instances/", - ":/icons/multimc/scalable/instances/" - }; + QStringList instFolders = { ":/icons/multimc/32x32/instances/", ":/icons/multimc/50x50/instances/", + ":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" }; m_icons.reset(new IconList(instFolders, setting->get().toString())); - connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value) - { - m_icons->directoryChanged(value.toString()); - }); + connect(setting.get(), &Setting::SettingChanged, + [&](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); }); qDebug() << "<> Instance icons intialized."; } @@ -792,8 +848,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // and remember that we have to show him a dialog when the gui starts (if it does so) QString instDir = InstDirSetting->get().toString(); qDebug() << "Instance path : " << instDir; - if (FS::checkProblemticPathJava(QDir(instDir))) - { + if (FS::checkProblemticPathJava(QDir(instDir))) { qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; } m_instances.reset(new InstanceList(m_settings, instDir, this)); @@ -843,11 +898,10 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // now we have network, download translation updates m_translations->downloadIndex(); - //FIXME: what to do with these? + // FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory())); - for (auto profiler : m_profilers.values()) - { + for (auto profiler : m_profilers.values()) { profiler->registerSettings(m_settings); } @@ -857,19 +911,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } #ifdef Q_OS_MACOS - connect(this, &Application::clickedOnDock, [this]() { - this->showMainWindow(); - }); + connect(this, &Application::clickedOnDock, [this]() { this->showMainWindow(); }); #endif - connect(this, &Application::aboutToQuit, [this](){ - if(m_instances) - { + connect(this, &Application::aboutToQuit, [this]() { + if (m_instances) { // save any remaining instance state m_instances->saveNow(); } - if(logFile) - { + if (logFile) { logFile->flush(); logFile->close(); } @@ -879,8 +929,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) updateCapabilities(); - if(createSetupWizard()) - { + if (createSetupWizard()) { return; } @@ -889,29 +938,25 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) bool Application::createSetupWizard() { - bool javaRequired = [&]() - { + bool javaRequired = [&]() { bool ignoreJavaWizard = m_settings->get("IgnoreJavaWizard").toBool(); - if(ignoreJavaWizard) { + if (ignoreJavaWizard) { return false; } QString currentHostName = QHostInfo::localHostName(); QString oldHostName = settings()->get("LastHostname").toString(); - if (currentHostName != oldHostName) - { + if (currentHostName != oldHostName) { settings()->set("LastHostname", currentHostName); return true; } QString currentJavaPath = settings()->get("JavaPath").toString(); QString actualPath = FS::ResolveExecutable(currentJavaPath); - if (actualPath.isNull()) - { + if (actualPath.isNull()) { return true; } return false; }(); - bool languageRequired = [&]() - { + bool languageRequired = [&]() { if (settings()->get("Language").toString().isEmpty()) return true; return false; @@ -920,27 +965,22 @@ bool Application::createSetupWizard() bool themeInterventionRequired = settings()->get("ApplicationTheme") == ""; bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired; - if(wizardRequired) - { + if (wizardRequired) { m_setupWizard = new SetupWizard(nullptr); - if (languageRequired) - { + if (languageRequired) { m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard)); } - if (javaRequired) - { + if (javaRequired) { m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); } - if (pasteInterventionRequired) - { + if (pasteInterventionRequired) { m_setupWizard->addPage(new PasteWizardPage(m_setupWizard)); } - if (themeInterventionRequired) - { - settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard + if (themeInterventionRequired) { + settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard)); } connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); @@ -980,26 +1020,22 @@ void Application::setupWizardFinished(int status) void Application::performMainStartupAction() { m_status = Application::Initialized; - if(!m_instanceIdToLaunch.isEmpty()) - { + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); - if(inst) - { + if (inst) { MinecraftServerTargetPtr serverToJoin = nullptr; MinecraftAccountPtr accountToUse = nullptr; qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching"; - if(!m_serverToJoin.isEmpty()) - { + if (!m_serverToJoin.isEmpty()) { // FIXME: validate the server string serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin))); qDebug() << " Launching with server" << m_serverToJoin; } - if(!m_profileToUse.isEmpty()) - { + if (!m_profileToUse.isEmpty()) { accountToUse = accounts()->getAccountByProfileName(m_profileToUse); - if(!accountToUse) { + if (!accountToUse) { return; } qDebug() << " Launching with account" << m_profileToUse; @@ -1009,26 +1045,22 @@ void Application::performMainStartupAction() return; } } - if(!m_instanceIdToShowWindowOf.isEmpty()) - { + if (!m_instanceIdToShowWindowOf.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToShowWindowOf); - if(inst) - { + if (inst) { qDebug() << "<> Showing window of instance " << m_instanceIdToShowWindowOf; showInstanceWindow(inst); return; } } - if(!m_mainWindow) - { + if (!m_mainWindow) { // normal main window showMainWindow(false); qDebug() << "<> Main window shown."; } - if(!m_zipsToImport.isEmpty()) - { + if (!m_zipsToImport.isEmpty()) { qDebug() << "<> Importing from zip:" << m_zipsToImport; - m_mainWindow->processURLs( m_zipsToImport ); + m_mainWindow->processURLs(m_zipsToImport); } } @@ -1046,8 +1078,7 @@ Application::~Application() #if defined Q_OS_WIN32 // Detach from Windows console - if(consoleAttached) - { + if (consoleAttached) { fclose(stdout); fclose(stdin); fclose(stderr); @@ -1058,8 +1089,7 @@ Application::~Application() void Application::messageReceived(const QByteArray& message) { - if(status() != Initialized) - { + if (status() != Initialized) { qDebug() << "Received message" << message << "while still initializing. It will be ignored."; return; } @@ -1067,66 +1097,51 @@ void Application::messageReceived(const QByteArray& message) ApplicationMessage received; received.parse(message); - auto & command = received.command; + auto& command = received.command; - if(command == "activate") - { + if (command == "activate") { showMainWindow(); - } - else if(command == "import") - { + } else if (command == "import") { QString path = received.args["path"]; - if(path.isEmpty()) - { + if (path.isEmpty()) { qWarning() << "Received" << command << "message without a zip path/URL."; return; } m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) }); - } - else if(command == "launch") - { + } else if (command == "launch") { QString id = received.args["id"]; QString server = received.args["server"]; QString profile = received.args["profile"]; InstancePtr instance; - if(!id.isEmpty()) { + if (!id.isEmpty()) { instance = instances()->getInstanceById(id); - if(!instance) { + if (!instance) { qWarning() << "Launch command requires an valid instance ID. " << id << "resolves to nothing."; return; } - } - else { + } else { qWarning() << "Launch command called without an instance ID..."; return; } MinecraftServerTargetPtr serverObject = nullptr; - if(!server.isEmpty()) { + if (!server.isEmpty()) { serverObject = std::make_shared(MinecraftServerTarget::parse(server)); } MinecraftAccountPtr accountObject; - if(!profile.isEmpty()) { + if (!profile.isEmpty()) { accountObject = accounts()->getAccountByProfileName(profile); - if(!accountObject) { - qWarning() << "Launch command requires the specified profile to be valid. " << profile << "does not resolve to any account."; + if (!accountObject) { + qWarning() << "Launch command requires the specified profile to be valid. " << profile + << "does not resolve to any account."; return; } } - launch( - instance, - true, - false, - nullptr, - serverObject, - accountObject - ); - } - else - { + launch(instance, true, false, nullptr, serverObject, accountObject); + } else { qWarning() << "Received invalid message" << message; } } @@ -1138,8 +1153,7 @@ std::shared_ptr Application::translations() std::shared_ptr Application::javalist() { - if (!m_javalist) - { + if (!m_javalist) { m_javalist.reset(new JavaInstallList()); } return m_javalist; @@ -1167,50 +1181,41 @@ void Application::setIconTheme(const QString& name) QIcon Application::getThemedIcon(const QString& name) { - if(name == "logo") { + if (name == "logo") { return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME); } return QIcon::fromTheme(name); } -bool Application::openJsonEditor(const QString &filename) +bool Application::openJsonEditor(const QString& filename) { const QString file = QDir::current().absoluteFilePath(filename); - if (m_settings->get("JsonEditor").toString().isEmpty()) - { + if (m_settings->get("JsonEditor").toString().isEmpty()) { return DesktopServices::openUrl(QUrl::fromLocalFile(file)); - } - else - { - //return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); - return DesktopServices::run(m_settings->get("JsonEditor").toString(), {file}); + } else { + // return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); + return DesktopServices::run(m_settings->get("JsonEditor").toString(), { file }); } } -bool Application::launch( - InstancePtr instance, - bool online, - bool demo, - BaseProfilerFactory *profiler, - MinecraftServerTargetPtr serverToJoin, - MinecraftAccountPtr accountToUse -) { - if(m_updateRunning) - { +bool Application::launch(InstancePtr instance, + bool online, + bool demo, + BaseProfilerFactory* profiler, + MinecraftServerTargetPtr serverToJoin, + MinecraftAccountPtr accountToUse) +{ + if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; - } - else if(instance->canLaunch()) - { - auto & extras = m_instanceExtras[instance->id()]; - auto & window = extras.window; - if(window) - { - if(!window->saveAll()) - { + } else if (instance->canLaunch()) { + auto& extras = m_instanceExtras[instance->id()]; + auto& window = extras.window; + if (window) { + if (!window->saveAll()) { return false; } } - auto & controller = extras.controller; + auto& controller = extras.controller; controller.reset(new LaunchController()); controller->setInstance(instance); controller->setOnline(online); @@ -1218,30 +1223,21 @@ bool Application::launch( controller->setProfiler(profiler); controller->setServerToJoin(serverToJoin); controller->setAccountToUse(accountToUse); - if(window) - { + if (window) { controller->setParentWidget(window); - } - else if(m_mainWindow) - { + } else if (m_mainWindow) { controller->setParentWidget(m_mainWindow); } connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded); connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed); - connect(controller.get(), &LaunchController::aborted, this, [this] { - controllerFailed(tr("Aborted")); - }); + connect(controller.get(), &LaunchController::aborted, this, [this] { controllerFailed(tr("Aborted")); }); addRunningInstance(); controller->start(); return true; - } - else if (instance->isRunning()) - { + } else if (instance->isRunning()) { showInstanceWindow(instance, "console"); return true; - } - else if (instance->canEdit()) - { + } else if (instance->canEdit()) { showInstanceWindow(instance); return true; } @@ -1250,16 +1246,14 @@ bool Application::launch( bool Application::kill(InstancePtr instance) { - if (!instance->isRunning()) - { + if (!instance->isRunning()) { qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running."; return false; } - auto & extras = m_instanceExtras[instance->id()]; + auto& extras = m_instanceExtras[instance->id()]; // NOTE: copy of the shared pointer keeps it alive auto controller = extras.controller; - if(controller) - { + if (controller) { return controller->abort(); } return true; @@ -1273,23 +1267,20 @@ void Application::closeCurrentWindow() void Application::addRunningInstance() { - m_runningInstances ++; - if(m_runningInstances == 1) - { + m_runningInstances++; + if (m_runningInstances == 1) { emit updateAllowedChanged(false); } } void Application::subRunningInstance() { - if(m_runningInstances == 0) - { + if (m_runningInstances == 0) { qCritical() << "Something went really wrong and we now have less than 0 running instances... WTF"; return; } - m_runningInstances --; - if(m_runningInstances == 0) - { + m_runningInstances--; + if (m_runningInstances == 0) { emit updateAllowedChanged(true); } } @@ -1309,20 +1300,17 @@ void Application::updateIsRunning(bool running) m_updateRunning = running; } - void Application::controllerSucceeded() { - auto controller = qobject_cast(QObject::sender()); - if(!controller) + auto controller = qobject_cast(QObject::sender()); + if (!controller) return; auto id = controller->id(); - auto & extras = m_instanceExtras[id]; + auto& extras = m_instanceExtras[id]; // on success, do... - if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) - { - if(extras.window) - { + if (controller->instance()->settings()->get("AutoCloseConsole").toBool()) { + if (extras.window) { extras.window->close(); } } @@ -1330,8 +1318,7 @@ void Application::controllerSucceeded() subRunningInstance(); // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { m_status = Status::Succeeded; exit(0); } @@ -1340,19 +1327,18 @@ void Application::controllerSucceeded() void Application::controllerFailed(const QString& error) { Q_UNUSED(error); - auto controller = qobject_cast(QObject::sender()); - if(!controller) + auto controller = qobject_cast(QObject::sender()); + if (!controller) return; auto id = controller->id(); - auto & extras = m_instanceExtras[id]; + auto& extras = m_instanceExtras[id]; // on failure, do... nothing extras.controller.reset(); subRunningInstance(); // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { m_status = Status::Failed; exit(1); } @@ -1360,7 +1346,7 @@ void Application::controllerFailed(const QString& error) void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) { - if(!m_globalSettingsProvider) { + if (!m_globalSettingsProvider) { return; } emit globalSettingsAboutToOpen(); @@ -1374,24 +1360,18 @@ void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) MainWindow* Application::showMainWindow(bool minimized) { - if(m_mainWindow) - { + if (m_mainWindow) { m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized); m_mainWindow->raise(); m_mainWindow->activateWindow(); - } - else - { + } else { m_mainWindow = new MainWindow(); m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray())); m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray())); - if(minimized) - { + if (minimized) { m_mainWindow->showMinimized(); - } - else - { + } else { m_mainWindow->show(); } @@ -1403,31 +1383,26 @@ MainWindow* Application::showMainWindow(bool minimized) return m_mainWindow; } -InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString page) +InstanceWindow* Application::showInstanceWindow(InstancePtr instance, QString page) { - if(!instance) + if (!instance) return nullptr; auto id = instance->id(); - auto & extras = m_instanceExtras[id]; - auto & window = extras.window; + auto& extras = m_instanceExtras[id]; + auto& window = extras.window; - if(window) - { + if (window) { window->raise(); window->activateWindow(); - } - else - { + } else { window = new InstanceWindow(instance); - m_openWindows ++; + m_openWindows++; connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); } - if(!page.isEmpty()) - { + if (!page.isEmpty()) { window->selectPage(page); } - if(extras.controller) - { + if (extras.controller) { extras.controller->setParentWidget(window); } return window; @@ -1436,24 +1411,20 @@ InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString pa void Application::on_windowClose() { m_openWindows--; - auto instWindow = qobject_cast(QObject::sender()); - if(instWindow) - { - auto & extras = m_instanceExtras[instWindow->instanceId()]; + auto instWindow = qobject_cast(QObject::sender()); + if (instWindow) { + auto& extras = m_instanceExtras[instWindow->instanceId()]; extras.window = nullptr; - if(extras.controller) - { + if (extras.controller) { extras.controller->setParentWidget(m_mainWindow); } } - auto mainWindow = qobject_cast(QObject::sender()); - if(mainWindow) - { + auto mainWindow = qobject_cast(QObject::sender()); + if (mainWindow) { m_mainWindow = nullptr; } // quit when there are no more windows. - if(shouldExitNow()) - { + if (shouldExitNow()) { exit(0); } } @@ -1461,23 +1432,14 @@ void Application::on_windowClose() void Application::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password) { // Set the application proxy settings. - if (proxyTypeStr == "SOCKS5") - { - QNetworkProxy::setApplicationProxy( - QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); - } - else if (proxyTypeStr == "HTTP") - { - QNetworkProxy::setApplicationProxy( - QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); - } - else if (proxyTypeStr == "None") - { + if (proxyTypeStr == "SOCKS5") { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); + } else if (proxyTypeStr == "HTTP") { + QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); + } else if (proxyTypeStr == "None") { // If we have no proxy set, set no proxy and return. QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy)); - } - else - { + } else { // If we have "Default" selected, set Qt to use the system proxy settings. QNetworkProxyFactory::setUseSystemConfiguration(true); } @@ -1487,39 +1449,35 @@ void Application::updateProxySettings(QString proxyTypeStr, QString addr, int po m_network->setProxy(proxy); QString proxyDesc; - if (proxy.type() == QNetworkProxy::NoProxy) - { + if (proxy.type() == QNetworkProxy::NoProxy) { qDebug() << "Using no proxy is an option!"; return; } - switch (proxy.type()) - { - case QNetworkProxy::DefaultProxy: - proxyDesc = "Default proxy: "; - break; - case QNetworkProxy::Socks5Proxy: - proxyDesc = "Socks5 proxy: "; - break; - case QNetworkProxy::HttpProxy: - proxyDesc = "HTTP proxy: "; - break; - case QNetworkProxy::HttpCachingProxy: - proxyDesc = "HTTP caching: "; - break; - case QNetworkProxy::FtpCachingProxy: - proxyDesc = "FTP caching: "; - break; - default: - proxyDesc = "DERP proxy: "; - break; + switch (proxy.type()) { + case QNetworkProxy::DefaultProxy: + proxyDesc = "Default proxy: "; + break; + case QNetworkProxy::Socks5Proxy: + proxyDesc = "Socks5 proxy: "; + break; + case QNetworkProxy::HttpProxy: + proxyDesc = "HTTP proxy: "; + break; + case QNetworkProxy::HttpCachingProxy: + proxyDesc = "HTTP caching: "; + break; + case QNetworkProxy::FtpCachingProxy: + proxyDesc = "FTP caching: "; + break; + default: + proxyDesc = "DERP proxy: "; + break; } - proxyDesc += QString("%1:%2") - .arg(proxy.hostName()) - .arg(proxy.port()); + proxyDesc += QString("%1:%2").arg(proxy.hostName()).arg(proxy.port()); qDebug() << proxyDesc; } -shared_qobject_ptr< HttpMetaCache > Application::metacache() +shared_qobject_ptr Application::metacache() { return m_metacache; } @@ -1531,8 +1489,7 @@ shared_qobject_ptr Application::network() shared_qobject_ptr Application::metadataIndex() { - if (!m_metadataIndex) - { + if (!m_metadataIndex) { m_metadataIndex.reset(new Meta::Index()); } return m_metadataIndex; @@ -1563,10 +1520,9 @@ QString Application::getJarPath(QString jarFile) #endif FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"), - FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging + FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging }; - for(QString p : potentialPaths) - { + for (QString p : potentialPaths) { QString jarPath = FS::PathCombine(p, jarFile); if (QFileInfo(jarPath).isFile()) return jarPath; @@ -1631,7 +1587,7 @@ int Application::suitableMaxMem() // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB if (totalRAM < (4096 * 1.5)) - maxMemoryAlloc = (int) (totalRAM / 1.5); + maxMemoryAlloc = (int)(totalRAM / 1.5); else maxMemoryAlloc = 4096; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 5cdb0383e..dc413bb62 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -625,6 +625,11 @@ set(PRISMUPDATER_SOURCES net/HeaderProxy.h net/RawHeaderProxy.h + ui/dialogs/ProgressDialog.cpp + ui/dialogs/ProgressDialog.h + ui/widgets/SubTaskProgressBar.h + ui/widgets/SubTaskProgressBar.cpp + ) ######## Logging categories ######## @@ -1144,6 +1149,8 @@ qt_add_resources(LAUNCHER_RESOURCES qt_wrap_ui(PRISMUPDATER_UI updater/prismupdater/SelectReleaseDialog.ui + ui/widgets/SubTaskProgressBar.ui + ui/dialogs/ProgressDialog.ui ) ######## Windows resource files ######## @@ -1231,11 +1238,13 @@ install(TARGETS ${Launcher_Name} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) -if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) ) +if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER)) # Updater add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(prism_updater_logic + QuaZip::QuaZip + ${ZLIB_LIBRARIES} systeminfo BuildConfig ghcFilesystem::ghc_filesystem diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 001ecb10c..2b62feeb3 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -46,8 +46,11 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#include +#include #include #include +#include #endif // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header @@ -100,27 +103,108 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt } } +#if defined Q_OS_WIN32 + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apearently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "r"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdin)); + if (dup2Result == 0) { + setvbuf(stdin, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + setvbuf(stdout, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stderr)); + if (dup2Result == 0) { + setvbuf(stderr, NULL, _IONBF, 0); + } + } + } + } + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} +#endif + PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 // attach the parent console if (AttachConsole(ATTACH_PARENT_PROCESS)) { - FILE* _stream; - errno_t err; - // if attach succeeds, reopen and sync all the i/o - if (err = freopen_s(&_stream, "CON", "w", stdout); err == 0) { - std::cout.sync_with_stdio(); - } - if (err = freopen_s(&_stream, "CON", "w", stderr); err == 0) { - std::cerr.sync_with_stdio(); - } - if (err = freopen_s(&_stream, "CON", "r", stdin); err == 0) { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD written; - const char* endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); + BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } #endif @@ -133,16 +217,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); - parser.addOptions({ { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, - { { "I", "install-version" }, "Install a specific version.", tr("version name") }, - { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, - { { "c", "check-only" }, - tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, - { { "F", "force" }, tr("Force an update, even if one is not needed.") }, - { { "l", "list" }, tr("List available releases.") }, - { "debug", tr("Log debug to console.") }, - { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, - { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); + parser.addOptions( + { { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, + { { "V", "prism-version" }, + tr("Use this version as the installed launcher version. (provided because stdout can not be reliably captured on windows)"), + tr("installed launcher version") }, + { { "I", "install-version" }, "Install a specific version.", tr("version name") }, + { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, + { { "c", "check-only" }, + tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, + { { "F", "force" }, tr("Force an update, even if one is not needed.") }, + { { "l", "list" }, tr("List available releases.") }, + { "debug", tr("Log debug to console.") }, + { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, + { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); parser.addHelpOption(); parser.addVersionOption(); @@ -150,24 +238,31 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar logToConsole = parser.isSet("debug"); - auto prism_executable = QCoreApplication::applicationFilePath(); + auto updater_executable = QCoreApplication::applicationFilePath(); if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); - if (!QFileInfo(prism_executable).isFile()) - showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); - - if (prism_executable.startsWith("/tmp/.mount_")) { + if (updater_executable.startsWith("/tmp/.mount_")) { m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); - if (m_appimagePath.isEmpty()) + if (m_appimagePath.isEmpty()) { showFatalErrorMessage(tr("Unsupported Installation"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); + } } m_isFlatpak = DesktopServices::isFlatpak(); + QString prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; +#if defined Q_OS_WIN32 + prism_executable += ".exe"; +#endif + + if (!QFileInfo(prism_executable).isFile()) { + showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); + } + m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); @@ -185,6 +280,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); + auto version = parser.value("prism-version"); + if (!version.isEmpty()) { + if (version.contains('-')) { + auto index = version.indexOf('-'); + m_prsimVersionChannel = version.mid(index + 1); + version = version.left(index); + } else { + m_prsimVersionChannel = "stable"; + } + auto version_parts = version.split('.'); + m_prismVersionMajor = version_parts.takeFirst().toInt(); + m_prismVersionMinor = version_parts.takeFirst().toInt(); + } + QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -338,9 +447,9 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar PrismUpdaterApp::~PrismUpdaterApp() { - qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); + qDebug() << "updater shutting down"; #if defined Q_OS_WIN32 // Detach from Windows console @@ -392,13 +501,19 @@ void PrismUpdaterApp::run() return exit(0); } - loadPrismVersionFromExe(m_prismExecutable); + if (!loadPrismVersionFromExe(m_prismExecutable)) { + m_prismVersion = BuildConfig.printableVersionString(); + m_prismVersionMajor = BuildConfig.VERSION_MAJOR; + m_prismVersionMinor = BuildConfig.VERSION_MINOR; + m_prsimVersionChannel = BuildConfig.VERSION_CHANNEL; + m_prismGitCommit = BuildConfig.GIT_COMMIT; + } m_status = Succeeded; qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVersion; qDebug() << "Version major:" << m_prismVersionMajor; - qDebug() << "Verison minor:" << m_prismVersionMinor; - qDebug() << "Verison channel:" << m_prsimVersionChannel; + qDebug() << "Version minor:" << m_prismVersionMinor; + qDebug() << "Version channel:" << m_prsimVersionChannel; qDebug() << "Git Commit:" << m_prismGitCommit; auto latest = getLatestRelease(); @@ -420,7 +535,7 @@ void PrismUpdaterApp::run() if (m_isAppimage) { bool result = true; if (need_update) - result = callAppimageUpdate(); + result = callAppImageUpdate(); return exit(result ? 0 : 1); } @@ -528,6 +643,13 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); bool for_portable = asset.name.toLower().contains("portable"); + if (for_platform && asset.name.toLower().contains("legacy") && !BuildConfig.BUILD_PLATFORM.toLower().contains("legacy")) + for_platform = false; + if (for_platform && asset.name.toLower().contains("arm64") && !QSysInfo::buildCpuArchitecture().contains("arm64")) + for_platform = false; + if (for_platform && !asset.name.toLower().contains("arm64") && QSysInfo::buildCpuArchitecture().contains("arm64")) + for_platform = false; + if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { valid.append(asset); } @@ -557,10 +679,11 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release) GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { - return showFatalErrorMessage(tr("No Valid Release Assets"), - tr("Github release %1 has no valid assets for this platform: %2") - .arg(release.tag_name) - .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable))); + return showFatalErrorMessage( + tr("No Valid Release Assets"), + tr("Github release %1 has no valid assets for this platform: %2") + .arg(release.tag_name) + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable ? tr("yes") : tr("no")))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { @@ -587,18 +710,21 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) auto file_url = QUrl(asset.browser_download_url); auto out_file_path = FS::PathCombine(temp_dir, file_url.fileName()); + qDebug() << "downloading" << file_url << "to" << out_file_path; auto download = Net::Download::makeFile(file_url, out_file_path); - + download->setNetwork(m_network); auto progress_dialog = ProgressDialog(); if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); + qDebug() << "download complete"; + QFileInfo out_file(out_file_path); return out_file; } -bool PrismUpdaterApp::callAppimageUpdate() +bool PrismUpdaterApp::callAppImageUpdate() { QProcess proc = QProcess(); proc.setProgram("AppImageUpdate"); @@ -614,20 +740,20 @@ void PrismUpdaterApp::clearUpdateLog() void PrismUpdaterApp::logUpdate(const QString& msg) { - qDebug() << msg; + qDebug() << qUtf8Printable(msg); auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); FS::append(update_log_path, QStringLiteral("%1\n").arg(msg).toUtf8()); } void PrismUpdaterApp::performInstall(QFileInfo file) { + qDebug() << "starting install"; auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::write(update_lock_path, QStringLiteral("FROM=%1\nTO=%2\n").arg(m_prismVersion).arg(m_install_release.tag_name).toUtf8()); clearUpdateLog(); logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); - // TODO setup marker file - if (m_isPortable) { + if (m_isPortable || file.suffix().toLower() == "zip") { logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { @@ -635,6 +761,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) QProcess proc = QProcess(); proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); + logUpdate(tr("Process start result: %1").arg(result ? tr("yes") : tr("no"))); exit(result ? 0 : 1); } } @@ -643,8 +770,24 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) { logUpdate(tr("Backing up install")); backupAppDir(); - auto loc = unpackArchive(archive); - // TODO: unpack (rename access failures) + + if (auto loc = unpackArchive(archive)) { + auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); + FS::write(marker_file_path, applicationDirPath().toUtf8()); + auto new_updater_path = loc.value().absoluteFilePath("prismlauncher-updater"); +#if defined Q_OS_WIN32 + new_updater_path.append(".exe"); +#endif + logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); + QProcess proc = QProcess(); + proc.startDetached(new_updater_path, {}, loc.value().absolutePath()); + if (!proc.waitForStarted(5000)) { + logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); + return exit(10); + } + return exit(); // up to the new updater now + } + return exit(1); // unpack failure } void PrismUpdaterApp::backupAppDir() @@ -680,6 +823,8 @@ void PrismUpdaterApp::backupAppDir() "styles/*", "styles/*", "tls/*", + "qt.conf", + "Qt*.dll", }); } file_list.append("portable.txt"); @@ -688,7 +833,10 @@ void PrismUpdaterApp::backupAppDir() logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); QDir app_dir = QCoreApplication::applicationDirPath(); - auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVersion + ":" + m_prismGitCommit); + auto backup_dir = FS::PathCombine( + app_dir.absolutePath(), QStringLiteral("backup_") + + QString(m_prismVersion).replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), QString("_")) + + "-" + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); for (auto glob : file_list) { @@ -723,13 +871,12 @@ std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) if (archive.fileName().endsWith(".zip")) { auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath()); if (result) { - logUpdate( - tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); + logUpdate(tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); } else { logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); showFatalErrorMessage("Failed to extract archive", tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); - return {}; + return std::nullopt; } } else if (archive.fileName().endsWith(".tar.gz")) { @@ -739,43 +886,52 @@ std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) QProcess proc = QProcess(); proc.start(cmd, args); if (!proc.waitForStarted(5000)) { // wait 5 seconds to start - showFatalErrorMessage(tr("Failed extract archive"), - tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" "))); - return {}; + auto msg = tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" ")); + logUpdate(msg); + showFatalErrorMessage(tr("Failed extract archive"), msg); + return std::nullopt; } auto result = proc.waitForFinished(5000); auto out = proc.readAll(); logUpdate(out); if (!result) { - showFatalErrorMessage(tr("Failed to extract archive"), tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" "))); - return {}; + auto msg = tr("Child process \"%1 %2\" failed.").arg(cmd).arg(args.join(" ")); + logUpdate(msg); + showFatalErrorMessage(tr("Failed to extract archive"), msg); + return std::nullopt; } } else { logUpdate(tr("Unknown archive format for %1").arg(archive.absoluteFilePath())); showFatalErrorMessage("Can not extract", QStringLiteral("Unknown archive format %1").arg(archive.absoluteFilePath())); - return {}; + return std::nullopt; } return tmp_extract_dir; } -void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) +bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.setReadChannel(QProcess::StandardOutput); proc.start(exe_path, { "-v" }); - if (!proc.waitForStarted(5000)) // wait 5 seconds to start - return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); - if (!proc.waitForFinished(5000)) - return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); + if (!proc.waitForStarted(5000)) { + showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); + return false; + } // wait 5 seconds to start + if (!proc.waitForFinished(5000)) { + showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); + return false; + } auto out = proc.readAll(); auto lines = out.split('\n'); if (lines.length() < 2) - return; + return false; auto first = lines.takeFirst(); auto first_parts = first.split(' '); if (first_parts.length() < 2) - return; + return false; m_prismBinaryName = first_parts.takeFirst(); auto version = first_parts.takeFirst(); m_prismVersion = version; @@ -790,6 +946,7 @@ void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) m_prismVersionMajor = version_parts.takeFirst().toInt(); m_prismVersionMinor = version_parts.takeFirst().toInt(); m_prismGitCommit = lines.takeFirst().simplified(); + return true; } void PrismUpdaterApp::loadReleaseList() diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index af7af7dee..909e36056 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -60,7 +60,7 @@ class PrismUpdaterApp : public QApplication { void abort(const QString& reason); void showFatalErrorMessage(const QString& title, const QString& content); - void loadPrismVersionFromExe(const QString& exe_path); + bool loadPrismVersionFromExe(const QString& exe_path); void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* response); @@ -83,7 +83,9 @@ class PrismUpdaterApp : public QApplication { std::optional unpackArchive(QFileInfo file); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); - bool callAppimageUpdate(); + bool callAppImageUpdate(); + + void moveAndPostProcess(QDir target); public slots: void downloadError(QString reason); @@ -114,8 +116,8 @@ class PrismUpdaterApp : public QApplication { QString m_prismBinaryName; QString m_prismVersion; - int m_prismVersionMajor; - int m_prismVersionMinor; + int m_prismVersionMajor = -1; + int m_prismVersionMinor = -1; QString m_prsimVersionChannel; QString m_prismGitCommit; diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 72fdf3404..14c584918 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -17,8 +17,8 @@ SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const Q ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); - ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); ui->versionsTree->header()->setStretchLastSection(false); ui->eplainLabel->setText(tr("Select a version to install.\n" @@ -90,8 +90,8 @@ SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QListversionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::Stretch); - ui->versionsTree->setHeaderLabels({tr("Verison"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); ui->versionsTree->header()->setStretchLastSection(false); ui->eplainLabel->setText(tr("Select a version to install.")); From d2a3acd493f3da7dab05c0744aa46d274535b9d2 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:53:20 -0700 Subject: [PATCH 27/72] fix: filter archive assets from windows non-portable installs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/prismupdater/PrismUpdater.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 2b62feeb3..7f065f536 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -640,14 +640,19 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel continue; else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) continue; + auto asset_name = asset.name.toLower(); + auto platform = BuildConfig.BUILD_PLATFORM.toLower(); + auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); + auto asset_is_arm = asset_name.contains("arm64"); + auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); - bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower()); - bool for_portable = asset.name.toLower().contains("portable"); - if (for_platform && asset.name.toLower().contains("legacy") && !BuildConfig.BUILD_PLATFORM.toLower().contains("legacy")) + bool for_platform = !platform.isEmpty() && asset_name.contains(platform); + bool for_portable = asset_name.contains("portable"); + if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) for_platform = false; - if (for_platform && asset.name.toLower().contains("arm64") && !QSysInfo::buildCpuArchitecture().contains("arm64")) + if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) for_platform = false; - if (for_platform && !asset.name.toLower().contains("arm64") && QSysInfo::buildCpuArchitecture().contains("arm64")) + if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) for_platform = false; if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { From 431346658951ef699669b8564fc38366eb62271f Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 22:21:16 -0700 Subject: [PATCH 28/72] feat(updater): final step for portable install Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- CMakeLists.txt | 4 +- .../minecraft/mod/tasks/LocalModParseTask.cpp | 5 +- launcher/modplatform/packwiz/Packwiz.cpp | 7 +- launcher/ui/dialogs/ProgressDialog.cpp | 9 +- launcher/ui/dialogs/ProgressDialog.ui | 7 +- .../updater/prismupdater/PrismUpdater.cpp | 270 +++++++++++++++--- launcher/updater/prismupdater/PrismUpdater.h | 5 +- .../prismupdater/SelectReleaseDialog.ui | 4 +- 8 files changed, 256 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70a553190..cb75f769c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,8 +148,8 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M # Build platform. set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") -# Channel list URL -set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.") +# Github repo URL with releases for updater +set(Launcher_UPDATER_GITHUB_REPO "" CACHE STRING "Base URL for the updater.") # The metadata server set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 264019f84..43fc4831d 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -108,10 +108,11 @@ ModDetails ReadMCModTOML(QByteArray contents) return {}; } #else - tomlData = toml::parse(contents.toStdString()); - if (!tomlData) { + toml::parse_result result = toml::parse(contents.toStdString()); + if (!result) { return {}; } + tomlData = result.table(); #endif // array defined by [[mods]] diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 510c7309d..967b8870c 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -241,12 +241,13 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod return {}; } #else - table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); - if (!table) { + toml::parse_result result = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); + if (!result) { qWarning() << QString("Could not open file %1!").arg(normalized_fname); - qWarning() << "Reason: " << QString(table.error().what()); + qWarning() << "Reason: " << result.error().description(); return {}; } + table = result.table(); #endif // index_file.close(); diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp index 246a0fd49..93f8076f4 100644 --- a/launcher/ui/dialogs/ProgressDialog.cpp +++ b/launcher/ui/dialogs/ProgressDialog.cpp @@ -68,6 +68,8 @@ ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Pr setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true); setSkipButton(false); changeProgress(0, 100); + updateSize(); + adjustSize(); } void ProgressDialog::setSkipButton(bool present, QString label) @@ -96,7 +98,11 @@ ProgressDialog::~ProgressDialog() void ProgressDialog::updateSize() { QSize lastSize = this->size(); - QSize qSize = QSize(480, minimumSizeHint().height()); + auto min_height = minimumSizeHint().height(); + if (ui->taskProgressScrollArea->isHidden()) + min_height -= ui->taskProgressScrollArea->minimumSizeHint().height(); + min_height = std::max(min_height, 0); + QSize qSize = QSize(480, min_height); // if the current window is too small if ((lastSize != qSize) && (lastSize.height() < qSize.height())) @@ -111,7 +117,6 @@ void ProgressDialog::updateSize() } setMinimumSize(qSize); - } int ProgressDialog::execWithTask(Task* task) diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui index a4d08124c..156ff247f 100644 --- a/launcher/ui/dialogs/ProgressDialog.ui +++ b/launcher/ui/dialogs/ProgressDialog.ui @@ -48,6 +48,9 @@ Global Task Status... + + true + @@ -109,8 +112,8 @@ 0 0 - 464 - 96 + 460 + 108 diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 7f065f536..221c1c649 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -39,7 +39,7 @@ #include #include -#include +#include #include #if defined Q_OS_WIN32 @@ -106,7 +106,7 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt #if defined Q_OS_WIN32 // taken from https://stackoverflow.com/a/25927081 -// getting a proper output to console with redirection support on windows is apearently hell +// getting a proper output to console with redirection support on windows is apparently hell void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) { // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been @@ -266,8 +266,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); + if (prism_update_url.isEmpty()) + prism_update_url = BuildConfig.UPDATER_GITHUB_REPO; if (prism_update_url.isEmpty()) prism_update_url = "https://github.com/PrismLauncher/PrismLauncher"; + m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); m_checkOnly = parser.isSet("check-only"); @@ -334,6 +337,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar #endif } + m_updateLogPath = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + { // setup logging static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; auto moveFile = [](const QString& oldName, const QString& newName) { @@ -424,14 +429,16 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; if (adjustedBy.size()) { - qDebug() << "Work dir before adjustment : " << origCwdPath; - qDebug() << "Work dir after adjustment : " << QDir::currentPath(); + qDebug() << "Data dir before adjustment : " << origCwdPath; + qDebug() << "Data dir after adjustment : " << m_dataPath; qDebug() << "Adjusted by : " << adjustedBy; } else { - qDebug() << "Work dir : " << QDir::currentPath(); + qDebug() << "Data dir : " << QDir::currentPath(); } + qDebug() << "Work dir : " << QDir::currentPath(); qDebug() << "Binary path : " << binPath; qDebug() << "Application root path : " << m_rootPath; + qDebug() << "Portable install : " << m_isPortable; qDebug() << "<> Paths set."; } @@ -442,14 +449,28 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_network->setProxy(proxy); } - QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); + auto marker_file_path = QDir(applicationDirPath()).absoluteFilePath(".prism_launcher_updater_unpack.marker"); + auto marker_file = QFileInfo(marker_file_path); + if (marker_file.exists()) { + auto target_dir = QString(FS::read(marker_file_path)).trimmed(); + if (target_dir.isEmpty()) { + qWarning() << "Empty updater marker file contains no install target. making best guess of parent dir"; + target_dir = QDir(applicationDirPath()).absoluteFilePath(".."); + } + + QMetaObject::invokeMethod( + this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); + + } else { + QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); + } } PrismUpdaterApp::~PrismUpdaterApp() { + qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); - qDebug() << "updater shutting down"; #if defined Q_OS_WIN32 // Detach from Windows console @@ -576,6 +597,81 @@ void PrismUpdaterApp::run() exit(0); } +void PrismUpdaterApp::moveAndFinishUpdate(QDir target) +{ + logUpdate("Finishing update process"); + auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); + QFileInfo manifest(manifest_path); + + auto app_dir = QDir(applicationDirPath()); + + QStringList file_list; + if (manifest.isFile()) { + // load manifest from file + logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); + try { + auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); + auto files = contents.split('\n'); + for (auto file : files) { + file_list.append(file.trimmed()); + } + } catch (FS::FileSystemException) { + } + } + + if (file_list.isEmpty()) { + logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(applicationDirPath())); + auto entries = target.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + for (auto entry : entries) { + file_list.append(entry.fileName()); + } + } + logUpdate(tr("Installing the following to %1 :\n %2").arg(target.absolutePath()).arg(file_list.join(",\n "))); + + bool error = false; + + QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + progress.setCancelButton(nullptr); + progress.show(); + QCoreApplication::processEvents(); + + int i = 0; + for (auto glob : file_list) { + QDirIterator iter(applicationDirPath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + progress.setValue(i); + QCoreApplication::processEvents(); + while (iter.hasNext()) { + auto to_install_file = iter.next(); + auto rel_path = app_dir.relativeFilePath(to_install_file); + auto install_path = FS::PathCombine(target.absolutePath(), rel_path); + logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); + FS::ensureFilePathExists(install_path); + auto result = FS::copy(to_install_file, install_path)(); + if (!result) { + error = true; + logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); + } + } + i++; + } + progress.setValue(i); + QCoreApplication::processEvents(); + + if (error) { + logUpdate(tr("There were errors installing the update.")); + auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); + FS::move(m_updateLogPath, fail_marker); + } else { + logUpdate(tr("Update succeed.")); + auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); + FS::move(m_updateLogPath, success_marker); + } + auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); + FS::deletePath(update_lock_path); + + exit(error ? 1 : 0); +} + void PrismUpdaterApp::printReleases() { for (auto release : m_releases) { @@ -739,24 +835,107 @@ bool PrismUpdaterApp::callAppImageUpdate() void PrismUpdaterApp::clearUpdateLog() { - auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); - QFile::remove(update_log_path); + QFile::remove(m_updateLogPath); } void PrismUpdaterApp::logUpdate(const QString& msg) { qDebug() << qUtf8Printable(msg); - auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); - FS::append(update_log_path, QStringLiteral("%1\n").arg(msg).toUtf8()); + FS::append(m_updateLogPath, QStringLiteral("%1\n").arg(msg).toUtf8()); +} + +std::tuple read_lock_File(const QString& path) +{ + auto contents = QString(FS::read(path)); + auto lines = contents.split('\n'); + + QDateTime timestamp; + QString from, to, target, data_path; + for (auto line : lines) { + auto index = line.indexOf("="); + if (index < 0) + continue; + auto left = line.left(index); + auto right = line.mid(index + 1); + if (left.toLower() == "timestamp") { + timestamp = QDateTime::fromString(right, Qt::ISODate); + } else if (left.toLower() == "from") { + from = right; + } else if (left.toLower() == "to") { + to = right; + } else if (left.toLower() == "target") { + target = right; + } else if (left.toLower() == "data_path") { + data_path = right; + } + } + return std::make_tuple(timestamp, from, to, target, data_path); +} + +bool write_lock_file(const QString& path, QDateTime timestamp, QString from, QString to, QString target, QString data_path) +{ + try { + FS::write(path, QStringLiteral("TIMESTAMP=%1\nFROM=%2\nTO=%3\nTARGET=%4\nDATA_PATH=%5\n") + .arg(timestamp.toString(Qt::ISODate)) + .arg(from) + .arg(to) + .arg(target) + .arg(data_path) + .toUtf8()); + } catch (FS::FileSystemException err) { + qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause(); + return false; + } + return true; } void PrismUpdaterApp::performInstall(QFileInfo file) { qDebug() << "starting install"; auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); - FS::write(update_lock_path, QStringLiteral("FROM=%1\nTO=%2\n").arg(m_prismVersion).arg(m_install_release.tag_name).toUtf8()); + QFileInfo update_lock(update_lock_path); + if (update_lock.exists()) { + auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock_path); + auto msg = tr("Update already in progress\n"); + auto infoMsg = + tr("This installation has a update lock file present at: %1\n" + "\n" + "Timestamp: %2\n" + "Updating from version %3 to %4\n" + "Target install path: %5\n" + "Data Path: %6" + "\n" + "This likely means that a previous update attempt failed. Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at \n" + "%7\n" + "for details on the last update attempt.\n" + "\n" + "To overwrite this lock and proceed with this update anyway, select \"Ignore\" below.") + .arg(update_lock_path) + .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) + .arg(m_updateLogPath); + QMessageBox msgBox; + msgBox.setText(msg); + msgBox.setInformativeText(infoMsg); + msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + switch (msgBox.exec()) { + case QMessageBox::Ignore: + break; + case QMessageBox::Cancel: + [[fallthrough]]; + default: + return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); + } + } + write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), + m_dataPath); clearUpdateLog(); + auto changelog_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.changelog"); + FS::write(changelog_path, m_install_release.body.toUtf8()); + logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); @@ -779,14 +958,13 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, applicationDirPath().toUtf8()); - auto new_updater_path = loc.value().absoluteFilePath("prismlauncher-updater"); + auto new_updater_path = loc.value().absoluteFilePath("prismlauncher_updater"); #if defined Q_OS_WIN32 new_updater_path.append(".exe"); #endif logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); QProcess proc = QProcess(); - proc.startDetached(new_updater_path, {}, loc.value().absolutePath()); - if (!proc.waitForStarted(5000)) { + if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); return exit(10); } @@ -807,7 +985,10 @@ void PrismUpdaterApp::backupAppDir() logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); try { auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); - file_list.append(contents.split('\n')); + auto files = contents.split('\n'); + for (auto file : files) { + file_list.append(file.trimmed()); + } } catch (FS::FileSystemException) { } } @@ -815,19 +996,19 @@ void PrismUpdaterApp::backupAppDir() if (file_list.isEmpty()) { // best guess if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") { - file_list.append({ "PrismLauncher", "bin/*", "share/*", "lib/*" }); + file_list.append({ "PrismLauncher", "bin", "share", "lib" }); } else { // windows by process of elimination file_list.append({ - "jars/*", + "jars", "prismlauncher.exe", "prismlauncher_filelink.exe", + "prismlauncher_updater.exe", "qtlogging.ini", - "imageformats/*", - "iconengines/*", - "platforms/*", - "styles/*", - "styles/*", - "tls/*", + "imageformats", + "iconengines", + "platforms", + "styles", + "tls", "qt.conf", "Qt*.dll", }); @@ -839,32 +1020,41 @@ void PrismUpdaterApp::backupAppDir() QDir app_dir = QCoreApplication::applicationDirPath(); auto backup_dir = FS::PathCombine( - app_dir.absolutePath(), QStringLiteral("backup_") + - QString(m_prismVersion).replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), QString("_")) + - "-" + m_prismGitCommit); + app_dir.absolutePath(), + QStringLiteral("backup_") + + QString(m_prismVersion).replace(QRegularExpression("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"), QString("_")) + "-" + + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); + auto backup_marker_path = FS::PathCombine(m_dataPath, ".prism_launcher_update_backup_path.txt"); + FS::write(backup_marker_path, backup_dir.toUtf8()); + QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + progress.setCancelButton(nullptr); + progress.show(); + QCoreApplication::processEvents(); + int i = 0; for (auto glob : file_list) { - QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, - QDirIterator::Subdirectories); + QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + progress.setValue(i); + QCoreApplication::processEvents(); while (iter.hasNext()) { auto to_bak_file = iter.next(); auto rel_path = app_dir.relativeFilePath(to_bak_file); auto bak_path = FS::PathCombine(backup_dir, rel_path); - - if (QFileInfo(to_bak_file).isFile()) { - logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); - FS::ensureFilePathExists(bak_path); - auto result = FS::copy(to_bak_file, bak_path)(); - if (!result) { - logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); - } else { - if (!FS::deletePath(to_bak_file)) - logUpdate(tr("Failed to remove %1").arg(to_bak_file)); - } + logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); + FS::ensureFilePathExists(bak_path); + auto result = FS::copy(to_bak_file, bak_path)(); + if (!result) { + logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); + } else { + if (!FS::deletePath(to_bak_file)) + logUpdate(tr("Failed to remove %1").arg(to_bak_file)); } } + i++; } + progress.setValue(i); + QCoreApplication::processEvents(); } std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 909e36056..113747b75 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -85,7 +85,7 @@ class PrismUpdaterApp : public QApplication { QFileInfo downloadAsset(const GitHubReleaseAsset& asset); bool callAppImageUpdate(); - void moveAndPostProcess(QDir target); + void moveAndFinishUpdate(QDir target); public slots: void downloadError(QString reason); @@ -98,7 +98,6 @@ class PrismUpdaterApp : public QApplication { void clearUpdateLog(); void logUpdate(const QString& msg); - QString m_rootPath; QString m_dataPath; bool m_isPortable = false; @@ -114,6 +113,8 @@ class PrismUpdaterApp : public QApplication { bool m_selectUI; bool m_allowDowngrade; + QString m_updateLogPath; + QString m_prismBinaryName; QString m_prismVersion; int m_prismVersionMajor = -1; diff --git a/launcher/updater/prismupdater/SelectReleaseDialog.ui b/launcher/updater/prismupdater/SelectReleaseDialog.ui index 9d3613727..a1aa38371 100644 --- a/launcher/updater/prismupdater/SelectReleaseDialog.ui +++ b/launcher/updater/prismupdater/SelectReleaseDialog.ui @@ -6,8 +6,8 @@ 0 0 - 478 - 517 + 468 + 385 From e099a3f7e8846a293d30a884498929ae9f5b2f07 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 24 Jun 2023 22:22:24 -0700 Subject: [PATCH 29/72] feat(updater): packaging - know the updater git repo Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- buildconfig/BuildConfig.cpp.in | 4 ++-- buildconfig/BuildConfig.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f93337b5..48be91ac8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -277,12 +277,12 @@ jobs: if: runner.os == 'Windows' && matrix.msystem != '' shell: msys2 {0} run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -297,7 +297,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja ## # BUILD diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 8a412b7ff..b03867c3c 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -60,7 +60,7 @@ Config::Config() BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; - UPDATER_BASE = "@Launcher_UPDATER_BASE@"; + UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; @@ -89,7 +89,7 @@ Config::Config() { VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL.remove("refs/heads/"); - if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) { + if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_PLATFORM.isEmpty()) { UPDATER_ENABLED = true; } } diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 8543d7241..b6ae6e6d8 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -75,7 +75,7 @@ class Config { QString BUILD_DATE; /// URL for the updater's channel - QString UPDATER_BASE; + QString UPDATER_GITHUB_REPO; /// The public key used to sign releases for the Sparkle updater appcast QString MAC_SPARKLE_PUB_KEY; From 4b65315cdc18b1a4687d8dd1922dc7b10abc8a51 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 14:21:55 -0700 Subject: [PATCH 31/72] chore: add license to headers Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../updater/prismupdater/GitHubRelease.cpp | 103 ++++++++++++++---- launcher/updater/prismupdater/GitHubRelease.h | 23 +++- launcher/updater/prismupdater/PrismUpdater.h | 2 +- .../updater/prismupdater/UpdaterDialogs.cpp | 72 +++++++----- .../updater/prismupdater/UpdaterDialogs.h | 24 +++- .../updater/prismupdater/updater_main.cpp | 10 +- 6 files changed, 176 insertions(+), 58 deletions(-) diff --git a/launcher/updater/prismupdater/GitHubRelease.cpp b/launcher/updater/prismupdater/GitHubRelease.cpp index 25e60e885..3beae31b1 100644 --- a/launcher/updater/prismupdater/GitHubRelease.cpp +++ b/launcher/updater/prismupdater/GitHubRelease.cpp @@ -1,36 +1,93 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + #include "GitHubRelease.h" QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset) { QDebugStateSaver saver(debug); - debug.nospace() << "GitHubReleaseAsset( " - "id: " << asset.id << ", " - "name " << asset.name << ", " - "label: " << asset.label << ", " - "content_type: " << asset.content_type << ", " - "size: " << asset.size << ", " - "created_at: " << asset.created_at << ", " - "updated_at: " << asset.updated_at << ", " - "browser_download_url: " << asset.browser_download_url << " " - ")"; + debug.nospace() << "GitHubReleaseAsset( " + "id: " + << asset.id + << ", " + "name " + << asset.name + << ", " + "label: " + << asset.label + << ", " + "content_type: " + << asset.content_type + << ", " + "size: " + << asset.size + << ", " + "created_at: " + << asset.created_at + << ", " + "updated_at: " + << asset.updated_at + << ", " + "browser_download_url: " + << asset.browser_download_url + << " " + ")"; return debug; } QDebug operator<<(QDebug debug, const GitHubRelease& rls) { QDebugStateSaver saver(debug); - debug.nospace() << "GitHubRelease( " - "id: " << rls.id << ", " - "name " << rls.name << ", " - "tag_name: " << rls.tag_name << ", " - "created_at: " << rls.created_at << ", " - "published_at: " << rls.published_at << ", " - "prerelease: " << rls.prerelease << ", " - "draft: " << rls.draft << ", " - "version" << rls.version << ", " - "body: " << rls.body << ", " - "assets: " << rls.assets << " " - ")"; + debug.nospace() << "GitHubRelease( " + "id: " + << rls.id + << ", " + "name " + << rls.name + << ", " + "tag_name: " + << rls.tag_name + << ", " + "created_at: " + << rls.created_at + << ", " + "published_at: " + << rls.published_at + << ", " + "prerelease: " + << rls.prerelease + << ", " + "draft: " + << rls.draft + << ", " + "version" + << rls.version + << ", " + "body: " + << rls.body + << ", " + "assets: " + << rls.assets + << " " + ")"; return debug; } - diff --git a/launcher/updater/prismupdater/GitHubRelease.h b/launcher/updater/prismupdater/GitHubRelease.h index 0c190333f..798c6b7ae 100644 --- a/launcher/updater/prismupdater/GitHubRelease.h +++ b/launcher/updater/prismupdater/GitHubRelease.h @@ -1,3 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + #pragma once #include #include @@ -37,4 +59,3 @@ struct GitHubRelease { QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls); QDebug operator<<(QDebug debug, const GitHubRelease& rls); - diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 113747b75..f879ad7ad 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -42,7 +42,7 @@ #define PRISM_EXTERNAL_EXE #include "FileSystem.h" -#include "updater/prismupdater/GitHubRelease.h" +#include "GitHubRelease.h" class PrismUpdaterApp : public QApplication { // friends for the purpose of limiting access to deprecated stuff diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 14c584918..395b658db 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -1,3 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + #include "UpdaterDialogs.h" #include "ui_SelectReleaseDialog.h" @@ -9,25 +31,25 @@ SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const Q : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) { ui->setupUi(this); - + ui->changelogTextBrowser->setOpenExternalLinks(true); ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); - + ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") }); ui->versionsTree->header()->setStretchLastSection(false); - + ui->eplainLabel->setText(tr("Select a version to install.\n" - "\n" - "Currently installed version: %1") - .arg(m_currentVersion.toString())); - + "\n" + "Currently installed version: %1") + .arg(m_currentVersion.toString())); + loadReleases(); - + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseDialog::selectionChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseDialog::accept); @@ -57,10 +79,11 @@ void SelectReleaseDialog::appendRelease(GitHubRelease const& release) ui->versionsTree->addTopLevelItem(rls_item); } -GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) { +GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) +{ int id = item->data(0, Qt::UserRole).toInt(); GitHubRelease release; - for (auto rls: m_releases) { + for (auto rls : m_releases) { if (rls.id == id) release = rls; } @@ -76,30 +99,28 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget ui->changelogTextBrowser->setHtml(body); } - - -SelectReleaseAssetDialog::SelectReleaseAssetDialog( const QList& assets, QWidget* parent) - : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) +SelectReleaseAssetDialog::SelectReleaseAssetDialog(const QList& assets, QWidget* parent) + : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) { ui->setupUi(this); - + ui->changelogTextBrowser->setOpenExternalLinks(true); ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); - + ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - ui->versionsTree->setHeaderLabels({tr("Version"), tr("Published Date")}); + ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") }); ui->versionsTree->header()->setStretchLastSection(false); - + ui->eplainLabel->setText(tr("Select a version to install.")); ui->changelogTextBrowser->setHidden(true); - + loadAssets(); - + connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept); @@ -129,10 +150,11 @@ void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset) ui->versionsTree->addTopLevelItem(rls_item); } -GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) { +GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) +{ int id = item->data(0, Qt::UserRole).toInt(); GitHubReleaseAsset selected_asset; - for (auto asset: m_assets) { + for (auto asset : m_assets) { if (asset.id == id) selected_asset = asset; } diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index c5e31b5a4..249ca1b5d 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -1,10 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + #pragma once #include #include #include "Version.h" -#include "updater/prismupdater/GitHubRelease.h" +#include "GitHubRelease.h" namespace Ui { class SelectReleaseDialog; diff --git a/launcher/updater/prismupdater/updater_main.cpp b/launcher/updater/prismupdater/updater_main.cpp index 6bc7dd208..89c1d1198 100644 --- a/launcher/updater/prismupdater/updater_main.cpp +++ b/launcher/updater/prismupdater/updater_main.cpp @@ -20,17 +20,14 @@ * */ - - -#include "updater/prismupdater/PrismUpdater.h" +#include "PrismUpdater.h" int main(int argc, char* argv[]) { PrismUpdaterApp wUpApp(argc, argv); - switch(wUpApp.status()) { + switch (wUpApp.status()) { case PrismUpdaterApp::Starting: - case PrismUpdaterApp::Initialized: - { + case PrismUpdaterApp::Initialized: { return wUpApp.exec(); } case PrismUpdaterApp::Failed: @@ -40,5 +37,4 @@ int main(int argc, char* argv[]) default: return -1; } - } From 8aeec1d52d30a008c9b7d815d0e3a8eda82878ca Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 14:36:20 -0700 Subject: [PATCH 32/72] fix: use new shared pointer for repsonse buffer Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/prismupdater/PrismUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 221c1c649..60e970597 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -1166,7 +1166,7 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); auto response = std::make_shared(); - auto download = Net::Download::makeByteArray(page_url, response.get()); + auto download = Net::Download::makeByteArray(page_url, response); download->setNetwork(m_network); m_current_url = page_url; From 10266f65e409da6ef5132a4433b4e09631d6b77e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 16:10:06 -0700 Subject: [PATCH 33/72] fix: include `^` updater doesn't need to know about mods Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/MMCZip.cpp | 2 ++ launcher/MMCZip.h | 5 +++++ launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++-- launcher/updater/prismupdater/PrismUpdater.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 1a336375b..33776d972 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -135,6 +135,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList return result; } +#if defined(LAUNCHER_APPLICATION) // ours bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { @@ -235,6 +236,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const } return true; } +#endif // ours QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root) diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 2a78f830f..ef0e09777 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -38,7 +38,10 @@ #include #include #include +#include +#if defined(LAUNCHER_APPLICATION) #include "minecraft/mod/Mod.h" +#endif #include #include @@ -74,10 +77,12 @@ namespace MMCZip */ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); +#if defined(LAUNCHER_APPLICATION) /** * take a source jar, add mods to it, resulting in target jar */ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); +#endif /** * Find a single file in archive by file name (not path) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 60e970597..3ca4f235e 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -72,7 +72,7 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif -#include +#include "DesktopServices.h" #include "updater/prismupdater/UpdaterDialogs.h" @@ -83,7 +83,7 @@ namespace fs = ghc::filesystem; #include "net/Download.h" #include "net/RawHeaderProxy.h" -#include +#include "MMCZip.h" /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index f879ad7ad..90ebeb8d2 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "QObjectPtr.h" #include "net/Download.h" From d8e0b14dc458a662671092b6941609c85348b38a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:36:20 -0700 Subject: [PATCH 34/72] feat(updater): tie in updater part 1 Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 15 +- launcher/CMakeLists.txt | 7 + launcher/StringUtils.cpp | 28 ++- launcher/StringUtils.h | 11 +- launcher/updater/PrismExternalUpdater.cpp | 206 ++++++++++++++++++ launcher/updater/PrismExternalUpdater.h | 94 ++++++++ .../updater/prismupdater/PrismUpdater.cpp | 44 +++- launcher/updater/prismupdater/PrismUpdater.h | 1 + 8 files changed, 390 insertions(+), 16 deletions(-) create mode 100644 launcher/updater/PrismExternalUpdater.cpp create mode 100644 launcher/updater/PrismExternalUpdater.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index f094a8ec9..a5103e8e5 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -132,6 +132,8 @@ #ifdef Q_OS_MAC #include "updater/MacSparkleUpdater.h" +#else +#include "updater/PrismExternalUpdater.h" #endif #if defined Q_OS_WIN32 @@ -271,10 +273,15 @@ void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr Application::Application(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 - // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // attach the parent console if stdout not already captured + auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); + consoleAttached = true; + } + } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { BindCrtHandlesToStdHandles(true, true, true); - consoleAttached = true; } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); @@ -823,6 +830,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Initializing updater"; #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); +#else + m_updater.reset(new PrismExternalUpdater(m_rootPath, dataPath)); #endif qDebug() << "<> Updater started."; } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index dc413bb62..86bf8fc48 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -179,6 +179,11 @@ set(MAC_UPDATE_SOURCES updater/MacSparkleUpdater.mm ) +set(PRISM_UPDATE_SOURCES + updater/PrismExternalUpdater.h + updater/PrismExternalUpdater.cpp +) + # Backend for the news bar... there's usually no news. set(NEWS_SOURCES # News System @@ -728,6 +733,8 @@ set(LOGIC_SOURCES if(APPLE) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) +else() + set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES}) endif() SET(LAUNCHER_SOURCES diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index e08e6fdce..f437b6dac 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -35,6 +35,7 @@ */ #include "StringUtils.h" +#include #include #include @@ -149,7 +150,7 @@ QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_ } if ((url_compact.length() >= max_len) && hard_limit) { - // still too long, truncate normaly + // still too long, truncate normally url_compact = QString(str_url); auto to_remove = url_compact.length() - max_len + 3; url_compact.remove(url_compact.length() - to_remove - 1, to_remove); @@ -182,3 +183,28 @@ QString StringUtils::getRandomAlphaNumeric() { return QUuid::createUuid().toString(QUuid::Id128); } + +QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { + QString left, right; + auto index = s.indexOf(sep, 0, cs); + left = s.mid(0, index); + right = s.mid(index + 1); + return qMakePair(left, right); +} + +QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { + QString left, right; + auto index = s.indexOf(sep, 0, cs); + left = s.mid(0, index); + right = s.mid(index + 1); + return qMakePair(left, right); +} + +QPair splitFirst(const QString& s, const QRegularExpression& re) { + QString left, right; + auto index = s.indexOf(re); + left = s.mid(0, index); + right = s.mid(index + 1); + return qMakePair(left, right); +} + diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index f90a6ac75..343ac9e4d 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -37,7 +37,9 @@ #pragma once #include +#include #include +#include namespace StringUtils { @@ -70,8 +72,8 @@ int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs) /** * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path * @param url Url to truncate - * @param max_len max lenght of url in charaters - * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. + * @param max_len max length of url in characters + * @param hard_limit if truncating the path can't get the url short enough, truncate it normally. */ QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false); @@ -79,4 +81,9 @@ QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_poi QString getRandomAlphaNumeric(); + +QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); +QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); +QPair splitFirst(const QString& s, const QRegularExpression& re); + } // namespace StringUtils diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp new file mode 100644 index 000000000..f510dfc6c --- /dev/null +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#include "PrismExternalUpdater.h" +#include +#include +#include +#include +#include +#include +#include + +#include "StringUtils.h" + +#include "BuildConfig.h" + +class PrismExternalUpdater::Private { + public: + QDir appDir; + QDir dataDir; + QTimer updateTimer; + bool allowBeta; + bool autoCheck; + double updateInterval; + QDateTime lastCheck; + std::unique_ptr settings; +}; + +PrismExternalUpdater::PrismExternalUpdater(const QString& appDir, const QString& dataDir) +{ + priv = new PrismExternalUpdater::Private(); + priv->appDir = QDir(appDir); + priv->dataDir = QDir(dataDir); + auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); + priv->settings = std::make_unique(settings_file, QSettings::Format::IniFormat); + priv->allowBeta = priv->settings->value("allow_beta", false).toBool(); + priv->autoCheck = priv->settings->value("auto_check", false).toBool(); + bool interval_ok; + priv->updateInterval = priv->settings->value("update_interval", 86400).toInt(&interval_ok); + if (!interval_ok) + priv->updateInterval = 86400; + auto last_check = priv->settings->value("last_check"); + if (!last_check.isNull() && last_check.isValid()) { + priv->lastCheck = QDateTime::fromString(last_check.toString(), Qt::ISODate); + } + connectTimer(); + resetAutoCheckTimer(); +} + +PrismExternalUpdater::~PrismExternalUpdater() +{ + if (priv->updateTimer.isActive()) + priv->updateTimer.stop(); + disconnectTimer(); + priv->settings->sync(); + delete priv; +} + +void PrismExternalUpdater::checkForUpdates() +{ + QProgressDialog progress(tr("Checking for updates..."), "", 0, -1); + progress.setCancelButton(nullptr); + progress.show(); + + QProcess proc; + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + + QStringList args = { "--check-only" }; + if (priv->allowBeta) + args.append("--pre-release"); + + proc.start(priv->appDir.absoluteFilePath(exe_name), args); + auto result_start = proc.waitForStarted(5000); + if (!result_start) { + auto err = proc.error(); + qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); + } + auto result_finished = proc.waitForFinished(60000); + if (!result_finished) { + auto err = proc.error(); + qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); + } + + auto exit_code = proc.exitCode(); + + auto std_output = proc.readAllStandardOutput(); + auto std_error = proc.readAllStandardError(); + + switch (exit_code) { + case 0: + // no update available + { + qDebug() << "No update available"; + } + break; + case 1: + // there was an error + { + qDebug() << "Updater subprocess error" << std_error; + } + break; + case 100: + // update available + { + auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n'); + auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n'); + auto [third_line, changelog] = StringUtils::splitFirst(remainder2, '\n'); + auto version_name = StringUtils::splitFirst(first_line, ": ").second; + auto version_tag = StringUtils::splitFirst(second_line, ": ").second; + auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate); + qDebug() << "Update available:" << version_name << version_tag << release_timestamp; + qDebug() << "Update changelog:" << changelog; + } + break; + default: + // unknown error code + { + qDebug() << "Updater exited with unknown code" << exit_code; + } + } + priv->lastCheck = QDateTime::currentDateTime(); + priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); + priv->settings->sync(); +} + +bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() { + return priv->autoCheck; +} + +double PrismExternalUpdater::getUpdateCheckInterval() { + return priv->updateInterval; +} + +bool PrismExternalUpdater::getBetaAllowed() { + return priv->allowBeta; +} + +void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) { + priv->autoCheck = check; + priv->settings->setValue("auto_check", check); + priv->settings->sync(); + resetAutoCheckTimer(); +} + +void PrismExternalUpdater::setUpdateCheckInterval(double seconds) { + priv->updateInterval = seconds; + priv->settings->setValue("update_interval", seconds); + priv->settings->sync(); + resetAutoCheckTimer(); +} + +void PrismExternalUpdater::setBetaAllowed(bool allowed) { + priv->allowBeta = allowed; + priv->settings->setValue("auto_beta", allowed); + priv->settings->sync(); +} + +void PrismExternalUpdater::resetAutoCheckTimer() { + int timeoutDuration = 0; + auto now = QDateTime::currentDateTime(); + if (priv->autoCheck) { + if (priv->lastCheck.isValid()) { + auto diff = priv->lastCheck.secsTo(now); + auto secs_left = priv->updateInterval - diff; + if (secs_left < 0) + secs_left = 0; + timeoutDuration = secs_left * 1000; // to msec + } + priv->updateTimer.start(timeoutDuration); + } else { + if (priv->updateTimer.isActive()) + priv->updateTimer.stop(); + } + +} + +void PrismExternalUpdater::connectTimer() { + connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); + +} + +void PrismExternalUpdater::disconnectTimer() { + disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); +} diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h new file mode 100644 index 000000000..852fb7d37 --- /dev/null +++ b/launcher/updater/PrismExternalUpdater.h @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#pragma once + +#include + +#include "ExternalUpdater.h" + +/*! + * An implementation for the updater on windows and linux that uses out external updater. + */ + +class PrismExternalUpdater : public ExternalUpdater { + Q_OBJECT + + public: + PrismExternalUpdater(const QString& appDir, const QString& dataDir); + ~PrismExternalUpdater() override; + + /*! + * Check for updates manually, showing the user a progress bar and an alert if no updates are found. + */ + void checkForUpdates() override; + + /*! + * Indicates whether or not to check for updates automatically. + */ + bool getAutomaticallyChecksForUpdates() override; + + /*! + * Indicates the current automatic update check interval in seconds. + */ + double getUpdateCheckInterval() override; + + /*! + * Indicates whether or not beta updates should be checked for in addition to regular releases. + */ + bool getBetaAllowed() override; + + /*! + * Set whether or not to check for updates automatically. + * + * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow + * reverting this property without kicking off a schedule change immediately." + */ + void setAutomaticallyChecksForUpdates(bool check) override; + + /*! + * Set the current automatic update check interval in seconds. + * + * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow + * reverting this property without kicking off a schedule change immediately." + */ + void setUpdateCheckInterval(double seconds) override; + + /*! + * Set whether or not beta updates should be checked for in addition to regular releases. + */ + void setBetaAllowed(bool allowed) override; + + void resetAutoCheckTimer(); + void disconnectTimer(); + void connectTimer(); + + void performUpdate(); + + public slots: + void autoCheckTimerFired(); + + private: + class Private; + + Private* priv; +}; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 3ca4f235e..f04a12c94 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -41,6 +41,7 @@ #include #include +#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -202,11 +203,17 @@ void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { #if defined Q_OS_WIN32 - // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // attach the parent console if stdout not already captured + auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); + consoleAttached = true; + } + } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { BindCrtHandlesToStdHandles(true, true, true); - consoleAttached = true; } + #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); @@ -226,6 +233,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, { { "c", "check-only" }, tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, + { { "p", "pre-release" }, tr("Allow updating to pre-release releases") }, { { "F", "force" }, tr("Force an update, even if one is not needed.") }, { { "l", "list" }, tr("List available releases.") }, { "debug", tr("Log debug to console.") }, @@ -297,6 +305,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_prismVersionMinor = version_parts.takeFirst().toInt(); } + m_allowPreRelease = parser.isSet("pre-release"); + QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -542,10 +552,19 @@ void PrismUpdaterApp::run() auto need_update = needUpdate(latest); if (m_checkOnly) { - if (need_update) + if (need_update) { + QTextStream stdOutStream(stdout); + stdOutStream << "Name: " << latest.name << "\n"; + stdOutStream << "Version: " << latest.tag_name << "\n"; + stdOutStream << "TimeStamp: " << latest.created_at.toString(Qt::ISODate) << "\n"; + stdOutStream << latest.body << "\n"; + stdOutStream.flush(); + return exit(100); - else + } + else { return exit(0); + } } if (m_isFlatpak) { @@ -958,10 +977,11 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, applicationDirPath().toUtf8()); - auto new_updater_path = loc.value().absoluteFilePath("prismlauncher_updater"); + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 - new_updater_path.append(".exe"); + exe_name.append(".exe"); #endif + auto new_updater_path = loc.value().absoluteFilePath(exe_name); logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); QProcess proc = QProcess(); if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { @@ -1110,7 +1130,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) QProcess proc = QProcess(); proc.setProcessChannelMode(QProcess::MergedChannels); proc.setReadChannel(QProcess::StandardOutput); - proc.start(exe_path, { "-v" }); + proc.start(exe_path, { "--version" }); if (!proc.waitForStarted(5000)) { showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); return false; @@ -1119,7 +1139,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); return false; } - auto out = proc.readAll(); + auto out = proc.readAllStandardOutput(); auto lines = out.split('\n'); if (lines.length() < 2) return false; @@ -1250,7 +1270,11 @@ GitHubRelease PrismUpdaterApp::getLatestRelease() { GitHubRelease latest; for (auto release : m_releases) { - if (!latest.isValid() || (!release.draft && release.version > latest.version)) { + if (release.draft) + continue; + if (release.prerelease && !m_allowPreRelease) + continue; + if (!latest.isValid() || (release.version > latest.version)) { latest = release; } } diff --git a/launcher/updater/prismupdater/PrismUpdater.h b/launcher/updater/prismupdater/PrismUpdater.h index 90ebeb8d2..f3dd6e062 100644 --- a/launcher/updater/prismupdater/PrismUpdater.h +++ b/launcher/updater/prismupdater/PrismUpdater.h @@ -113,6 +113,7 @@ class PrismUpdaterApp : public QApplication { bool m_printOnly; bool m_selectUI; bool m_allowDowngrade; + bool m_allowPreRelease; QString m_updateLogPath; From b7dd32274ca3b04b66ed76f6bb68b77ab5acb338 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:30:20 -0700 Subject: [PATCH 35/72] Proper capture on windows Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 -- launcher/StringUtils.cpp | 6 +++--- launcher/updater/PrismExternalUpdater.cpp | 15 ++++++++++++++- launcher/updater/prismupdater/PrismUpdater.cpp | 6 +----- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a5103e8e5..4c88a950d 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -280,8 +280,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } - } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { - BindCrtHandlesToStdHandles(true, true, true); } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index f437b6dac..b54299721 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -184,7 +184,7 @@ QString StringUtils::getRandomAlphaNumeric() return QUuid::createUuid().toString(QUuid::Id128); } -QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { +QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) { QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); @@ -192,7 +192,7 @@ QPair splitFirst(const QString& s, const QString& sep, Qt::Cas return qMakePair(left, right); } -QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive) { +QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) { QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); @@ -200,7 +200,7 @@ QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitiv return qMakePair(left, right); } -QPair splitFirst(const QString& s, const QRegularExpression& re) { +QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) { QString left, right; auto index = s.indexOf(re); left = s.mid(0, index); diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index f510dfc6c..9ec033f02 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "StringUtils.h" @@ -80,6 +81,8 @@ void PrismExternalUpdater::checkForUpdates() QProgressDialog progress(tr("Checking for updates..."), "", 0, -1); progress.setCancelButton(nullptr); progress.show(); + QCoreApplication::processEvents(); + QProcess proc; auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); @@ -87,7 +90,7 @@ void PrismExternalUpdater::checkForUpdates() exe_name.append(".exe"); #endif - QStringList args = { "--check-only" }; + QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; if (priv->allowBeta) args.append("--pre-release"); @@ -97,6 +100,8 @@ void PrismExternalUpdater::checkForUpdates() auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); } + QCoreApplication::processEvents(); + auto result_finished = proc.waitForFinished(60000); if (!result_finished) { auto err = proc.error(); @@ -108,6 +113,9 @@ void PrismExternalUpdater::checkForUpdates() auto std_output = proc.readAllStandardOutput(); auto std_error = proc.readAllStandardError(); + qDebug() << "captured output:" << std_output; + qDebug() << "captured error:" << std_error; + switch (exit_code) { case 0: // no update available @@ -188,6 +196,7 @@ void PrismExternalUpdater::resetAutoCheckTimer() { secs_left = 0; timeoutDuration = secs_left * 1000; // to msec } + qDebug() << "Auto update timer starting," << timeoutDuration / 1000 << "seconds left"; priv->updateTimer.start(timeoutDuration); } else { if (priv->updateTimer.isActive()) @@ -204,3 +213,7 @@ void PrismExternalUpdater::connectTimer() { void PrismExternalUpdater::disconnectTimer() { disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); } + +void PrismExternalUpdater::autoCheckTimerFired() { + checkForUpdates(); +} diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index f04a12c94..e01295605 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -41,7 +41,6 @@ #include #include -#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -210,10 +209,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } - } else if (stdout_type == FILE_TYPE_DISK || stdout_type == FILE_TYPE_PIPE ) { - BindCrtHandlesToStdHandles(true, true, true); - } - + } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); From 1f70589debda457b8f43426f7283d9c47a8129f1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:22:33 -0700 Subject: [PATCH 36/72] feat(updater): tie in part 2, let there be UI! Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 115 ++++++++++++- launcher/Application.h | 6 + launcher/CMakeLists.txt | 17 ++ launcher/FileSystem.cpp | 2 +- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 63 +++++++ launcher/ui/dialogs/UpdateAvailableDialog.h | 49 ++++++ launcher/ui/dialogs/UpdateAvailableDialog.ui | 155 ++++++++++++++++++ launcher/updater/PrismExternalUpdater.cpp | 112 ++++++++++--- launcher/updater/PrismExternalUpdater.h | 5 +- .../updater/prismupdater/PrismUpdater.cpp | 24 ++- 10 files changed, 512 insertions(+), 36 deletions(-) create mode 100644 launcher/ui/dialogs/UpdateAvailableDialog.cpp create mode 100644 launcher/ui/dialogs/UpdateAvailableDialog.h create mode 100644 launcher/ui/dialogs/UpdateAvailableDialog.ui diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 4c88a950d..a04f85c28 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -122,6 +122,7 @@ #include #include +#include #include #ifdef Q_OS_LINUX @@ -397,6 +398,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) .arg(dataPath)); return; } + m_dataPath = dataPath; /* * Establish the mechanism for communication with an already running PrismLauncher that uses the same data path. @@ -829,7 +831,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - m_updater.reset(new PrismExternalUpdater(m_rootPath, dataPath)); + m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } @@ -1024,9 +1026,120 @@ void Application::setupWizardFinished(int status) performMainStartupAction(); } +std::tuple read_lock_File(const QString& path) +{ + auto contents = QString(FS::read(path)); + auto lines = contents.split('\n'); + + QDateTime timestamp; + QString from, to, target, data_path; + for (auto line : lines) { + auto index = line.indexOf("="); + if (index < 0) + continue; + auto left = line.left(index); + auto right = line.mid(index + 1); + if (left.toLower() == "timestamp") { + timestamp = QDateTime::fromString(right, Qt::ISODate); + } else if (left.toLower() == "from") { + from = right; + } else if (left.toLower() == "to") { + to = right; + } else if (left.toLower() == "target") { + target = right; + } else if (left.toLower() == "data_path") { + data_path = right; + } + } + return std::make_tuple(timestamp, from, to, target, data_path); +} + void Application::performMainStartupAction() { m_status = Application::Initialized; + + auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + + auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); + if (update_lock.exists()) { + auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); + auto infoMsg = tr("This installation has a update lock file present at: %1\n" + "\n" + "Timestamp: %2\n" + "Updating from version %3 to %4\n" + "Target install path: %5\n" + "Data Path: %6" + "\n" + "This likely means that a update attempt failed. Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%7\n" + "for details on the last update attempt.\n" + "\n" + "To delete this lock and proceed select \"Ignore\" below.") + .arg(update_lock.absoluteFilePath()) + .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + switch (msgBox.exec()) { + case QMessageBox::AcceptRole: { + FS::deletePath(update_lock.absoluteFilePath()); + break; + } + case QMessageBox::RejectRole: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + exit(1); + } + } + } + + auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); + if (update_fail_marker.exists()) { + auto infoMsg = tr("An update attempt failed\n" + "\n" + "Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details on the last update attempt.") + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + switch (msgBox.exec()) { + case QMessageBox::AcceptRole: { + FS::deletePath(update_fail_marker.absoluteFilePath()); + break; + } + case QMessageBox::RejectRole: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + exit(1); + } + } + } + + auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); + if (update_success_marker.exists()) { + auto infoMsg = tr("Update succeeded\n" + "\n" + "You are now running %1 .\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details.") + .arg(BuildConfig.printableVersionString()) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.open(); + FS::deletePath(update_success_marker.absoluteFilePath()); + } + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if (inst) { diff --git a/launcher/Application.h b/launcher/Application.h index ced0af17d..baf64575d 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -187,6 +187,11 @@ public: return m_rootPath; } + /// the data path the application is using + const QString& dataRoot() { + return m_dataPath; + } + bool isPortable() { return m_portable; } @@ -277,6 +282,7 @@ private: QMap> m_profilers; QString m_rootPath; + QString m_dataPath; Status m_status = Application::StartingUp; Capabilities m_capabilities; bool m_portable = false; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 86bf8fc48..b68179af8 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1078,6 +1078,15 @@ SET(LAUNCHER_SOURCES ui/instanceview/VisualGroup.h ) +if (NOT Apple) +set(LAUNCHER_SOURCES + ${LAUNCHER_SOURCES} + + ui/dialogs/UpdateAvailableDialog.h + ui/dialogs/UpdateAvailableDialog.cpp +) +endif() + qt_wrap_ui(LAUNCHER_UI ui/MainWindow.ui ui/setupwizard/PasteWizardPage.ui @@ -1138,6 +1147,14 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ChooseProviderDialog.ui ) +qt_wrap_ui(PRISM_UPDATE_UI + ui/dialogs/UpdateAvailableDialog.ui +) + +if (NOT Apple) + set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI}) +endif() + qt_add_resources(LAUNCHER_RESOURCES resources/backgrounds/backgrounds.qrc resources/multimc/multimc.qrc diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 2a0ca76b5..7bfb5b1e3 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -199,7 +199,7 @@ void appendSafe(const QString& filename, const QByteArray& data) QByteArray buffer; try { buffer = read(filename); - } catch (FileSystemException) { + } catch (FileSystemException&) { buffer = QByteArray(); } buffer.append(data); diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp new file mode 100644 index 000000000..9f7308cba --- /dev/null +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ + +#include "UpdateAvailableDialog.h" +#include +#include "Application.h" +#include "BuildConfig.h" +#include "Markdown.h" +#include "ui_UpdateAvailableDialog.h" + +UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, + const QString& availableVersion, + const QString& releaseNotes, + QWidget* parent) + : QDialog(parent), ui(new Ui::UpdateAvailableDialog) +{ + ui->setupUi(this); + + QString launcherName = BuildConfig.LAUNCHER_DISPLAYNAME; + + ui->headerLabel->setText(tr("A new version of %1 is available!").arg(launcherName)); + ui->versionAvailableLabel->setText( + tr("Version %1 is now available - you have %2 . Would you like to download it now?").arg(availableVersion).arg(currentVersion)); + ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64)); + + auto releaseNotesHtml = markdownToHTML(releaseNotes); + ui->releaseNotes->setHtml(releaseNotesHtml); + ui->releaseNotes->setOpenExternalLinks(true); + + connect(ui->skipButton, &QPushButton::clicked, this, [this](){ + this->setResult(DialogCode::Skip); + this->close(); + }); + + connect(ui->delayButton, &QPushButton::clicked, this, [this](){ + this->setResult(DialogCode::DontInstall); + this->close(); + }); + + connect(ui->installButton, &QPushButton::clicked, this, [this](){ + this->setResult(DialogCode::Install); + this->close(); + }); +} diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h new file mode 100644 index 000000000..d37839fda --- /dev/null +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-only + +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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 . + * + */ +#pragma once + +#include + +namespace Ui { +class UpdateAvailableDialog; +} + +class UpdateAvailableDialog : public QDialog { + Q_OBJECT + + public: + + enum DialogCode { + Install, + DontInstall, + Skip, + }; + + explicit UpdateAvailableDialog(const QString& currentVersion, + const QString& availableVersion, + const QString& releaseNotes, + QWidget* parent = 0); + ~UpdateAvailableDialog(); + + private: + Ui::UpdateAvailableDialog* ui; +}; diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.ui b/launcher/ui/dialogs/UpdateAvailableDialog.ui new file mode 100644 index 000000000..b0d85f6f0 --- /dev/null +++ b/launcher/ui/dialogs/UpdateAvailableDialog.ui @@ -0,0 +1,155 @@ + + + UpdateAvailableDialog + + + + 0 + 0 + 636 + 352 + + + + Update Available + + + + + + + + + + + 64 + 64 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 11 + 75 + true + + + + A new version is available! + + + + + + + Version %1 is now available - you have %2 . Would you like to download it now? + + + + + + + + 75 + true + + + + Release Notes: + + + + + + + + + + + + + + + + Skip This Version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remind Me Later + + + false + + + false + + + + + + + Install Update + + + true + + + + + + + + + + diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 9ec033f02..f2c88cf6a 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -21,19 +21,22 @@ */ #include "PrismExternalUpdater.h" -#include +#include #include -#include #include #include -#include +#include #include -#include +#include +#include +#include #include "StringUtils.h" #include "BuildConfig.h" +#include "ui/dialogs/UpdateAvailableDialog.h" + class PrismExternalUpdater::Private { public: QDir appDir; @@ -78,12 +81,11 @@ PrismExternalUpdater::~PrismExternalUpdater() void PrismExternalUpdater::checkForUpdates() { - QProgressDialog progress(tr("Checking for updates..."), "", 0, -1); + QProgressDialog progress(tr("Checking for updates..."), "", 0, 0); progress.setCancelButton(nullptr); progress.show(); QCoreApplication::processEvents(); - QProcess proc; auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 @@ -98,14 +100,16 @@ void PrismExternalUpdater::checkForUpdates() auto result_start = proc.waitForStarted(5000); if (!result_start) { auto err = proc.error(); - qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); + qDebug() << "Failed to start updater after 5 seconds." + << "reason:" << err << proc.errorString(); } QCoreApplication::processEvents(); auto result_finished = proc.waitForFinished(60000); if (!result_finished) { auto err = proc.error(); - qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); + qDebug() << "Updater failed to close after 60 seconds." + << "reason:" << err << proc.errorString(); } auto exit_code = proc.exitCode(); @@ -116,6 +120,9 @@ void PrismExternalUpdater::checkForUpdates() qDebug() << "captured output:" << std_output; qDebug() << "captured error:" << std_error; + progress.hide(); + QCoreApplication::processEvents(); + switch (exit_code) { case 0: // no update available @@ -134,12 +141,14 @@ void PrismExternalUpdater::checkForUpdates() { auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n'); auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n'); - auto [third_line, changelog] = StringUtils::splitFirst(remainder2, '\n'); + auto [third_line, release_notes] = StringUtils::splitFirst(remainder2, '\n'); auto version_name = StringUtils::splitFirst(first_line, ": ").second; auto version_tag = StringUtils::splitFirst(second_line, ": ").second; auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate); qDebug() << "Update available:" << version_name << version_tag << release_timestamp; - qDebug() << "Update changelog:" << changelog; + qDebug() << "Update release notes:" << release_notes; + + offerUpdate(version_name, version_tag, release_notes); } break; default: @@ -153,48 +162,55 @@ void PrismExternalUpdater::checkForUpdates() priv->settings->sync(); } -bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() { +bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() +{ return priv->autoCheck; } -double PrismExternalUpdater::getUpdateCheckInterval() { +double PrismExternalUpdater::getUpdateCheckInterval() +{ return priv->updateInterval; } -bool PrismExternalUpdater::getBetaAllowed() { +bool PrismExternalUpdater::getBetaAllowed() +{ return priv->allowBeta; } -void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) { +void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) +{ priv->autoCheck = check; priv->settings->setValue("auto_check", check); priv->settings->sync(); resetAutoCheckTimer(); } -void PrismExternalUpdater::setUpdateCheckInterval(double seconds) { +void PrismExternalUpdater::setUpdateCheckInterval(double seconds) +{ priv->updateInterval = seconds; priv->settings->setValue("update_interval", seconds); priv->settings->sync(); resetAutoCheckTimer(); } -void PrismExternalUpdater::setBetaAllowed(bool allowed) { +void PrismExternalUpdater::setBetaAllowed(bool allowed) +{ priv->allowBeta = allowed; priv->settings->setValue("auto_beta", allowed); priv->settings->sync(); } -void PrismExternalUpdater::resetAutoCheckTimer() { +void PrismExternalUpdater::resetAutoCheckTimer() +{ int timeoutDuration = 0; auto now = QDateTime::currentDateTime(); if (priv->autoCheck) { - if (priv->lastCheck.isValid()) { + if (priv->lastCheck.isValid()) { auto diff = priv->lastCheck.secsTo(now); auto secs_left = priv->updateInterval - diff; if (secs_left < 0) secs_left = 0; - timeoutDuration = secs_left * 1000; // to msec + timeoutDuration = secs_left * 1000; // to msec } qDebug() << "Auto update timer starting," << timeoutDuration / 1000 << "seconds left"; priv->updateTimer.start(timeoutDuration); @@ -202,18 +218,66 @@ void PrismExternalUpdater::resetAutoCheckTimer() { if (priv->updateTimer.isActive()) priv->updateTimer.stop(); } - } -void PrismExternalUpdater::connectTimer() { +void PrismExternalUpdater::connectTimer() +{ connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); - } -void PrismExternalUpdater::disconnectTimer() { +void PrismExternalUpdater::disconnectTimer() +{ disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); } -void PrismExternalUpdater::autoCheckTimerFired() { +void PrismExternalUpdater::autoCheckTimerFired() +{ checkForUpdates(); } + +void PrismExternalUpdater::offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes) +{ + priv->settings->beginGroup("skip"); + auto should_skip = priv->settings->value(version_tag, false).toBool(); + priv->settings->endGroup(); + + if (should_skip) + return; + + UpdateAvailableDialog dlg(BuildConfig.printableVersionString(), version_name, release_notes); + + auto result = dlg.exec(); + switch (result) { + case UpdateAvailableDialog::Install: { + performUpdate(version_tag); + } + case UpdateAvailableDialog::Skip: { + priv->settings->beginGroup("skip"); + priv->settings->setValue(version_tag, true); + priv->settings->endGroup(); + priv->settings->sync(); + return; + } + case UpdateAvailableDialog::DontInstall: { + return; + } + } +} + +void PrismExternalUpdater::performUpdate(const QString& version_tag) { + QProcess proc; + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + + QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; + if (priv->allowBeta) + args.append("--pre-release"); + + auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args); + if (!result) { + qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); + } + QCoreApplication::exit(); +} diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h index 852fb7d37..f8ed29ccc 100644 --- a/launcher/updater/PrismExternalUpdater.h +++ b/launcher/updater/PrismExternalUpdater.h @@ -34,7 +34,7 @@ class PrismExternalUpdater : public ExternalUpdater { Q_OBJECT public: - PrismExternalUpdater(const QString& appDir, const QString& dataDir); + PrismExternalUpdater(const QString& appDir, const QString& dataDir); ~PrismExternalUpdater() override; /*! @@ -82,7 +82,8 @@ class PrismExternalUpdater : public ExternalUpdater { void disconnectTimer(); void connectTimer(); - void performUpdate(); + void offerUpdate(const QString& version_name, const QString& version_tag, const QString& release_notes); + void performUpdate(const QString& version_tag); public slots: void autoCheckTimerFired(); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index e01295605..658b1a57b 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -630,7 +630,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) for (auto file : files) { file_list.append(file.trimmed()); } - } catch (FS::FileSystemException) { + } catch (FS::FileSystemException&) { } } @@ -675,15 +675,23 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) if (error) { logUpdate(tr("There were errors installing the update.")); auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); - FS::move(m_updateLogPath, fail_marker); + FS::copy(m_updateLogPath, fail_marker)(); } else { logUpdate(tr("Update succeed.")); auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); - FS::move(m_updateLogPath, success_marker); + FS::copy(m_updateLogPath, success_marker)(); } auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::deletePath(update_lock_path); + QProcess proc; + auto app_exe_name = BuildConfig.LAUNCHER_APP_BINARY_NAME; +#if defined Q_OS_WIN32 + app_exe_name.append(".exe"); +#endif + auto app_exe_path = target.absoluteFilePath(app_exe_name); + proc.startDetached(app_exe_path); + exit(error ? 1 : 0); } @@ -897,7 +905,7 @@ bool write_lock_file(const QString& path, QDateTime timestamp, QString from, QSt .arg(target) .arg(data_path) .toUtf8()); - } catch (FS::FileSystemException err) { + } catch (FS::FileSystemException& err) { qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause(); return false; } @@ -922,7 +930,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) "\n" "This likely means that a previous update attempt failed. Please ensure your installation is in working order before " "proceeding.\n" - "Check the Prism Launcher updater log at \n" + "Check the Prism Launcher updater log at: \n" "%7\n" "for details on the last update attempt.\n" "\n" @@ -936,9 +944,9 @@ void PrismUpdaterApp::performInstall(QFileInfo file) msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); switch (msgBox.exec()) { - case QMessageBox::Ignore: + case QMessageBox::AcceptRole: break; - case QMessageBox::Cancel: + case QMessageBox::RejectRole: [[fallthrough]]; default: return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); @@ -1005,7 +1013,7 @@ void PrismUpdaterApp::backupAppDir() for (auto file : files) { file_list.append(file.trimmed()); } - } catch (FS::FileSystemException) { + } catch (FS::FileSystemException&) { } } From c0f046255084ce88323375af665c2d6bf27e2385 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:41:51 -0700 Subject: [PATCH 37/72] fix(updater): logs/ folder Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 10 ++++++++-- launcher/ui/dialogs/UpdateAvailableDialog.h | 2 +- launcher/updater/prismupdater/PrismUpdater.cpp | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a04f85c28..e172472a1 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -831,7 +831,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + auto updater_binary = QFileInfo(QDir(m_rootPath).absoluteFilePath(exe_name)); + if (updater_binary.isFile()) + m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } @@ -1058,7 +1064,7 @@ void Application::performMainStartupAction() { m_status = Application::Initialized; - auto update_log_path = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); if (update_lock.exists()) { diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h index d37839fda..7a14c01da 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.h +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -42,7 +42,7 @@ class UpdateAvailableDialog : public QDialog { const QString& availableVersion, const QString& releaseNotes, QWidget* parent = 0); - ~UpdateAvailableDialog(); + ~UpdateAvailableDialog() = default; private: Ui::UpdateAvailableDialog* ui; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 658b1a57b..439457bae 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -343,10 +343,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar #endif } - m_updateLogPath = FS::PathCombine(m_dataPath, "prism_launcher_update.log"); + m_updateLogPath = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); { // setup logging - static const QString logBase = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; + static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; + static const QString logBase = FS::PathCombine("logs", baseLogFile); auto moveFile = [](const QString& oldName, const QString& newName) { QFile::remove(newName); QFile::copy(oldName, newName); From 6f7454243ef8d6c9b317ee7e83cf0c361f863c14 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Mon, 26 Jun 2023 23:24:46 -0700 Subject: [PATCH 38/72] fix: prep for changes in #1276 - sign updater Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 9 +- CMakeLists.txt | 10 +- buildconfig/BuildConfig.cpp.in | 22 ++ buildconfig/BuildConfig.h | 36 ++- launcher/Application.cpp | 229 +++++++++--------- .../updater/prismupdater/PrismUpdater.cpp | 16 +- 6 files changed, 197 insertions(+), 125 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 777e7a79b..5a1554fc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -283,12 +283,12 @@ jobs: if: runner.os == 'Windows' && matrix.msystem != '' shell: msys2 {0} run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -303,7 +303,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_UPDATER_GITHUB_REPO=https://github.com/${{ github.repository }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja ## # BUILD @@ -391,7 +391,6 @@ jobs: if: runner.os == 'Windows' && matrix.msystem == '' run: | cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }} - Get-Content ${{ env.BUILD_DIR }}/install_manifest.txt | %{ $_.TrimStart("$pwd/") } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('./') } | Out-File -Append -FilePath ${{ env.INSTALL_DIR }}/manifest.txt cd ${{ env.INSTALL_DIR }} if ("${{ matrix.qt_ver }}" -eq "5") @@ -416,7 +415,7 @@ jobs: if (Get-Content ./codesign.pfx){ cd ${{ env.INSTALL_DIR }} # We ship the exact same executable for portable and non-portable editions, so signing just once is fine - SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe + SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe } else { ":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY } diff --git a/CMakeLists.txt b/CMakeLists.txt index cb75f769c..bbe8bfbbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,10 @@ set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_M set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") # Github repo URL with releases for updater -set(Launcher_UPDATER_GITHUB_REPO "" CACHE STRING "Base URL for the updater.") +set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.") + +# Name to help updater identify valid artifacts +set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.") # The metadata server set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") @@ -193,6 +196,11 @@ set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING " # This key was issued specifically for Prism Launcher set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform") +set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID}) +set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) +set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME}) +set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION}) +set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) #### Check the current Git commit and branch include(GetGitRevisionDescription) diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index b03867c3c..e8e8a4ef8 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -33,6 +33,7 @@ * limitations under the License. */ +#include #include "BuildConfig.h" #include @@ -59,9 +60,17 @@ Config::Config() VERSION_MINOR = @Launcher_VERSION_MINOR@; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; + BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@"; + COMPILER_NAME = "@Launcher_COMPILER_NAME@"; + COMPILER_VERSION = "@Launcher_COMPILER_VERSION@"; + + COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@"; + COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@"; + COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@"; + MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; @@ -133,3 +142,16 @@ QString Config::printableVersionString() const } return vstr; } + +QString Config::compilerID() const +{ + if (COMPILER_VERSION.isEmpty()) + return COMPILER_NAME; + return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION); +} + +QString Config::systemID() const +{ + return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR); +} + diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index b6ae6e6d8..723b2e714 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -36,8 +36,8 @@ */ #pragma once -#include #include +#include /** * \brief The Config class holds all the build-time information passed from the build system. @@ -71,9 +71,27 @@ class Config { /// A short string identifying this build's platform. For example, "lin64" or "win32". QString BUILD_PLATFORM; + /// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32". + QString BUILD_ARTIFACT; + /// A string containing the build timestamp QString BUILD_DATE; + /// A string identifying the compiler use to build + QString COMPILER_NAME; + + /// A string identifying the compiler version used to build + QString COMPILER_VERSION; + + /// A string identifying the compiler target system os + QString COMPILER_TARGET_SYSTEM; + + /// A String identifying the compiler target system version + QString COMPILER_TARGET_SYSTEM_VERSION; + + /// A String identifying the compiler target processor + QString COMPILER_TARGET_SYSTEM_PROCESSOR; + /// URL for the updater's channel QString UPDATER_GITHUB_REPO; @@ -145,7 +163,7 @@ class Config { QString AUTH_BASE = "https://authserver.mojang.com/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists - QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists + QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; @@ -162,7 +180,7 @@ class Config { QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; - QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"}; + QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" }; QString FLAME_BASE_URL = "https://api.curseforge.com/v1"; @@ -172,6 +190,18 @@ class Config { * \return The version number in string format (major.minor.revision.build). */ QString printableVersionString() const; + + /** + * \brief Compiler ID String + * \return a string of the form "Name - Version" of just "Name" if the version is empty + */ + QString compilerID() const; + + /** + * \brief System ID String + * \return a string of the form "OS Verison Processor" + */ + QString systemID() const; }; extern const Config BuildConfig; diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e172472a1..a8b26ed74 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -175,6 +175,34 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QSt } // namespace +std::tuple read_lock_File(const QString& path) +{ + auto contents = QString(FS::read(path)); + auto lines = contents.split('\n'); + + QDateTime timestamp; + QString from, to, target, data_path; + for (auto line : lines) { + auto index = line.indexOf("="); + if (index < 0) + continue; + auto left = line.left(index); + auto right = line.mid(index + 1); + if (left.toLower() == "timestamp") { + timestamp = QDateTime::fromString(right, Qt::ISODate); + } else if (left.toLower() == "from") { + from = right; + } else if (left.toLower() == "to") { + to = right; + } else if (left.toLower() == "target") { + target = right; + } else if (left.toLower() == "data_path") { + data_path = right; + } + } + return std::make_tuple(timestamp, from, to, target, data_path); +} + #if defined Q_OS_WIN32 // taken from https://stackoverflow.com/a/25927081 @@ -554,6 +582,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; + qDebug() << "Compiled for : " << BuildConfig.systemID(); + qDebug() << "Compiled by : " << BuildConfig.compilerID(); if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); @@ -942,6 +972,95 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) applyCurrentlySelectedTheme(true); + // check update locks + { + auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); + + auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); + if (update_lock.exists()) { + auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); + auto infoMsg = tr("This installation has a update lock file present at: %1\n" + "\n" + "Timestamp: %2\n" + "Updating from version %3 to %4\n" + "Target install path: %5\n" + "Data Path: %6" + "\n" + "This likely means that a update attempt failed. Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%7\n" + "for details on the last update attempt.\n" + "\n" + "To delete this lock and proceed select \"Ignore\" below.") + .arg(update_lock.absoluteFilePath()) + .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + auto res = msgBox.exec(); + switch (res) { + case QMessageBox::Ignore: { + FS::deletePath(update_lock.absoluteFilePath()); + break; + } + case QMessageBox::Abort: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + return; + } + } + } + + auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); + if (update_fail_marker.exists()) { + auto infoMsg = tr("An update attempt failed\n" + "\n" + "Please ensure your installation is in working order before " + "proceeding.\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details on the last update attempt.") + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.setModal(true); + auto res = msgBox.exec(); + switch (res) { + case QMessageBox::Ignore: { + FS::deletePath(update_fail_marker.absoluteFilePath()); + break; + } + case QMessageBox::Abort: + [[fallthrough]]; + default: { + qDebug() << "Exiting because update lockfile is present"; + QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + return; + } + } + } + + auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); + if (update_success_marker.exists()) { + auto infoMsg = tr("Update succeeded\n" + "\n" + "You are now running %1 .\n" + "Check the Prism Launcher updater log at: \n" + "%1\n" + "for details.") + .arg(BuildConfig.printableVersionString()) + .arg(update_log_path); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.open(); + FS::deletePath(update_success_marker.absoluteFilePath()); + } + } + updateCapabilities(); if (createSetupWizard()) { @@ -1032,119 +1151,11 @@ void Application::setupWizardFinished(int status) performMainStartupAction(); } -std::tuple read_lock_File(const QString& path) -{ - auto contents = QString(FS::read(path)); - auto lines = contents.split('\n'); - - QDateTime timestamp; - QString from, to, target, data_path; - for (auto line : lines) { - auto index = line.indexOf("="); - if (index < 0) - continue; - auto left = line.left(index); - auto right = line.mid(index + 1); - if (left.toLower() == "timestamp") { - timestamp = QDateTime::fromString(right, Qt::ISODate); - } else if (left.toLower() == "from") { - from = right; - } else if (left.toLower() == "to") { - to = right; - } else if (left.toLower() == "target") { - target = right; - } else if (left.toLower() == "data_path") { - data_path = right; - } - } - return std::make_tuple(timestamp, from, to, target, data_path); -} - void Application::performMainStartupAction() { m_status = Application::Initialized; - auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); - - auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); - if (update_lock.exists()) { - auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); - auto infoMsg = tr("This installation has a update lock file present at: %1\n" - "\n" - "Timestamp: %2\n" - "Updating from version %3 to %4\n" - "Target install path: %5\n" - "Data Path: %6" - "\n" - "This likely means that a update attempt failed. Please ensure your installation is in working order before " - "proceeding.\n" - "Check the Prism Launcher updater log at: \n" - "%7\n" - "for details on the last update attempt.\n" - "\n" - "To delete this lock and proceed select \"Ignore\" below.") - .arg(update_lock.absoluteFilePath()) - .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) - .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); - msgBox.setDefaultButton(QMessageBox::Abort); - msgBox.setModal(true); - switch (msgBox.exec()) { - case QMessageBox::AcceptRole: { - FS::deletePath(update_lock.absoluteFilePath()); - break; - } - case QMessageBox::RejectRole: - [[fallthrough]]; - default: { - qDebug() << "Exiting because update lockfile is present"; - exit(1); - } - } - } - - auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); - if (update_fail_marker.exists()) { - auto infoMsg = tr("An update attempt failed\n" - "\n" - "Please ensure your installation is in working order before " - "proceeding.\n" - "Check the Prism Launcher updater log at: \n" - "%1\n" - "for details on the last update attempt.") - .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); - msgBox.setDefaultButton(QMessageBox::Abort); - msgBox.setModal(true); - switch (msgBox.exec()) { - case QMessageBox::AcceptRole: { - FS::deletePath(update_fail_marker.absoluteFilePath()); - break; - } - case QMessageBox::RejectRole: - [[fallthrough]]; - default: { - qDebug() << "Exiting because update lockfile is present"; - exit(1); - } - } - } - - auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); - if (update_success_marker.exists()) { - auto infoMsg = tr("Update succeeded\n" - "\n" - "You are now running %1 .\n" - "Check the Prism Launcher updater log at: \n" - "%1\n" - "for details.") - .arg(BuildConfig.printableVersionString()) - .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.open(); - FS::deletePath(update_success_marker.absoluteFilePath()); - } + if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 439457bae..3fd832b24 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -244,7 +244,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar auto updater_executable = QCoreApplication::applicationFilePath(); - if (BuildConfig.BUILD_PLATFORM.toLower() == "macos") + if (BuildConfig.BUILD_ARTIFACT.toLower() == "macos") showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); if (updater_executable.startsWith("/tmp/.mount_")) { @@ -435,6 +435,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Version : " << BuildConfig.printableVersionString(); qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; + qDebug() << "Compiled for : " << BuildConfig.systemID(); + qDebug() << "Compiled by : " << BuildConfig.compilerID(); if (adjustedBy.size()) { qDebug() << "Data dir before adjustment : " << origCwdPath; qDebug() << "Data dir after adjustment : " << m_dataPath; @@ -576,7 +578,7 @@ void PrismUpdaterApp::run() return exit(result ? 0 : 1); } - if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) { + if (BuildConfig.BUILD_ARTIFACT.toLower() == "linux" && !m_isPortable) { showFatalErrorMessage(tr("Updating Not Supported"), tr("Updating non-portable linux installations is not supported. Please use your system package manager")); return; @@ -751,9 +753,9 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel { QList valid; - qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_PLATFORM + qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_ARTIFACT << "portable:" << m_isPortable; - if (BuildConfig.BUILD_PLATFORM.isEmpty()) + if (BuildConfig.BUILD_ARTIFACT.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) @@ -761,7 +763,7 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) continue; auto asset_name = asset.name.toLower(); - auto platform = BuildConfig.BUILD_PLATFORM.toLower(); + auto platform = BuildConfig.BUILD_ARTIFACT.toLower(); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); auto asset_is_arm = asset_name.contains("arm64"); auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); @@ -808,7 +810,7 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release) tr("No Valid Release Assets"), tr("Github release %1 has no valid assets for this platform: %2") .arg(release.tag_name) - .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable ? tr("yes") : tr("no")))); + .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_ARTIFACT).arg(m_isPortable ? tr("yes") : tr("no")))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { @@ -1020,7 +1022,7 @@ void PrismUpdaterApp::backupAppDir() if (file_list.isEmpty()) { // best guess - if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") { + if (BuildConfig.BUILD_ARTIFACT.toLower() == "linux") { file_list.append({ "PrismLauncher", "bin", "share", "lib" }); } else { // windows by process of elimination file_list.append({ From cb35fb8d0ed9836c49d0f50f78fdd3f449244035 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:56:02 -0700 Subject: [PATCH 39/72] add optional pre-release tag Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- CMakeLists.txt | 3 ++- buildconfig/BuildConfig.cpp.in | 3 ++- buildconfig/BuildConfig.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbe8bfbbc..c67febdb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,8 +140,9 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 8) set(Launcher_VERSION_MINOR 0) +set(Launcher_VERSION_PRERELEASE "") -set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") +set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}${Launcher_VERSION_PRERELEASE}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index e8e8a4ef8..3e0547046 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -58,6 +58,7 @@ Config::Config() // Version information VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@; + VERSION_PRERELEASE = "@Launcher_VERSION_PRERELEASE@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; @@ -128,7 +129,7 @@ Config::Config() QString Config::versionString() const { - return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR); + return QString("%1.%2%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PRERELEASE); } QString Config::printableVersionString() const diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 723b2e714..f8835f1b3 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -60,6 +60,8 @@ class Config { /// The minor version number. int VERSION_MINOR; + QString VERSION_PRERELEASE; + /** * The version channel * This is used by the updater to determine what channel the current version came from. From 4123343130a1b5e6f443a465b719775f2520c045 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 00:25:09 -0700 Subject: [PATCH 40/72] no need for pre release tag Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- CMakeLists.txt | 3 +- buildconfig/BuildConfig.cpp.in | 3 +- buildconfig/BuildConfig.h | 2 - launcher/filelink/FileLink.cpp | 121 +++++++++++++++--- .../updater/prismupdater/PrismUpdater.cpp | 21 ++- 5 files changed, 117 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c67febdb2..bbe8bfbbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,9 +140,8 @@ set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRIN ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 8) set(Launcher_VERSION_MINOR 0) -set(Launcher_VERSION_PRERELEASE "") -set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}${Launcher_VERSION_PRERELEASE}") +set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 3e0547046..e8e8a4ef8 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -58,7 +58,6 @@ Config::Config() // Version information VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@; - VERSION_PRERELEASE = "@Launcher_VERSION_PRERELEASE@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; @@ -129,7 +128,7 @@ Config::Config() QString Config::versionString() const { - return QString("%1.%2%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PRERELEASE); + return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR); } QString Config::printableVersionString() const diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index f8835f1b3..723b2e714 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -60,8 +60,6 @@ class Config { /// The minor version number. int VERSION_MINOR; - QString VERSION_PRERELEASE; - /** * The version channel * This is used by the updater to determine what channel the current version came from. diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 79b30c665..be69de007 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -40,8 +40,11 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#include +#include #include #include +#include #endif // Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header @@ -63,26 +66,112 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif +#if defined Q_OS_WIN32 + +// taken from https://stackoverflow.com/a/25927081 +// getting a proper output to console with redirection support on windows is apparently hell +void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) +{ + // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been + // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 + // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our + // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value + // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to + // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target + // using the "_dup2" function. + if (bindStdIn) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "r", stdin); + } + if (bindStdOut) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stdout); + } + if (bindStdErr) { + FILE* dummyFile; + freopen_s(&dummyFile, "nul", "w", stderr); + } + + // Redirect unbuffered stdin from the current standard input handle + if (bindStdIn) { + HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "r"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdin)); + if (dup2Result == 0) { + setvbuf(stdin, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stdout to the current standard output handle + if (bindStdOut) { + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + setvbuf(stdout, NULL, _IONBF, 0); + } + } + } + } + } + + // Redirect unbuffered stderr to the current standard error handle + if (bindStdErr) { + HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + if (fileDescriptor != -1) { + FILE* file = _fdopen(fileDescriptor, "w"); + if (file != NULL) { + int dup2Result = _dup2(_fileno(file), _fileno(stderr)); + if (dup2Result == 0) { + setvbuf(stderr, NULL, _IONBF, 0); + } + } + } + } + } + + // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the + // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In + // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything + // has been read from or written to the targets or not. + if (bindStdIn) { + std::wcin.clear(); + std::cin.clear(); + } + if (bindStdOut) { + std::wcout.clear(); + std::cout.clear(); + } + if (bindStdErr) { + std::wcerr.clear(); + std::cerr.clear(); + } +} +#endif + FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this)) { #if defined Q_OS_WIN32 - // attach the parent console - if (AttachConsole(ATTACH_PARENT_PROCESS)) { - // if attach succeeds, reopen and sync all the i/o - if (freopen("CON", "w", stdout)) { - std::cout.sync_with_stdio(); + // attach the parent console if stdout not already captured + auto stdout_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + if (stdout_type == FILE_TYPE_CHAR || stdout_type == FILE_TYPE_UNKNOWN) { + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + BindCrtHandlesToStdHandles(true, true, true); + consoleAttached = true; } - if (freopen("CON", "w", stderr)) { - std::cerr.sync_with_stdio(); - } - if (freopen("CON", "r", stdin)) { - std::cin.sync_with_stdio(); - } - auto out = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD written; - const char* endline = "\n"; - WriteConsole(out, endline, strlen(endline), &written, NULL); - consoleAttached = true; } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 3fd832b24..614174446 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -39,8 +39,8 @@ #include #include -#include #include +#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -209,7 +209,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar BindCrtHandlesToStdHandles(true, true, true); consoleAttached = true; } - } + } #endif setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); @@ -272,8 +272,6 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar auto prism_update_url = parser.value("update-url"); if (prism_update_url.isEmpty()) prism_update_url = BuildConfig.UPDATER_GITHUB_REPO; - if (prism_update_url.isEmpty()) - prism_update_url = "https://github.com/PrismLauncher/PrismLauncher"; m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); @@ -354,18 +352,20 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar QFile::remove(oldName); }; - moveFile(logBase.arg(1), logBase.arg(2)); - moveFile(logBase.arg(0), logBase.arg(1)); + if (FS::ensureFolderPathExists("logs")) { + moveFile(logBase.arg(1), logBase.arg(2)); + moveFile(logBase.arg(0), logBase.arg(1)); + } logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { showFatalErrorMessage(tr("The launcher data folder is not writable!"), - tr("The launcher couldn't create a log file - the data folder is not writable.\n" + tr("The updater couldn't create a log file - the data folder is not writable.\n" "\n" "Make sure you have write permissions to the data folder.\n" "(%1)\n" "\n" - "The launcher cannot continue until you fix this problem.") + "The updater cannot continue until you fix this problem.") .arg(m_dataPath)); return; } @@ -560,8 +560,7 @@ void PrismUpdaterApp::run() stdOutStream.flush(); return exit(100); - } - else { + } else { return exit(0); } } @@ -1155,7 +1154,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) if (first_parts.length() < 2) return false; m_prismBinaryName = first_parts.takeFirst(); - auto version = first_parts.takeFirst(); + auto version = first_parts.takeFirst().trimmed(); m_prismVersion = version; if (version.contains('-')) { auto index = version.indexOf('-'); From 46e840fdf12a039e4e7f5e4ee49f834099e3d774 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 09:18:07 -0700 Subject: [PATCH 41/72] fix: add thread sleep to wait for resources - add detail text from logs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 5 ++++- launcher/updater/PrismExternalUpdater.cpp | 18 +++++++++++------- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a8b26ed74..4c3e36d64 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -999,6 +999,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); + msgBox.setDetailedText(FS::read(update_log_path)); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1028,6 +1029,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); + msgBox.setDetailedText(FS::read(update_log_path)); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1056,7 +1058,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) .arg(update_log_path); auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.open(); + msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.exec(); FS::deletePath(update_success_marker.absoluteFilePath()); } } diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index f2c88cf6a..42515332e 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -23,13 +23,14 @@ #include "PrismExternalUpdater.h" #include #include +#include #include +#include #include #include #include #include #include -#include #include "StringUtils.h" @@ -117,9 +118,6 @@ void PrismExternalUpdater::checkForUpdates() auto std_output = proc.readAllStandardOutput(); auto std_error = proc.readAllStandardError(); - qDebug() << "captured output:" << std_output; - qDebug() << "captured error:" << std_error; - progress.hide(); QCoreApplication::processEvents(); @@ -128,12 +126,17 @@ void PrismExternalUpdater::checkForUpdates() // no update available { qDebug() << "No update available"; + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version.")); + msgBox.exec(); } break; case 1: // there was an error { - qDebug() << "Updater subprocess error" << std_error; + qDebug() << "Updater subprocess error" << qPrintable(std_error); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); + msgBox.setDetailedText(std_error); + msgBox.exec(); } break; case 100: @@ -264,14 +267,15 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin } } -void PrismExternalUpdater::performUpdate(const QString& version_tag) { +void PrismExternalUpdater::performUpdate(const QString& version_tag) +{ QProcess proc; auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); #endif - QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; + QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; if (priv->allowBeta) args.append("--pre-release"); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 614174446..4df103517 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -617,6 +617,10 @@ void PrismUpdaterApp::run() void PrismUpdaterApp::moveAndFinishUpdate(QDir target) { logUpdate("Finishing update process"); + + logUpdate("Waiting 2 seconds for resources to free"); + this->thread()->sleep(2); + auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); QFileInfo manifest(manifest_path); From e29126ca26a6817d2f5060aacd6a9f1eb5a0eb6d Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 10:56:16 -0700 Subject: [PATCH 42/72] fix: add split markdown source Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 9534c3a3f..9ac496361 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -600,6 +600,7 @@ set(PRISMUPDATER_SOURCES Version.h Version.cpp Markdown.h + Markdown.cpp # Zip MMCZip.h From f287d9deaca3970592edf5ae99f190dc12f11a36 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:09:59 -0700 Subject: [PATCH 43/72] fix add NetRequest source Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 080882c6e..4b0e98f83 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -623,6 +623,8 @@ set(PRISMUPDATER_SOURCES net/Logging.h net/Logging.cpp net/NetAction.h + net/NetRequest.cpp + net/NetRequest.h net/NetJob.cpp net/NetJob.h net/NetUtils.h From c1235583146e6f1f6fae997b8ea2808687dbb372 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:25:59 -0700 Subject: [PATCH 44/72] fix: add build config header & trimlines form stdout Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/net/NetRequest.cpp | 1 + launcher/updater/PrismExternalUpdater.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index 304577121..66307099c 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -46,6 +46,7 @@ #if defined(LAUNCHER_APPLICATION) #include "Application.h" #endif +#include "BuildConfig.h" #include "net/NetAction.h" diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 42515332e..9b99b2008 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -145,9 +145,9 @@ void PrismExternalUpdater::checkForUpdates() auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n'); auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\n'); auto [third_line, release_notes] = StringUtils::splitFirst(remainder2, '\n'); - auto version_name = StringUtils::splitFirst(first_line, ": ").second; - auto version_tag = StringUtils::splitFirst(second_line, ": ").second; - auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate); + auto version_name = StringUtils::splitFirst(first_line, ": ").second.trimmed(); + auto version_tag = StringUtils::splitFirst(second_line, ": ").second.trimmed(); + auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second.trimmed(), Qt::ISODate); qDebug() << "Update available:" << version_name << version_tag << release_timestamp; qDebug() << "Update release notes:" << release_notes; From 400a2f7201ecb05af769dff5ea8738dde19205ed Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 12:46:28 -0700 Subject: [PATCH 45/72] fix: final fixes - use `done(code)` for offer dialog Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 12 ++++++------ launcher/ui/dialogs/UpdateAvailableDialog.h | 8 ++++---- launcher/updater/PrismExternalUpdater.cpp | 10 ++++++++-- launcher/updater/prismupdater/PrismUpdater.cpp | 9 +++++++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 9f7308cba..28f7167cb 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -47,17 +47,17 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, ui->releaseNotes->setOpenExternalLinks(true); connect(ui->skipButton, &QPushButton::clicked, this, [this](){ - this->setResult(DialogCode::Skip); - this->close(); + setResult(ResultCode::Skip); + done(ResultCode::Skip); }); connect(ui->delayButton, &QPushButton::clicked, this, [this](){ - this->setResult(DialogCode::DontInstall); - this->close(); + setResult(ResultCode::DontInstall); + done(ResultCode::DontInstall); }); connect(ui->installButton, &QPushButton::clicked, this, [this](){ - this->setResult(DialogCode::Install); - this->close(); + setResult(ResultCode::Install); + done(ResultCode::Install); }); } diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h index 7a14c01da..f3ea5cbb1 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.h +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -32,10 +32,10 @@ class UpdateAvailableDialog : public QDialog { public: - enum DialogCode { - Install, - DontInstall, - Skip, + enum ResultCode { + Install = 10, + DontInstall = 11, + Skip = 12, }; explicit UpdateAvailableDialog(const QString& currentVersion, diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 9b99b2008..969897405 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -134,7 +134,8 @@ void PrismExternalUpdater::checkForUpdates() // there was an error { qDebug() << "Updater subprocess error" << qPrintable(std_error); - auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); + auto msgBox = + QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); msgBox.setDetailedText(std_error); msgBox.exec(); } @@ -244,15 +245,20 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin auto should_skip = priv->settings->value(version_tag, false).toBool(); priv->settings->endGroup(); - if (should_skip) + if (should_skip) { + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available.")); + msgBox.exec(); return; + } UpdateAvailableDialog dlg(BuildConfig.printableVersionString(), version_name, release_notes); auto result = dlg.exec(); + qDebug() << "offer dlg result" << result; switch (result) { case UpdateAvailableDialog::Install: { performUpdate(version_tag); + return; } case UpdateAvailableDialog::Skip: { priv->settings->beginGroup("skip"); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 4df103517..f8819d940 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -327,6 +327,11 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar // on command line adjustedBy = "Command line"; m_dataPath = dirParam; +#ifndef Q_OS_MACOS + if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { + m_isPortable = true; + } +#endif } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); m_dataPath = foo.absolutePath(); @@ -958,8 +963,6 @@ void PrismUpdaterApp::performInstall(QFileInfo file) return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); } } - write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), - m_dataPath); clearUpdateLog(); auto changelog_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.changelog"); @@ -967,6 +970,8 @@ void PrismUpdaterApp::performInstall(QFileInfo file) logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { + write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), + m_dataPath); logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { From cb7ff81ade4bdc125d19b85aa48a67a493994305 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:51:58 -0700 Subject: [PATCH 46/72] fix: copy needs to overwrite Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/FileSystem.cpp | 3 +++ launcher/FileSystem.h | 6 ++++++ launcher/updater/prismupdater/PrismUpdater.cpp | 8 ++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index eff4884db..6dfb69bfa 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -297,6 +297,9 @@ bool copy::operator()(const QString& offset, bool dryRun) if (!m_followSymlinks) opt |= copy_opts::copy_symlinks; + if (m_overwrite) + opt |= copy_opts::overwrite_existing; + // Function that'll do the actual copying auto copy_file = [&](QString src_path, QString relative_dst_path) { if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist)) diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index ca26c1ee7..ab29cf936 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -120,6 +120,11 @@ class copy : public QObject { m_whitelist = whitelist; return *this; } + copy& overwrite(const bool overwrite) + { + m_overwrite = overwrite; + return *this; + } bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } @@ -136,6 +141,7 @@ class copy : public QObject { bool m_followSymlinks = true; const IPathMatcher* m_matcher = nullptr; bool m_whitelist = false; + bool m_overwrite = false; QDir m_src; QDir m_dst; int m_copied; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index f8819d940..ef7e505b9 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -672,7 +672,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) auto install_path = FS::PathCombine(target.absolutePath(), rel_path); logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); FS::ensureFilePathExists(install_path); - auto result = FS::copy(to_install_file, install_path)(); + auto result = FS::copy(to_install_file, install_path).overwrite(true)(); if (!result) { error = true; logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); @@ -686,11 +686,11 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) if (error) { logUpdate(tr("There were errors installing the update.")); auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); - FS::copy(m_updateLogPath, fail_marker)(); + FS::copy(m_updateLogPath, fail_marker).overwrite(true)(); } else { logUpdate(tr("Update succeed.")); auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); - FS::copy(m_updateLogPath, success_marker)(); + FS::copy(m_updateLogPath, success_marker).overwrite(true)(); } auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::deletePath(update_lock_path); @@ -1078,7 +1078,7 @@ void PrismUpdaterApp::backupAppDir() auto bak_path = FS::PathCombine(backup_dir, rel_path); logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); FS::ensureFilePathExists(bak_path); - auto result = FS::copy(to_bak_file, bak_path)(); + auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); if (!result) { logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); } else { From e38adf6006e36928af92b317a1605eee063d70ed Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:01:11 -0700 Subject: [PATCH 47/72] fix(updater) fixes form first round of testing - reset update time after check Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- buildconfig/BuildConfig.cpp.in | 7 ++- launcher/Application.cpp | 40 +++++++++++----- launcher/Application.h | 3 ++ launcher/ui/MainWindow.cpp | 8 ++-- launcher/updater/PrismExternalUpdater.cpp | 46 +++++++++++++++++-- .../updater/prismupdater/PrismUpdater.cpp | 21 +++++++-- 6 files changed, 95 insertions(+), 30 deletions(-) diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index e8e8a4ef8..b1c9b5bef 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -77,6 +77,8 @@ Config::Config() if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) { UPDATER_ENABLED = true; + } else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) { + UPDATER_ENABLED = true; } GIT_COMMIT = "@Launcher_GIT_COMMIT@"; @@ -97,10 +99,7 @@ Config::Config() if (GIT_REFSPEC.startsWith("refs/heads/")) { VERSION_CHANNEL = GIT_REFSPEC; - VERSION_CHANNEL.remove("refs/heads/"); - if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_PLATFORM.isEmpty()) { - UPDATER_ENABLED = true; - } + VERSION_CHANNEL.remove("refs/heads/"); } else if (!GIT_COMMIT.isEmpty()) { diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1a381a045..c59bea8d6 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -584,6 +584,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled by : " << BuildConfig.compilerID(); + qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; + qDebug() << "Updates Enabeled : " << (updaterEnabled() ? "Yes" : "No"); if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); @@ -856,18 +858,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // initialize the updater - if (BuildConfig.UPDATER_ENABLED) { + if (updaterEnabled()) { qDebug() << "Initializing updater"; #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); -#if defined Q_OS_WIN32 - exe_name.append(".exe"); -#endif - auto updater_binary = QFileInfo(QDir(m_rootPath).absoluteFilePath(exe_name)); - if (updater_binary.isFile()) - m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath)); + m_updater.reset(new PrismExternalUpdater(applicationDirPath(), m_dataPath)); #endif qDebug() << "<> Updater started."; } @@ -1000,6 +996,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1030,6 +1027,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { @@ -1056,10 +1054,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) "for details.") .arg(BuildConfig.printableVersionString()) .arg(update_log_path); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.setDetailedText(FS::read(update_log_path)); - msgBox.exec(); + auto msgBox = new QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); + msgBox->setDefaultButton(QMessageBox::Ok); + msgBox->setDetailedText(FS::read(update_log_path)); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->adjustSize(); + msgBox->open(); FS::deletePath(update_success_marker.absoluteFilePath()); } } @@ -1127,6 +1127,22 @@ bool Application::createSetupWizard() return false; } +bool Application::updaterEnabled() { +#if defined(Q_OS_MAC) + return BuildConfig.UPDATER_ENABLED; +#else + return BuildConfig.UPDATER_ENABLED && QFileInfo(updaterBinaryName()).isFile(); +#endif +} + +QString Application::updaterBinaryName() { + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); +#if defined Q_OS_WIN32 + exe_name.append(".exe"); +#endif + return exe_name; +} + bool Application::event(QEvent* event) { #ifdef Q_OS_MACOS diff --git a/launcher/Application.h b/launcher/Application.h index baf64575d..c87ee2652 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -216,6 +216,9 @@ public: int suitableMaxMem(); + bool updaterEnabled(); + QString updaterBinaryName(); + signals: void updateAllowedChanged(bool status); void globalSettingsAboutToOpen(); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 496738e32..16d696dc8 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -215,7 +215,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->actionDISCORD->setVisible(!BuildConfig.DISCORD_URL.isEmpty()); ui->actionREDDIT->setVisible(!BuildConfig.SUBREDDIT_URL.isEmpty()); - ui->actionCheckUpdate->setVisible(BuildConfig.UPDATER_ENABLED); + ui->actionCheckUpdate->setVisible(APPLICATION->updaterEnabled()); #ifndef Q_OS_MAC ui->actionAddToPATH->setVisible(false); @@ -388,7 +388,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi updateNewsLabel(); } - if (BuildConfig.UPDATER_ENABLED) { + if (APPLICATION->updaterEnabled()) { bool updatesAllowed = APPLICATION->updatesAreAllowed(); updatesAllowedChanged(updatesAllowed); @@ -781,7 +781,7 @@ void MainWindow::repopulateAccountsMenu() void MainWindow::updatesAllowedChanged(bool allowed) { - if(!BuildConfig.UPDATER_ENABLED) + if(!APPLICATION->updaterEnabled()) { return; } @@ -1259,7 +1259,7 @@ void MainWindow::on_actionViewCentralModsFolder_triggered() void MainWindow::checkForUpdates() { - if(BuildConfig.UPDATER_ENABLED) + if(APPLICATION->updaterEnabled()) { APPLICATION->triggerUpdateCheck(); } diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 969897405..7f987204a 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -40,7 +40,7 @@ class PrismExternalUpdater::Private { public: - QDir appDir; + QDir binDir; QDir dataDir; QTimer updateTimer; bool allowBeta; @@ -50,10 +50,10 @@ class PrismExternalUpdater::Private { std::unique_ptr settings; }; -PrismExternalUpdater::PrismExternalUpdater(const QString& appDir, const QString& dataDir) +PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& dataDir) { priv = new PrismExternalUpdater::Private(); - priv->appDir = QDir(appDir); + priv->binDir = QDir(binDir); priv->dataDir = QDir(dataDir); auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); priv->settings = std::make_unique(settings_file, QSettings::Format::IniFormat); @@ -97,20 +97,42 @@ void PrismExternalUpdater::checkForUpdates() if (priv->allowBeta) args.append("--pre-release"); - proc.start(priv->appDir.absoluteFilePath(exe_name), args); + proc.start(priv->binDir.absoluteFilePath(exe_name), args); auto result_start = proc.waitForStarted(5000); if (!result_start) { auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString())); + + msgBox.adjustSize(); + msgBox.exec(); + priv->lastCheck = QDateTime::currentDateTime(); + priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); + priv->settings->sync(); + resetAutoCheckTimer(); + return; } QCoreApplication::processEvents(); auto result_finished = proc.waitForFinished(60000); if (!result_finished) { + proc.kill(); auto err = proc.error(); + auto output = proc.readAll(); qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); + auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString())); + msgBox.setDetailedText(output); + msgBox.adjustSize(); + msgBox.exec(); + priv->lastCheck = QDateTime::currentDateTime(); + priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); + priv->settings->sync(); + resetAutoCheckTimer(); + return; } auto exit_code = proc.exitCode(); @@ -127,6 +149,8 @@ void PrismExternalUpdater::checkForUpdates() { qDebug() << "No update available"; auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version.")); + + msgBox.adjustSize(); msgBox.exec(); } break; @@ -137,6 +161,8 @@ void PrismExternalUpdater::checkForUpdates() auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); msgBox.setDetailedText(std_error); + + msgBox.adjustSize(); msgBox.exec(); } break; @@ -159,11 +185,19 @@ void PrismExternalUpdater::checkForUpdates() // unknown error code { qDebug() << "Updater exited with unknown code" << exit_code; + auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), + tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code)); + auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); + msgBox.setDetailedText(detail_txt); + + msgBox.adjustSize(); + msgBox.exec(); } } priv->lastCheck = QDateTime::currentDateTime(); priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); priv->settings->sync(); + resetAutoCheckTimer(); } bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() @@ -247,6 +281,8 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin if (should_skip) { auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available.")); + + msgBox.adjustSize(); msgBox.exec(); return; } @@ -285,7 +321,7 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) if (priv->allowBeta) args.append("--pre-release"); - auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args); + auto result = proc.startDetached(priv->binDir.absoluteFilePath(exe_name), args); if (!result) { qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); } diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index ef7e505b9..1859d5262 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -350,14 +350,14 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar { // setup logging static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; - static const QString logBase = FS::PathCombine("logs", baseLogFile); + static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); auto moveFile = [](const QString& oldName, const QString& newName) { QFile::remove(newName); QFile::copy(oldName, newName); QFile::remove(oldName); }; - if (FS::ensureFolderPathExists("logs")) { + if (FS::ensureFolderPathExists("logs")) { // enough history to track both launches of the updater during a portable install moveFile(logBase.arg(1), logBase.arg(2)); moveFile(logBase.arg(0), logBase.arg(1)); } @@ -442,6 +442,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC; qDebug() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled by : " << BuildConfig.compilerID(); + qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; if (adjustedBy.size()) { qDebug() << "Data dir before adjustment : " << origCwdPath; qDebug() << "Data dir after adjustment : " << m_dataPath; @@ -521,6 +522,7 @@ void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); msgBox->setIcon(QMessageBox::Critical); + msgBox->adjustSize(); msgBox->exec(); exit(1); } @@ -658,6 +660,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); @@ -766,10 +769,13 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel if (BuildConfig.BUILD_ARTIFACT.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { - if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) + if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) { + qDebug() << "Rejecting" << asset.name << "because it is an AppImage"; continue; - else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) + } else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) { + qDebug() << "Rejecting" << asset.name << "because it is not an AppImage"; continue; + } auto asset_name = asset.name.toLower(); auto platform = BuildConfig.BUILD_ARTIFACT.toLower(); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); @@ -786,6 +792,8 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel for_platform = false; if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { + qDebug() << "Rejecting" << asset.name << "|" + << "For Platform:" << (for_platform ? "Yes" : "No") << "For Portable:" << (for_portable ? "Yes" : "No"); valid.append(asset); } } @@ -849,6 +857,7 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) auto download = Net::Download::makeFile(file_url, out_file_path); download->setNetwork(m_network); auto progress_dialog = ProgressDialog(); + progress_dialog.adjustSize(); if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); @@ -954,6 +963,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) msgBox.setInformativeText(infoMsg); msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); + msgBox.adjustSize(); switch (msgBox.exec()) { case QMessageBox::AcceptRole: break; @@ -971,7 +981,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), - m_dataPath); + m_dataPath); logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); unpackAndInstall(file); } else { @@ -1065,6 +1075,7 @@ void PrismUpdaterApp::backupAppDir() QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); int i = 0; From a6c8a37a5deca0fcdb14f53a5f91aa83c3d558b5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:06:14 -0700 Subject: [PATCH 48/72] fixa(updater): better update timer logs Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 7f987204a..d51ad690e 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -60,6 +60,7 @@ PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& priv->allowBeta = priv->settings->value("allow_beta", false).toBool(); priv->autoCheck = priv->settings->value("auto_check", false).toBool(); bool interval_ok; + // default once per day priv->updateInterval = priv->settings->value("update_interval", 86400).toInt(&interval_ok); if (!interval_ok) priv->updateInterval = 86400; @@ -240,9 +241,9 @@ void PrismExternalUpdater::setBetaAllowed(bool allowed) void PrismExternalUpdater::resetAutoCheckTimer() { - int timeoutDuration = 0; - auto now = QDateTime::currentDateTime(); if (priv->autoCheck) { + int timeoutDuration = 0; + auto now = QDateTime::currentDateTime(); if (priv->lastCheck.isValid()) { auto diff = priv->lastCheck.secsTo(now); auto secs_left = priv->updateInterval - diff; @@ -270,6 +271,7 @@ void PrismExternalUpdater::disconnectTimer() void PrismExternalUpdater::autoCheckTimerFired() { + qDebug() << "Auto update Timer fired"; checkForUpdates(); } From 494483fd2b8b3ecc8c7ad19d493a8f7552cc95e2 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 29 Jun 2023 19:09:52 -0300 Subject: [PATCH 49/72] fix(updater): add parent to dialog to fix issues in tilling WMs QProgressDialog without parents in Qt tend to cause graphical issues on some systems, so we add the main window as a parent here! Signed-off-by: flow --- launcher/Application.cpp | 38 +++++++++++------------ launcher/updater/PrismExternalUpdater.cpp | 7 +++-- launcher/updater/PrismExternalUpdater.h | 2 +- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c59bea8d6..8007f26a9 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -857,17 +857,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "<> Translations loaded."; } - // initialize the updater - if (updaterEnabled()) { - qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC - m_updater.reset(new MacSparkleUpdater()); -#else - m_updater.reset(new PrismExternalUpdater(applicationDirPath(), m_dataPath)); -#endif - qDebug() << "<> Updater started."; - } - // Instance icons { auto setting = APPLICATION->settings()->getSetting("IconsDir"); @@ -968,6 +957,25 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) applyCurrentlySelectedTheme(true); + updateCapabilities(); + + if (createSetupWizard()) { + return; + } + + performMainStartupAction(); + + // initialize the updater + if (updaterEnabled()) { + qDebug() << "Initializing updater"; +#ifdef Q_OS_MAC + m_updater.reset(new MacSparkleUpdater()); +#else + m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); +#endif + qDebug() << "<> Updater started."; + } + // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); @@ -1063,14 +1071,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) FS::deletePath(update_success_marker.absoluteFilePath()); } } - - updateCapabilities(); - - if (createSetupWizard()) { - return; - } - - performMainStartupAction(); } bool Application::createSetupWizard() diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index d51ad690e..792dd2a52 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -48,9 +48,11 @@ class PrismExternalUpdater::Private { double updateInterval; QDateTime lastCheck; std::unique_ptr settings; + + QWidget* parent; }; -PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& dataDir) +PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& binDir, const QString& dataDir) { priv = new PrismExternalUpdater::Private(); priv->binDir = QDir(binDir); @@ -68,6 +70,7 @@ PrismExternalUpdater::PrismExternalUpdater(const QString& binDir, const QString& if (!last_check.isNull() && last_check.isValid()) { priv->lastCheck = QDateTime::fromString(last_check.toString(), Qt::ISODate); } + priv->parent = parent; connectTimer(); resetAutoCheckTimer(); } @@ -83,7 +86,7 @@ PrismExternalUpdater::~PrismExternalUpdater() void PrismExternalUpdater::checkForUpdates() { - QProgressDialog progress(tr("Checking for updates..."), "", 0, 0); + QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent); progress.setCancelButton(nullptr); progress.show(); QCoreApplication::processEvents(); diff --git a/launcher/updater/PrismExternalUpdater.h b/launcher/updater/PrismExternalUpdater.h index f8ed29ccc..bfe94c149 100644 --- a/launcher/updater/PrismExternalUpdater.h +++ b/launcher/updater/PrismExternalUpdater.h @@ -34,7 +34,7 @@ class PrismExternalUpdater : public ExternalUpdater { Q_OBJECT public: - PrismExternalUpdater(const QString& appDir, const QString& dataDir); + PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir); ~PrismExternalUpdater() override; /*! From 41cb8d7ec609b91e191e2c89ef651d2f141140cf Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:24:23 -0700 Subject: [PATCH 50/72] fix: adjust dialog size, add parent to msgboxes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 792dd2a52..c3b3bcfee 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -88,6 +88,7 @@ void PrismExternalUpdater::checkForUpdates() { QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent); progress.setCancelButton(nullptr); + progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); @@ -107,8 +108,9 @@ void PrismExternalUpdater::checkForUpdates() auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), - tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString())); + auto msgBox = + QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.adjustSize(); msgBox.exec(); @@ -127,8 +129,9 @@ void PrismExternalUpdater::checkForUpdates() auto output = proc.readAll(); qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); - auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), - tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString())); + auto msgBox = + QMessageBox(QMessageBox::Information, tr("Update Check Failed"), + tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(output); msgBox.adjustSize(); msgBox.exec(); @@ -152,7 +155,8 @@ void PrismExternalUpdater::checkForUpdates() // no update available { qDebug() << "No update available"; - auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version.")); + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."), + QMessageBox::Ok, priv->parent); msgBox.adjustSize(); msgBox.exec(); @@ -162,8 +166,8 @@ void PrismExternalUpdater::checkForUpdates() // there was an error { qDebug() << "Updater subprocess error" << qPrintable(std_error); - auto msgBox = - QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check.")); + auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), + tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(std_error); msgBox.adjustSize(); @@ -190,7 +194,8 @@ void PrismExternalUpdater::checkForUpdates() { qDebug() << "Updater exited with unknown code" << exit_code; auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), - tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code)); + tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code), + QMessageBox::Ok, priv->parent); auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); msgBox.setDetailedText(detail_txt); @@ -285,7 +290,8 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin priv->settings->endGroup(); if (should_skip) { - auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available.")); + auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available."), + QMessageBox::Ok, priv->parent); msgBox.adjustSize(); msgBox.exec(); From 109ae5bae0f487e8218d3e4c12b610399541205a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:42:36 -0700 Subject: [PATCH 51/72] fix(updater): convert int to string Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index c3b3bcfee..585d8f8bf 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -193,9 +193,10 @@ void PrismExternalUpdater::checkForUpdates() // unknown error code { qDebug() << "Updater exited with unknown code" << exit_code; - auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), - tr("The updater exited with an unknown condition.\nExit Code: %1").arg(exit_code), - QMessageBox::Ok, priv->parent); + auto msgBox = + QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), + tr("The updater exited with an unknown condition.\nExit Code: %1").arg(QString::number(exit_code)), + QMessageBox::Ok, priv->parent); auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); msgBox.setDetailedText(detail_txt); From 603e3e7e2edb53ee2cf7f15846b04c4acd29e9df Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:56:44 -0700 Subject: [PATCH 52/72] fix(updater): set minimum dialog / msgbox sizes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 17 +++++++++++------ launcher/updater/PrismExternalUpdater.cpp | 11 ++++++----- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 8007f26a9..1993a1491 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1004,6 +1004,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { @@ -1015,7 +1016,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) [[fallthrough]]; default: { qDebug() << "Exiting because update lockfile is present"; - QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, []() { exit(1); }, Qt::QueuedConnection); return; } } @@ -1035,6 +1037,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { @@ -1046,7 +1049,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) [[fallthrough]]; default: { qDebug() << "Exiting because update lockfile is present"; - QMetaObject::invokeMethod(this, [](){ exit(1); }, Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, []() { exit(1); }, Qt::QueuedConnection); return; } } @@ -1066,6 +1070,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setDetailedText(FS::read(update_log_path)); msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->open(); FS::deletePath(update_success_marker.absoluteFilePath()); @@ -1127,7 +1132,8 @@ bool Application::createSetupWizard() return false; } -bool Application::updaterEnabled() { +bool Application::updaterEnabled() +{ #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else @@ -1135,7 +1141,8 @@ bool Application::updaterEnabled() { #endif } -QString Application::updaterBinaryName() { +QString Application::updaterBinaryName() +{ auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); @@ -1174,8 +1181,6 @@ void Application::performMainStartupAction() { m_status = Application::Initialized; - - if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if (inst) { diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 585d8f8bf..29d411a1e 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -111,7 +111,7 @@ void PrismExternalUpdater::checkForUpdates() auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); priv->lastCheck = QDateTime::currentDateTime(); @@ -133,6 +133,7 @@ void PrismExternalUpdater::checkForUpdates() QMessageBox(QMessageBox::Information, tr("Update Check Failed"), tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(output); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); priv->lastCheck = QDateTime::currentDateTime(); @@ -157,7 +158,7 @@ void PrismExternalUpdater::checkForUpdates() qDebug() << "No update available"; auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."), QMessageBox::Ok, priv->parent); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } @@ -169,7 +170,7 @@ void PrismExternalUpdater::checkForUpdates() auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(std_error); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } @@ -199,7 +200,7 @@ void PrismExternalUpdater::checkForUpdates() QMessageBox::Ok, priv->parent); auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); msgBox.setDetailedText(detail_txt); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } @@ -293,7 +294,7 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin if (should_skip) { auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available."), QMessageBox::Ok, priv->parent); - + msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); return; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 1859d5262..91cc87ec8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -522,6 +522,7 @@ void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); msgBox->setIcon(QMessageBox::Critical); + msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->exec(); exit(1); @@ -660,6 +661,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); @@ -963,6 +965,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file) msgBox.setInformativeText(infoMsg); msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); + msgBox.setMinimumWidth(460); msgBox.adjustSize(); switch (msgBox.exec()) { case QMessageBox::AcceptRole: @@ -1075,6 +1078,7 @@ void PrismUpdaterApp::backupAppDir() QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); progress.setCancelButton(nullptr); + progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); From 4320830a861d16c8fe0b1593e1ee6d022b2d1404 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:59:14 -0700 Subject: [PATCH 53/72] fix(updater): Explicit conversion to string for QByteArray Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/PrismExternalUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 29d411a1e..02e3a3dec 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -169,7 +169,7 @@ void PrismExternalUpdater::checkForUpdates() qDebug() << "Updater subprocess error" << qPrintable(std_error); auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); - msgBox.setDetailedText(std_error); + msgBox.setDetailedText(QString(std_error)); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); @@ -198,7 +198,7 @@ void PrismExternalUpdater::checkForUpdates() QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), tr("The updater exited with an unknown condition.\nExit Code: %1").arg(QString::number(exit_code)), QMessageBox::Ok, priv->parent); - auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(std_output).arg(std_error); + auto detail_txt = tr("StdOut: %1\nStdErr: %2").arg(QString(std_output)).arg(QString(std_error)); msgBox.setDetailedText(detail_txt); msgBox.setMinimumWidth(460); msgBox.adjustSize(); From 3db83a189c4c52437bbab94f80d8e0d663d21e75 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 22:38:16 -0700 Subject: [PATCH 54/72] fix(updater): include updater in setup.exe Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- program_info/win_install.nsi.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index d3b5c256f..1e4c29ad7 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -350,6 +350,7 @@ Section "@Launcher_DisplayName@" File "@Launcher_APP_BINARY_NAME@.exe" File "@Launcher_APP_BINARY_NAME@_filelink.exe" + File "@Launcher_APP_BINARY_NAME@_updater.exe" File "qt.conf" File "qtlogging.ini" File *.dll @@ -431,6 +432,7 @@ Section "Uninstall" Delete $INSTDIR\@Launcher_APP_BINARY_NAME@.exe Delete $INSTDIR\@Launcher_APP_BINARY_NAME@_filelink.exe + Delete $INSTDIR\@Launcher_APP_BINARY_NAME@_updater.exe Delete $INSTDIR\qt.conf Delete $INSTDIR\*.dll From 8dd3a02747ab49a205903b362bf3519ce79205bd Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 29 Jun 2023 23:49:31 -0700 Subject: [PATCH 55/72] Update launcher/Application.cpp Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 1993a1491..e98cc0081 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -585,7 +585,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) qDebug() << "Compiled for : " << BuildConfig.systemID(); qDebug() << "Compiled by : " << BuildConfig.compilerID(); qDebug() << "Build Artifact : " << BuildConfig.BUILD_ARTIFACT; - qDebug() << "Updates Enabeled : " << (updaterEnabled() ? "Yes" : "No"); + qDebug() << "Updates Enabled : " << (updaterEnabled() ? "Yes" : "No"); if (adjustedBy.size()) { qDebug() << "Work dir before adjustment : " << origcwdPath; qDebug() << "Work dir after adjustment : " << QDir::currentPath(); From 00f75e2d5425edec250134de96f83e0a53cd6500 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:54:29 -0700 Subject: [PATCH 56/72] fix(updater): avoid windows installer detection with "asInvoker" in xml exe.manifest Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 1 + .../updater/prismupdater/updater.exe.manifest | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 launcher/updater/prismupdater/updater.exe.manifest diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 4b0e98f83..da4d66b34 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1285,6 +1285,7 @@ if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER)) ) add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) + target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest) target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) if(DEFINED Launcher_APP_BINARY_NAME) diff --git a/launcher/updater/prismupdater/updater.exe.manifest b/launcher/updater/prismupdater/updater.exe.manifest new file mode 100644 index 000000000..2bce76b77 --- /dev/null +++ b/launcher/updater/prismupdater/updater.exe.manifest @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From a01a48793c6670743452038f9851c533c1734b0a Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 03:00:26 -0700 Subject: [PATCH 57/72] fix(updater): force `asInvoker` for updater on windows - use `__COMPAT_LAYER` env var in windows to bypass Installer Detection and run as a normal user anyway - set up updater directly after main windows is created - check update locks before main window Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 35 ++++++++++--------- launcher/updater/PrismExternalUpdater.cpp | 8 +++++ .../updater/prismupdater/PrismUpdater.cpp | 19 +++++++++- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e98cc0081..c1d78c5da 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -959,23 +959,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) updateCapabilities(); - if (createSetupWizard()) { - return; - } - - performMainStartupAction(); - - // initialize the updater - if (updaterEnabled()) { - qDebug() << "Initializing updater"; -#ifdef Q_OS_MAC - m_updater.reset(new MacSparkleUpdater()); -#else - m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); -#endif - qDebug() << "<> Updater started."; - } - // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); @@ -1076,6 +1059,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) FS::deletePath(update_success_marker.absoluteFilePath()); } } + + if (createSetupWizard()) { + return; + } + + performMainStartupAction(); } bool Application::createSetupWizard() @@ -1219,6 +1208,18 @@ void Application::performMainStartupAction() showMainWindow(false); qDebug() << "<> Main window shown."; } + + // initialize the updater + if (updaterEnabled()) { + qDebug() << "Initializing updater"; +#ifdef Q_OS_MAC + m_updater.reset(new MacSparkleUpdater()); +#else + m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); +#endif + qDebug() << "<> Updater started."; + } + if (!m_zipsToImport.isEmpty()) { qDebug() << "<> Importing from zip:" << m_zipsToImport; m_mainWindow->processURLs(m_zipsToImport); diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 02e3a3dec..8de2a8119 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -96,6 +96,10 @@ void PrismExternalUpdater::checkForUpdates() auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; @@ -328,6 +332,10 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 91cc87ec8..771c6f754 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -704,7 +704,12 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) auto app_exe_name = BuildConfig.LAUNCHER_APP_BINARY_NAME; #if defined Q_OS_WIN32 app_exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif + auto app_exe_path = target.absoluteFilePath(app_exe_name); proc.startDetached(app_exe_path); @@ -990,6 +995,11 @@ void PrismUpdaterApp::performInstall(QFileInfo file) } else { logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); QProcess proc = QProcess(); +#if defined Q_OS_WIN + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); +#endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); logUpdate(tr("Process start result: %1").arg(result ? tr("yes") : tr("no"))); @@ -1005,13 +1015,20 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, applicationDirPath().toUtf8()); + + QProcess proc = QProcess(); + auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); + + auto env = QProcessEnvironment::systemEnvironment(); + env.insert("__COMPAT_LAYER", "RUNASINVOKER"); + proc.setProcessEnvironment(env); #endif + auto new_updater_path = loc.value().absoluteFilePath(exe_name); logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); - QProcess proc = QProcess(); if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); return exit(10); From 6476023cf7041340c4e05b020e5c08065578b2d1 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 10:57:53 -0700 Subject: [PATCH 58/72] fix(updater): check the app root dir for binary Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index c1d78c5da..69cd34abc 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1126,7 +1126,7 @@ bool Application::updaterEnabled() #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else - return BuildConfig.UPDATER_ENABLED && QFileInfo(updaterBinaryName()).isFile(); + return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(applicationDirPath(), updaterBinaryName())).isFile(); #endif } From cd527c44a431e337b58e6888225842b9eb99ff12 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 18:17:06 -0700 Subject: [PATCH 59/72] fix(updater): build atrifact fix on linux ci + add qt-ver to artifact name Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .github/workflows/build.yml | 6 ++-- launcher/StringUtils.cpp | 20 ++++++----- .../updater/prismupdater/PrismUpdater.cpp | 33 ++++++++++++++----- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a1554fc6..de908d335 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -283,12 +283,12 @@ jobs: if: runner.os == 'Windows' && matrix.msystem != '' shell: msys2 {0} run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja - name: Configure CMake (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} # https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix) if ("${{ env.CCACHE_VAR }}") { @@ -303,7 +303,7 @@ jobs: - name: Configure CMake (Linux) if: runner.os == 'Linux' run: | - cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=${{ matrix.name }} -G Ninja + cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja ## # BUILD diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index b54299721..72ccdfbff 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -184,27 +184,31 @@ QString StringUtils::getRandomAlphaNumeric() return QUuid::createUuid().toString(QUuid::Id128); } -QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) { +QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) +{ QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); - right = s.mid(index + 1); + right = s.mid(index + sep.length()); return qMakePair(left, right); } -QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) { +QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) +{ QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); - right = s.mid(index + 1); + right = s.mid(left.length() + 1); return qMakePair(left, right); } -QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) { +QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) +{ QString left, right; - auto index = s.indexOf(re); + QRegularExpressionMatch match; + auto index = s.indexOf(re, 0, &match); left = s.mid(0, index); - right = s.mid(index + 1); + auto end = match.hasMatch() ? left.length() + match.capturedLength() : left.length() + 1; + right = s.mid(end); return qMakePair(left, right); } - diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 771c6f754..3683286d8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -784,23 +784,41 @@ QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRel continue; } auto asset_name = asset.name.toLower(); - auto platform = BuildConfig.BUILD_ARTIFACT.toLower(); + auto [platform, platform_qt_ver] = StringUtils::splitFirst(BuildConfig.BUILD_ARTIFACT.toLower(), "-qt"); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); auto asset_is_arm = asset_name.contains("arm64"); auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); bool for_platform = !platform.isEmpty() && asset_name.contains(platform); + if (!for_platform) { + qDebug() << "Rejecting" << asset.name << "because platforms do not match"; + } bool for_portable = asset_name.contains("portable"); - if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) + if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) { + qDebug() << "Rejecting" << asset.name << "because platforms do not match"; for_platform = false; - if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) + } + if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) { + qDebug() << "Rejecting" << asset.name << "because architecture does not match"; for_platform = false; - if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) + } + if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) { + qDebug() << "Rejecting" << asset.name << "because it is not an installer"; for_platform = false; + } + + auto qt_pattern = QRegularExpression("-qt(\\d+)"); + auto qt_match = qt_pattern.match(asset_name); + if (for_platform && qt_match.hasMatch()) { + if (platform_qt_ver.isEmpty() || platform_qt_ver.toInt() != qt_match.captured(1).toInt()) { + qDebug() << "Rejecting" << asset.name << "because it is not for the correct qt version" << platform_qt_ver.toInt() << "vs" + << qt_match.captured(1).toInt(); + for_platform = false; + } + } if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { - qDebug() << "Rejecting" << asset.name << "|" - << "For Platform:" << (for_platform ? "Yes" : "No") << "For Portable:" << (for_portable ? "Yes" : "No"); + qDebug() << "Accepting" << asset.name; valid.append(asset); } } @@ -866,8 +884,7 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) auto progress_dialog = ProgressDialog(); progress_dialog.adjustSize(); - if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected) - showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString())); + progress_dialog.execWithTask(download.get()); qDebug() << "download complete"; From 1fd90e9dd03aa087fd7b8fa2178018812466d80e Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:24:30 -0700 Subject: [PATCH 60/72] fix(updater): ensure updater knows binaries are in `./bin/` on linux and can find them Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 6 ++- launcher/updater/PrismExternalUpdater.cpp | 12 +++-- .../updater/prismupdater/PrismUpdater.cpp | 48 +++++++++++-------- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 69cd34abc..7eec45bfd 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1126,7 +1126,7 @@ bool Application::updaterEnabled() #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else - return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(applicationDirPath(), updaterBinaryName())).isFile(); + return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(m_rootPath, updaterBinaryName())).isFile(); #endif } @@ -1135,6 +1135,8 @@ QString Application::updaterBinaryName() auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); +#else + exe_name.prepend("bin/"); #endif return exe_name; } @@ -1215,7 +1217,7 @@ void Application::performMainStartupAction() #ifdef Q_OS_MAC m_updater.reset(new MacSparkleUpdater()); #else - m_updater.reset(new PrismExternalUpdater(m_mainWindow, applicationDirPath(), m_dataPath)); + m_updater.reset(new PrismExternalUpdater(m_mainWindow, m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 8de2a8119..3595042d6 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -40,7 +40,7 @@ class PrismExternalUpdater::Private { public: - QDir binDir; + QDir appDir; QDir dataDir; QTimer updateTimer; bool allowBeta; @@ -52,10 +52,10 @@ class PrismExternalUpdater::Private { QWidget* parent; }; -PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& binDir, const QString& dataDir) +PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir) { priv = new PrismExternalUpdater::Private(); - priv->binDir = QDir(binDir); + priv->appDir = QDir(appDir); priv->dataDir = QDir(dataDir); auto settings_file = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); priv->settings = std::make_unique(settings_file, QSettings::Format::IniFormat); @@ -100,13 +100,15 @@ void PrismExternalUpdater::checkForUpdates() auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name = QString("bin/%1").arg(exe_name); #endif QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; if (priv->allowBeta) args.append("--pre-release"); - proc.start(priv->binDir.absoluteFilePath(exe_name), args); + proc.start(priv->appDir.absoluteFilePath(exe_name), args); auto result_start = proc.waitForStarted(5000); if (!result_start) { auto err = proc.error(); @@ -342,7 +344,7 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) if (priv->allowBeta) args.append("--pre-release"); - auto result = proc.startDetached(priv->binDir.absoluteFilePath(exe_name), args); + auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args); if (!result) { qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); } diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 3683286d8..8af65d3c6 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -258,9 +258,9 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_isFlatpak = DesktopServices::isFlatpak(); - QString prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME; + QString prism_executable = FS::PathCombine(applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 - prism_executable += ".exe"; + prism_executable.append(".exe"); #endif if (!QFileInfo(prism_executable).isFile()) { @@ -349,6 +349,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_updateLogPath = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); { // setup logging + FS::ensureFolderPathExists(FS::PathCombine(m_dataPath, "logs")); static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); auto moveFile = [](const QString& oldName, const QString& newName) { @@ -464,13 +465,13 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar m_network->setProxy(proxy); } - auto marker_file_path = QDir(applicationDirPath()).absoluteFilePath(".prism_launcher_updater_unpack.marker"); + auto marker_file_path = QDir(m_rootPath).absoluteFilePath(".prism_launcher_updater_unpack.marker"); auto marker_file = QFileInfo(marker_file_path); if (marker_file.exists()) { auto target_dir = QString(FS::read(marker_file_path)).trimmed(); if (target_dir.isEmpty()) { qWarning() << "Empty updater marker file contains no install target. making best guess of parent dir"; - target_dir = QDir(applicationDirPath()).absoluteFilePath(".."); + target_dir = QDir(m_rootPath).absoluteFilePath(".."); } QMetaObject::invokeMethod( @@ -629,10 +630,10 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) logUpdate("Waiting 2 seconds for resources to free"); this->thread()->sleep(2); - auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); + auto manifest_path = FS::PathCombine(m_rootPath, "manifest.txt"); QFileInfo manifest(manifest_path); - auto app_dir = QDir(applicationDirPath()); + auto app_dir = QDir(m_rootPath); QStringList file_list; if (manifest.isFile()) { @@ -649,7 +650,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) } if (file_list.isEmpty()) { - logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(applicationDirPath())); + logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(m_rootPath)); auto entries = target.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (auto entry : entries) { file_list.append(entry.fileName()); @@ -659,7 +660,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) bool error = false; - QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); @@ -668,7 +669,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) int i = 0; for (auto glob : file_list) { - QDirIterator iter(applicationDirPath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + QDirIterator iter(m_rootPath, QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); while (iter.hasNext()) { @@ -708,6 +709,8 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name.prepend("bin/"); #endif auto app_exe_path = target.absoluteFilePath(app_exe_name); @@ -894,10 +897,15 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) bool PrismUpdaterApp::callAppImageUpdate() { + auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); + qDebug() << "Calling: AppImageUpdate" << appimage_path; proc.setProgram("AppImageUpdate"); - proc.setArguments({ QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")) }); - return proc.startDetached(); + proc.setArguments({ appimage_path }); + auto result = proc.startDetached(); + if (!result) + qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString(); + return result; } void PrismUpdaterApp::clearUpdateLog() @@ -1005,9 +1013,8 @@ void PrismUpdaterApp::performInstall(QFileInfo file) logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.suffix().toLower() == "zip") { - write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, applicationDirPath(), - m_dataPath); - logUpdate(tr("Updating portable install at %1").arg(applicationDirPath())); + write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, m_rootPath, m_dataPath); + logUpdate(tr("Updating portable install at %1").arg(m_rootPath)); unpackAndInstall(file); } else { logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); @@ -1016,6 +1023,8 @@ void PrismUpdaterApp::performInstall(QFileInfo file) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name.prepend("bin/"); #endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); @@ -1031,7 +1040,7 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); - FS::write(marker_file_path, applicationDirPath().toUtf8()); + FS::write(marker_file_path, m_rootPath.toUtf8()); QProcess proc = QProcess(); @@ -1042,6 +1051,8 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name.prepend("bin/"); #endif auto new_updater_path = loc.value().absoluteFilePath(exe_name); @@ -1057,7 +1068,7 @@ void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) void PrismUpdaterApp::backupAppDir() { - auto manifest_path = FS::PathCombine(applicationDirPath(), "manifest.txt"); + auto manifest_path = FS::PathCombine(m_rootPath, "manifest.txt"); QFileInfo manifest(manifest_path); QStringList file_list; @@ -1099,8 +1110,7 @@ void PrismUpdaterApp::backupAppDir() logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); - - QDir app_dir = QCoreApplication::applicationDirPath(); + auto app_dir = QDir(m_rootPath); auto backup_dir = FS::PathCombine( app_dir.absolutePath(), QStringLiteral("backup_") + @@ -1110,7 +1120,7 @@ void PrismUpdaterApp::backupAppDir() auto backup_marker_path = FS::PathCombine(m_dataPath, ".prism_launcher_update_backup_path.txt"); FS::write(backup_marker_path, backup_dir.toUtf8()); - QProgressDialog progress(tr("Backing up install at %1").arg(applicationDirPath()), "", 0, file_list.length()); + QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); From cea285f5f5b678bed0786ff78586e2e921a77392 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:33:22 -0700 Subject: [PATCH 61/72] fix(updater): fix bad exe_name on linux Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 8af65d3c6..2604d2e0b 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -710,7 +710,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #else - exe_name.prepend("bin/"); + app_exe_name.prepend("bin/"); #endif auto app_exe_path = target.absoluteFilePath(app_exe_name); @@ -1023,8 +1023,6 @@ void PrismUpdaterApp::performInstall(QFileInfo file) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); -#else - exe_name.prepend("bin/"); #endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); From b67844e74c1125bde0111606b06aee5a4686c94b Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Fri, 30 Jun 2023 23:04:55 -0700 Subject: [PATCH 62/72] fix(windows installer): old installers didn't uninstall first. now they do so ensure shortcuts stay selected by default Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- program_info/win_install.nsi.in | 1 - 1 file changed, 1 deletion(-) diff --git a/program_info/win_install.nsi.in b/program_info/win_install.nsi.in index 1e4c29ad7..fa8615e94 100644 --- a/program_info/win_install.nsi.in +++ b/program_info/win_install.nsi.in @@ -470,7 +470,6 @@ Function .onInit ${GetParameters} $R0 ${GetOptions} $R0 "/NoShortcuts" $R1 ${IfNot} ${Errors} -${OrIf} ${FileExists} "$InstDir\@Launcher_APP_BINARY_NAME@.exe" !insertmacro UnselectSection ${SM_SHORTCUTS} !insertmacro UnselectSection ${DESKTOP_SHORTCUTS} ${EndIf} From 5d68a4c992d07cdda847764931b4924572826ad5 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 30 Jul 2023 14:43:04 -0700 Subject: [PATCH 63/72] fix: religate export zip to launcher app only Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> fix(MMCZip): include QUrl Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> fix(FileLink): drop FreeConsole Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> fix(filelink exe): add console sources Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/CMakeLists.txt | 4 +++- launcher/MMCZip.cpp | 9 ++++++++- launcher/MMCZip.h | 3 +++ launcher/filelink/FileLink.cpp | 5 ++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 49deda07a..c639d10d2 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -577,6 +577,9 @@ set(ATLAUNCHER_SOURCES ) set(LINKEXE_SOURCES + WindowsConsole.cpp + WindowsConsole.h + filelink/FileLink.h filelink/FileLink.cpp FileSystem.h @@ -1206,7 +1209,6 @@ endif() # Add executable add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) -target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) target_link_libraries(Launcher_logic diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index ad47c1d03..40790a824 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -42,7 +42,11 @@ #include #include +#include + +#if defined(LAUNCHER_APPLICATION) #include +#endif namespace MMCZip { // ours @@ -424,6 +428,7 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q return true; } +#if defined(LAUNCHER_APPLICATION) void ExportToZipTask::executeTask() { setStatus("Adding files..."); @@ -502,5 +507,7 @@ bool ExportToZipTask::abort() } return false; } +#endif + +} // namespace MMCZip -} // namespace MMCZip \ No newline at end of file diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 163f71671..8afb12410 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -151,6 +151,8 @@ bool extractFile(QString fileCompressed, QString file, QString dir); */ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); + +#if defined(LAUNCHER_APPLICATION) class ExportToZipTask : public Task { public: ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) @@ -193,4 +195,5 @@ class ExportToZipTask : public Task { QFuture m_build_zip_future; QFutureWatcher m_build_zip_watcher; }; +#endif } // namespace MMCZip diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 6e869bad4..35d745dbd 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -269,7 +269,7 @@ void FileLinkApp::runLink() FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; m_path_results.append(result); } else { - FS::LinkResult result = { src_path, dst_path }; + FS::LinkResult result = { src_path, dst_path, "", 0}; m_path_results.append(result); } } @@ -329,7 +329,7 @@ void FileLinkApp::readPathPairs() in >> numLinks; qDebug() << "numLinks" << numLinks; - for (int i = 0; i < numLinks; i++) { + for (unsigned int i = 0; i < numLinks; i++) { FS::LinkPair pair; in >> pair.src; in >> pair.dst; @@ -352,7 +352,6 @@ FileLinkApp::~FileLinkApp() fclose(stdout); fclose(stdin); fclose(stderr); - FreeConsole(); } #endif } From 1e947ca8931f47e78e3b4fbf64e1b03ce749332c Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 30 Jul 2023 15:42:15 -0700 Subject: [PATCH 64/72] fix(flame creation task): import ApiDownload Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- .../modplatform/flame/FlameInstanceCreationTask.cpp | 10 ++++++---- launcher/modplatform/flame/FlameInstanceCreationTask.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index c2c7080f0..61f8d4f51 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -62,6 +62,8 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" +#include "net/ApiDownload.h" + static const FlameAPI api; bool FlameCreationTask::abort() @@ -542,7 +544,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) m_mod_id_resolver.reset(); connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { m_files_job.reset(); - validateZIPResouces(); + validateZIPResources(); }); connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { m_files_job.reset(); @@ -591,7 +593,7 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) setAbortable(true); } -void FlameCreationTask::validateZIPResouces() +void FlameCreationTask::validateZIPResources() { qDebug() << "Validating whether resources stored as .zip are in the right place"; for (auto [fileName, targetFolder] : m_ZIP_resources) { @@ -644,8 +646,8 @@ void FlameCreationTask::validateZIPResouces() validatePath(fileName, targetFolder, "datapacks"); break; case PackedResourceType::ShaderPack: - // in theroy flame API can't do this but who knows, that *may* change ? - // better to handle it if it *does* occure in the future + // in theory flame API can't do this but who knows, that *may* change ? + // better to handle it if it *does* occur in the future validatePath(fileName, targetFolder, "shaderpacks"); break; case PackedResourceType::WorldSave: diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 603d3693e..02ad48f2e 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask { void idResolverSucceeded(QEventLoop&); void setupDownloadJob(QEventLoop&); void copyBlockedMods(QList const& blocked_mods); - void validateZIPResouces(); + void validateZIPResources(); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); private: From 14e16db01ee8827c485e111b7d5ff852b0a476aa Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sun, 30 Jul 2023 16:02:37 -0700 Subject: [PATCH 65/72] refactor(filelink): drop moved funciton Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/filelink/FileLink.cpp | 96 ---------------------------------- 1 file changed, 96 deletions(-) diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp index 35d745dbd..c3f9bb852 100644 --- a/launcher/filelink/FileLink.cpp +++ b/launcher/filelink/FileLink.cpp @@ -59,102 +59,6 @@ namespace fs = std::filesystem; namespace fs = ghc::filesystem; #endif -#if defined Q_OS_WIN32 - -// taken from https://stackoverflow.com/a/25927081 -// getting a proper output to console with redirection support on windows is apparently hell -void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) -{ - // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been - // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 - // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our - // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value - // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to - // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target - // using the "_dup2" function. - if (bindStdIn) { - FILE* dummyFile; - freopen_s(&dummyFile, "nul", "r", stdin); - } - if (bindStdOut) { - FILE* dummyFile; - freopen_s(&dummyFile, "nul", "w", stdout); - } - if (bindStdErr) { - FILE* dummyFile; - freopen_s(&dummyFile, "nul", "w", stderr); - } - - // Redirect unbuffered stdin from the current standard input handle - if (bindStdIn) { - HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - if (fileDescriptor != -1) { - FILE* file = _fdopen(fileDescriptor, "r"); - if (file != NULL) { - int dup2Result = _dup2(_fileno(file), _fileno(stdin)); - if (dup2Result == 0) { - setvbuf(stdin, NULL, _IONBF, 0); - } - } - } - } - } - - // Redirect unbuffered stdout to the current standard output handle - if (bindStdOut) { - HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - if (fileDescriptor != -1) { - FILE* file = _fdopen(fileDescriptor, "w"); - if (file != NULL) { - int dup2Result = _dup2(_fileno(file), _fileno(stdout)); - if (dup2Result == 0) { - setvbuf(stdout, NULL, _IONBF, 0); - } - } - } - } - } - - // Redirect unbuffered stderr to the current standard error handle - if (bindStdErr) { - HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - if (fileDescriptor != -1) { - FILE* file = _fdopen(fileDescriptor, "w"); - if (file != NULL) { - int dup2Result = _dup2(_fileno(file), _fileno(stderr)); - if (dup2Result == 0) { - setvbuf(stderr, NULL, _IONBF, 0); - } - } - } - } - } - - // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the - // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In - // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything - // has been read from or written to the targets or not. - if (bindStdIn) { - std::wcin.clear(); - std::cin.clear(); - } - if (bindStdOut) { - std::wcout.clear(); - std::cout.clear(); - } - if (bindStdErr) { - std::wcerr.clear(); - std::cerr.clear(); - } -} -#endif - FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this)) { #if defined Q_OS_WIN32 From b8d9c3d7795692f2d012421a28232c3eb1a73301 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 26 Sep 2023 20:19:35 +0300 Subject: [PATCH 66/72] format Signed-off-by: Trial97 --- launcher/Application.cpp | 2 +- launcher/FileSystem.h | 2 -- launcher/MMCZip.cpp | 1 - launcher/MMCZip.h | 1 - launcher/StringUtils.h | 2 +- launcher/filelink/FileLink.h | 11 ++--------- launcher/filelink/filelink_main.cpp | 5 ++--- launcher/modplatform/modrinth/ModrinthAPI.cpp | 1 - launcher/ui/MainWindow.cpp | 6 ++---- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 6 +++--- launcher/ui/dialogs/UpdateAvailableDialog.h | 3 +-- launcher/updater/prismupdater/UpdaterDialogs.h | 2 +- 12 files changed, 13 insertions(+), 29 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 955ec692f..ba60b1e04 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -875,7 +875,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) updateCapabilities(); detectLibraries(); - + // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index c474334c3..861cfa267 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -61,7 +61,6 @@ class FileSystemException : public ::Exception { */ void write(const QString& filename, const QByteArray& data); - /** * append data to a file safely */ @@ -72,7 +71,6 @@ void appendSafe(const QString& filename, const QByteArray& data); */ void append(const QString& filename, const QByteArray& data); - /** * read data from a file safely\ */ diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 40790a824..3bfe16ab5 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -510,4 +510,3 @@ bool ExportToZipTask::abort() #endif } // namespace MMCZip - diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index 8afb12410..1c6ebe34a 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -151,7 +151,6 @@ bool extractFile(QString fileCompressed, QString file, QString dir); */ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter); - #if defined(LAUNCHER_APPLICATION) class ExportToZipTask : public Task { public: diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 9244d3461..9d2bdd85e 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -36,8 +36,8 @@ #pragma once -#include #include +#include #include #include diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h index ab3e9d360..583d0d43a 100644 --- a/launcher/filelink/FileLink.h +++ b/launcher/filelink/FileLink.h @@ -41,17 +41,10 @@ class FileLinkApp : public QCoreApplication { // friends for the purpose of limiting access to deprecated stuff Q_OBJECT public: - enum Status { - Starting, - Failed, - Succeeded, - Initialized - }; + enum Status { Starting, Failed, Succeeded, Initialized }; FileLinkApp(int& argc, char** argv); virtual ~FileLinkApp(); - Status status() const { - return m_status; - } + Status status() const { return m_status; } private: void joinServer(QString server); diff --git a/launcher/filelink/filelink_main.cpp b/launcher/filelink/filelink_main.cpp index a656a9c96..2a8bcb703 100644 --- a/launcher/filelink/filelink_main.cpp +++ b/launcher/filelink/filelink_main.cpp @@ -26,10 +26,9 @@ int main(int argc, char* argv[]) { FileLinkApp ldh(argc, argv); - switch(ldh.status()) { + switch (ldh.status()) { case FileLinkApp::Starting: - case FileLinkApp::Initialized: - { + case FileLinkApp::Initialized: { return ldh.exec(); } case FileLinkApp::Failed: diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index 355c759cd..f453f5cb9 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -10,7 +10,6 @@ #include "net/ApiUpload.h" #include "net/NetJob.h" #include "net/Upload.h" -#include "net/ApiDownload.h" Task::Ptr ModrinthAPI::currentVersion(QString hash, QString hash_format, std::shared_ptr response) { diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 3a5ce9c47..326b22043 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -676,8 +676,7 @@ void MainWindow::repopulateAccountsMenu() void MainWindow::updatesAllowedChanged(bool allowed) { - if(!APPLICATION->updaterEnabled()) - { + if (!APPLICATION->updaterEnabled()) { return; } ui->actionCheckUpdate->setEnabled(allowed); @@ -1209,8 +1208,7 @@ void MainWindow::refreshInstances() void MainWindow::checkForUpdates() { - if(APPLICATION->updaterEnabled()) - { + if (APPLICATION->updaterEnabled()) { APPLICATION->triggerUpdateCheck(); } else { qWarning() << "Updater not set up. Cannot check for updates."; diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 28f7167cb..5eebe87a3 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -46,17 +46,17 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, ui->releaseNotes->setHtml(releaseNotesHtml); ui->releaseNotes->setOpenExternalLinks(true); - connect(ui->skipButton, &QPushButton::clicked, this, [this](){ + connect(ui->skipButton, &QPushButton::clicked, this, [this]() { setResult(ResultCode::Skip); done(ResultCode::Skip); }); - connect(ui->delayButton, &QPushButton::clicked, this, [this](){ + connect(ui->delayButton, &QPushButton::clicked, this, [this]() { setResult(ResultCode::DontInstall); done(ResultCode::DontInstall); }); - connect(ui->installButton, &QPushButton::clicked, this, [this](){ + connect(ui->installButton, &QPushButton::clicked, this, [this]() { setResult(ResultCode::Install); done(ResultCode::Install); }); diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.h b/launcher/ui/dialogs/UpdateAvailableDialog.h index f3ea5cbb1..6af9ace36 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.h +++ b/launcher/ui/dialogs/UpdateAvailableDialog.h @@ -31,13 +31,12 @@ class UpdateAvailableDialog : public QDialog { Q_OBJECT public: - enum ResultCode { Install = 10, DontInstall = 11, Skip = 12, }; - + explicit UpdateAvailableDialog(const QString& currentVersion, const QString& availableVersion, const QString& releaseNotes, diff --git a/launcher/updater/prismupdater/UpdaterDialogs.h b/launcher/updater/prismupdater/UpdaterDialogs.h index 249ca1b5d..e336c0e2c 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.h +++ b/launcher/updater/prismupdater/UpdaterDialogs.h @@ -25,8 +25,8 @@ #include #include -#include "Version.h" #include "GitHubRelease.h" +#include "Version.h" namespace Ui { class SelectReleaseDialog; From 498c9db1cedc9b20d631d6e3cb38b4145d660344 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 15:14:59 +0300 Subject: [PATCH 67/72] fixed appImageUpdate Signed-off-by: Trial97 --- launcher/updater/PrismExternalUpdater.cpp | 2 ++ launcher/updater/prismupdater/PrismUpdater.cpp | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index 3595042d6..bee72e3a0 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -338,6 +338,8 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag) auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); +#else + exe_name = QString("bin/%1").arg(exe_name); #endif QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", version_tag }; diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 2604d2e0b..4ffe29b35 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -900,7 +900,7 @@ bool PrismUpdaterApp::callAppImageUpdate() auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); qDebug() << "Calling: AppImageUpdate" << appimage_path; - proc.setProgram("AppImageUpdate"); + proc.setProgram("bin/AppImageUpdate"); proc.setArguments({ appimage_path }); auto result = proc.startDetached(); if (!result) @@ -1213,8 +1213,14 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) } auto out = proc.readAllStandardOutput(); auto lines = out.split('\n'); + lines.removeAll(""); if (lines.length() < 2) return false; + else if (lines.length() > 2) { + auto line1 = lines.takeLast(); + auto line2 = lines.takeLast(); + lines = { line2, line1 }; + } auto first = lines.takeFirst(); auto first_parts = first.split(' '); if (first_parts.length() < 2) @@ -1230,6 +1236,8 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) m_prsimVersionChannel = "stable"; } auto version_parts = version.split('.'); + if (version_parts.length() < 2) + return false; m_prismVersionMajor = version_parts.takeFirst().toInt(); m_prismVersionMinor = version_parts.takeFirst().toInt(); m_prismGitCommit = lines.takeFirst().simplified(); From 606c12ffeb871e711b15025dedf93634dc4989be Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 18:47:21 +0300 Subject: [PATCH 68/72] more fixing Signed-off-by: Trial97 --- .github/workflows/build.yml | 74 ++++++++----------- .github/workflows/trigger_builds.yml | 29 ++++---- .github/workflows/trigger_release.yml | 9 ++- .../updater/prismupdater/PrismUpdater.cpp | 2 +- 4 files changed, 53 insertions(+), 61 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b703977b..e1641ed50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,6 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-20.04 qt_ver: 5 @@ -127,9 +126,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'true' + submodules: "true" - - name: 'Setup MSYS2' + - name: "Setup MSYS2" if: runner.os == 'Windows' && matrix.msystem != '' uses: msys2/setup-msys2@v2 with: @@ -169,7 +168,7 @@ jobs: path: '${{ github.workspace }}\.ccache' key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }} restore-keys: | - ${{ matrix.os }}-mingw-w64-ccache + ${{ matrix.os }}-mingw-w64-ccache - name: Setup ccache (Windows MinGW-w64) if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug' @@ -214,35 +213,35 @@ jobs: if: runner.os == 'Windows' && matrix.architecture == 'arm64' uses: jurplel/install-qt-action@v3 with: - aqtversion: '==3.1.*' - py7zrversion: '>=0.20.2' - version: ${{ matrix.qt_version }} - host: 'windows' - target: 'desktop' - arch: '' - modules: ${{ matrix.qt_modules }} - tools: ${{ matrix.qt_tools }} - cache: ${{ inputs.is_qt_cached }} - cache-key-prefix: host-qt-arm64-windows - dir: ${{ github.workspace }}\HostQt - set-env: false + aqtversion: "==3.1.*" + py7zrversion: ">=0.20.2" + version: ${{ matrix.qt_version }} + host: "windows" + target: "desktop" + arch: "" + modules: ${{ matrix.qt_modules }} + tools: ${{ matrix.qt_tools }} + cache: ${{ inputs.is_qt_cached }} + cache-key-prefix: host-qt-arm64-windows + dir: ${{ github.workspace }}\HostQt + set-env: false - name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC) if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '') uses: jurplel/install-qt-action@v3 with: - aqtversion: '==3.1.*' - py7zrversion: '>=0.20.2' - version: ${{ matrix.qt_version }} - host: ${{ matrix.qt_host }} - target: 'desktop' - arch: ${{ matrix.qt_arch }} - modules: ${{ matrix.qt_modules }} - tools: ${{ matrix.qt_tools }} - cache: ${{ inputs.is_qt_cached }} + aqtversion: "==3.1.*" + py7zrversion: ">=0.20.2" + version: ${{ matrix.qt_version }} + host: ${{ matrix.qt_host }} + target: "desktop" + arch: ${{ matrix.qt_arch }} + modules: ${{ matrix.qt_modules }} + tools: ${{ matrix.qt_tools }} + cache: ${{ inputs.is_qt_cached }} - name: Install MSVC (Windows MSVC) - if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool + if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool uses: ilammy/msvc-dev-cmd@v1 with: vsversion: 2022 @@ -343,7 +342,7 @@ jobs: - name: Test (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64' run: | - ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} + ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }} ## # PACKAGE BUILDS @@ -385,7 +384,7 @@ jobs: run: | cmake --install ${{ env.BUILD_DIR }} touch ${{ env.INSTALL_DIR }}/manifest.txt - for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt + for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt - name: Package (Windows MSVC) if: runner.os == 'Windows' && matrix.msystem == '' @@ -402,10 +401,9 @@ jobs: Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt - - name: Fetch codesign certificate (Windows) if: runner.os == 'Windows' - shell: bash # yes, we are not using MSYS2 or PowerShell here + shell: bash # yes, we are not using MSYS2 or PowerShell here run: | echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx @@ -507,15 +505,7 @@ jobs: export LD_LIBRARY_PATH chmod +x AppImageUpdate-x86_64.AppImage - ./AppImageUpdate-x86_64.AppImage --appimage-extract - - mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional - mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins - - cp -r squashfs-root/usr/bin/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin - cp -r squashfs-root/usr/lib/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib - cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/optional - cp -r squashfs-root/usr/optional/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins + cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync" @@ -599,7 +589,7 @@ jobs: with: name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage - + - name: Upload AppImage Zsync (Linux) if: runner.os == 'Linux' && matrix.qt_ver != 5 uses: actions/upload-artifact@v3 @@ -623,10 +613,10 @@ jobs: uses: actions/checkout@v4 if: inputs.build_type == 'Debug' with: - submodules: 'true' + submodules: "true" - name: Build Flatpak (Linux) if: inputs.build_type == 'Debug' uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: bundle: "Prism Launcher.flatpak" - manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml + manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 26ee4380b..70fda60ed 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -3,26 +3,25 @@ name: Build Application on: push: branches-ignore: - - 'renovate/**' + - "renovate/**" paths-ignore: - - '**.md' - - '**/LICENSE' - - 'flake.lock' - - 'packages/**' - - '.github/ISSUE_TEMPLATE/**' - - '.markdownlint**' + - "**.md" + - "**/LICENSE" + - "flake.lock" + - "packages/**" + - ".github/ISSUE_TEMPLATE/**" + - ".markdownlint**" pull_request: paths-ignore: - - '**.md' - - '**/LICENSE' - - 'flake.lock' - - 'packages/**' - - '.github/ISSUE_TEMPLATE/**' - - '.markdownlint**' + - "**.md" + - "**/LICENSE" + - "flake.lock" + - "packages/**" + - ".github/ISSUE_TEMPLATE/**" + - ".markdownlint**" workflow_dispatch: jobs: - build_debug: name: Build Debug uses: ./.github/workflows/build.yml @@ -34,3 +33,5 @@ jobs: WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index bda75e354..44bcd539c 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -3,10 +3,9 @@ name: Build Application and Make Release on: push: tags: - - '*' + - "*" jobs: - build_release: name: Build Release uses: ./.github/workflows/build.yml @@ -18,6 +17,8 @@ jobs: WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }} WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }} CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} create_release: needs: build_release @@ -28,8 +29,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'true' - path: 'PrismLauncher-source' + submodules: "true" + path: "PrismLauncher-source" - name: Download artifacts uses: actions/download-artifact@v3 - name: Grab and store version diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 4ffe29b35..761c368f0 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -900,7 +900,7 @@ bool PrismUpdaterApp::callAppImageUpdate() auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); qDebug() << "Calling: AppImageUpdate" << appimage_path; - proc.setProgram("bin/AppImageUpdate"); + proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate-x86_64.AppImage")); proc.setArguments({ appimage_path }); auto result = proc.startDetached(); if (!result) From 742384909f95eb25ab736094c81f4b48f834f176 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 21:24:31 +0300 Subject: [PATCH 69/72] updated portable update Signed-off-by: Trial97 --- .../updater/prismupdater/PrismUpdater.cpp | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 761c368f0..110945f55 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -27,20 +27,20 @@ #include #include -#include -#include -#include - #include +#include +#include +#include #include #include +#include #include #include +#include #include #include -#include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -660,28 +660,42 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target) bool error = false; - QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); + QProgressDialog progress(tr("Installing from %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); + logUpdate(tr("Installing from %1").arg(m_rootPath)); + + auto copy = [this, app_dir, target](QString to_install_file) { + auto rel_path = app_dir.relativeFilePath(to_install_file); + auto install_path = FS::PathCombine(target.absolutePath(), rel_path); + logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); + FS::ensureFilePathExists(install_path); + auto result = FS::copy(to_install_file, install_path).overwrite(true)(); + if (!result) { + logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); + return true; + } + return false; + }; + int i = 0; for (auto glob : file_list) { QDirIterator iter(m_rootPath, QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); - while (iter.hasNext()) { - auto to_install_file = iter.next(); - auto rel_path = app_dir.relativeFilePath(to_install_file); - auto install_path = FS::PathCombine(target.absolutePath(), rel_path); - logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); - FS::ensureFilePathExists(install_path); - auto result = FS::copy(to_install_file, install_path).overwrite(true)(); - if (!result) { - error = true; - logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); + if (!iter.hasNext() && !glob.isEmpty()) { + if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { + error |= copy(file_info.absoluteFilePath()); + } else { + logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); + } + } else { + while (iter.hasNext()) { + error |= copy(iter.next()); } } i++; @@ -1124,23 +1138,37 @@ void PrismUpdaterApp::backupAppDir() progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); + + logUpdate(tr("Backing up install at %1").arg(m_rootPath)); + + auto copy = [this, app_dir, backup_dir](QString to_bak_file) { + auto rel_path = app_dir.relativeFilePath(to_bak_file); + auto bak_path = FS::PathCombine(backup_dir, rel_path); + logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); + FS::ensureFilePathExists(bak_path); + auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); + if (!result) { + logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); + } else { + if (!FS::deletePath(to_bak_file)) + logUpdate(tr("Failed to remove %1").arg(to_bak_file)); + } + }; + int i = 0; for (auto glob : file_list) { QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); - while (iter.hasNext()) { - auto to_bak_file = iter.next(); - auto rel_path = app_dir.relativeFilePath(to_bak_file); - auto bak_path = FS::PathCombine(backup_dir, rel_path); - logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); - FS::ensureFilePathExists(bak_path); - auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); - if (!result) { - logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); + if (!iter.hasNext() && !glob.isEmpty()) { + if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { + copy(file_info.absoluteFilePath()); } else { - if (!FS::deletePath(to_bak_file)) - logUpdate(tr("Failed to remove %1").arg(to_bak_file)); + logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); + } + } else { + while (iter.hasNext()) { + copy(iter.next()); } } i++; From bef701eba847ecb1c4e6b0420315bee1ea045eef Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 28 Sep 2023 21:27:11 +0300 Subject: [PATCH 70/72] chaged folder again Signed-off-by: Trial97 --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 110945f55..1c5aaf1e8 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -1161,10 +1161,10 @@ void PrismUpdaterApp::backupAppDir() progress.setValue(i); QCoreApplication::processEvents(); if (!iter.hasNext() && !glob.isEmpty()) { - if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { + if (auto file_info = QFileInfo(FS::PathCombine(app_dir.absolutePath(), glob)); file_info.exists()) { copy(file_info.absoluteFilePath()); } else { - logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); + logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(app_dir.absolutePath(), glob))); } } else { while (iter.hasNext()) { From 5cb6d931360ff9924164f4bc08050674a93b87aa Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 9 Oct 2023 22:25:45 +0300 Subject: [PATCH 71/72] fixed updater build Signed-off-by: Trial97 --- launcher/net/NetJob.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index b99c5acb0..784d81c37 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -36,11 +36,16 @@ */ #include "NetJob.h" +#if defined(LAUNCHER_APPLICATION) #include "Application.h" +#endif -NetJob::NetJob(QString job_name, shared_qobject_ptr network) - : ConcurrentTask(nullptr, job_name, APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()), m_network(network) -{} +NetJob::NetJob(QString job_name, shared_qobject_ptr network) : ConcurrentTask(nullptr, job_name), m_network(network) +{ +#if defined(LAUNCHER_APPLICATION) + setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); +#endif +} auto NetJob::addNetAction(NetAction::Ptr action) -> bool { From ef0813754be290ed842b5acaa62d2de7fa620291 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 17:56:11 +0300 Subject: [PATCH 72/72] added explicit qt version for linux portable Signed-off-by: Trial97 --- .github/workflows/build.yml | 56 +++++++++++++-------------- .github/workflows/trigger_release.yml | 10 ++--- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c05747d0..ee5662ba6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,58 +43,58 @@ jobs: - os: ubuntu-20.04 qt_ver: 6 qt_host: linux - qt_arch: '' - qt_version: '6.2.4' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "" + qt_version: "6.2.4" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: windows-2022 name: "Windows-MinGW-w64" msystem: clang64 - vcvars_arch: 'amd64_x86' + vcvars_arch: "amd64_x86" - os: windows-2022 name: "Windows-MSVC" - msystem: '' - architecture: 'x64' - vcvars_arch: 'amd64' + msystem: "" + architecture: "x64" + vcvars_arch: "amd64" qt_ver: 6 qt_host: windows - qt_arch: '' - qt_version: '6.5.2' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "" + qt_version: "6.5.2" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: windows-2022 name: "Windows-MSVC-arm64" - msystem: '' - architecture: 'arm64' - vcvars_arch: 'amd64_arm64' + msystem: "" + architecture: "arm64" + vcvars_arch: "amd64_arm64" qt_ver: 6 qt_host: windows - qt_arch: 'win64_msvc2019_arm64' - qt_version: '6.5.2' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "win64_msvc2019_arm64" + qt_version: "6.5.2" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: macos-12 name: macOS macosx_deployment_target: 11.0 qt_ver: 6 qt_host: mac - qt_arch: '' - qt_version: '6.5.2' - qt_modules: 'qt5compat qtimageformats' - qt_tools: '' + qt_arch: "" + qt_version: "6.5.2" + qt_modules: "qt5compat qtimageformats" + qt_tools: "" - os: macos-12 name: macOS-Legacy macosx_deployment_target: 10.13 qt_ver: 5 qt_host: mac - qt_version: '5.15.2' - qt_modules: '' - qt_tools: '' + qt_version: "5.15.2" + qt_modules: "" + qt_tools: "" runs-on: ${{ matrix.os }} @@ -547,14 +547,14 @@ jobs: if: runner.os == 'Linux' && matrix.qt_ver != 6 uses: actions/upload-artifact@v3 with: - name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }} + name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher.tar.gz - name: Upload binary tarball (Linux, portable, Qt 5) if: runner.os == 'Linux' && matrix.qt_ver != 6 uses: actions/upload-artifact@v3 with: - name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }} + name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }} path: PrismLauncher-portable.tar.gz - name: Upload binary tarball (Linux, Qt 6) diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 98842664d..28578165f 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -41,9 +41,9 @@ jobs: run: | mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }} mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz - mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz + mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz @@ -87,8 +87,8 @@ jobs: draft: true prerelease: false files: | - PrismLauncher-Linux-${{ env.VERSION }}.tar.gz - PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz + PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz + PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz PrismLauncher-Linux-x86_64.AppImage PrismLauncher-Linux-x86_64.AppImage.zsync PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz