feat(updater): tie in part 2, let there be UI!
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
parent
b7dd32274c
commit
1f70589deb
@ -122,6 +122,7 @@
|
|||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <LocalPeer.h>
|
#include <LocalPeer.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys.h>
|
#include <sys.h>
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
@ -397,6 +398,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
.arg(dataPath));
|
.arg(dataPath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_dataPath = dataPath;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
|
* 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
|
#ifdef Q_OS_MAC
|
||||||
m_updater.reset(new MacSparkleUpdater());
|
m_updater.reset(new MacSparkleUpdater());
|
||||||
#else
|
#else
|
||||||
m_updater.reset(new PrismExternalUpdater(m_rootPath, dataPath));
|
m_updater.reset(new PrismExternalUpdater(m_rootPath, m_dataPath));
|
||||||
#endif
|
#endif
|
||||||
qDebug() << "<> Updater started.";
|
qDebug() << "<> Updater started.";
|
||||||
}
|
}
|
||||||
@ -1024,9 +1026,120 @@ void Application::setupWizardFinished(int status)
|
|||||||
performMainStartupAction();
|
performMainStartupAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<QDateTime, QString, QString, QString, QString> 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()
|
void Application::performMainStartupAction()
|
||||||
{
|
{
|
||||||
m_status = Application::Initialized;
|
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()) {
|
if (!m_instanceIdToLaunch.isEmpty()) {
|
||||||
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
||||||
if (inst) {
|
if (inst) {
|
||||||
|
@ -187,6 +187,11 @@ public:
|
|||||||
return m_rootPath;
|
return m_rootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// the data path the application is using
|
||||||
|
const QString& dataRoot() {
|
||||||
|
return m_dataPath;
|
||||||
|
}
|
||||||
|
|
||||||
bool isPortable() {
|
bool isPortable() {
|
||||||
return m_portable;
|
return m_portable;
|
||||||
}
|
}
|
||||||
@ -277,6 +282,7 @@ private:
|
|||||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||||
|
|
||||||
QString m_rootPath;
|
QString m_rootPath;
|
||||||
|
QString m_dataPath;
|
||||||
Status m_status = Application::StartingUp;
|
Status m_status = Application::StartingUp;
|
||||||
Capabilities m_capabilities;
|
Capabilities m_capabilities;
|
||||||
bool m_portable = false;
|
bool m_portable = false;
|
||||||
|
@ -1078,6 +1078,15 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/instanceview/VisualGroup.h
|
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
|
qt_wrap_ui(LAUNCHER_UI
|
||||||
ui/MainWindow.ui
|
ui/MainWindow.ui
|
||||||
ui/setupwizard/PasteWizardPage.ui
|
ui/setupwizard/PasteWizardPage.ui
|
||||||
@ -1138,6 +1147,14 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/dialogs/ChooseProviderDialog.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
|
qt_add_resources(LAUNCHER_RESOURCES
|
||||||
resources/backgrounds/backgrounds.qrc
|
resources/backgrounds/backgrounds.qrc
|
||||||
resources/multimc/multimc.qrc
|
resources/multimc/multimc.qrc
|
||||||
|
@ -199,7 +199,7 @@ void appendSafe(const QString& filename, const QByteArray& data)
|
|||||||
QByteArray buffer;
|
QByteArray buffer;
|
||||||
try {
|
try {
|
||||||
buffer = read(filename);
|
buffer = read(filename);
|
||||||
} catch (FileSystemException) {
|
} catch (FileSystemException&) {
|
||||||
buffer = QByteArray();
|
buffer = QByteArray();
|
||||||
}
|
}
|
||||||
buffer.append(data);
|
buffer.append(data);
|
||||||
|
63
launcher/ui/dialogs/UpdateAvailableDialog.cpp
Normal file
63
launcher/ui/dialogs/UpdateAvailableDialog.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UpdateAvailableDialog.h"
|
||||||
|
#include <QPushButton>
|
||||||
|
#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();
|
||||||
|
});
|
||||||
|
}
|
49
launcher/ui/dialogs/UpdateAvailableDialog.h
Normal file
49
launcher/ui/dialogs/UpdateAvailableDialog.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
155
launcher/ui/dialogs/UpdateAvailableDialog.ui
Normal file
155
launcher/ui/dialogs/UpdateAvailableDialog.ui
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>UpdateAvailableDialog</class>
|
||||||
|
<widget class="QDialog" name="UpdateAvailableDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>636</width>
|
||||||
|
<height>352</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Update Available</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="leftsideLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="icon">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>64</width>
|
||||||
|
<height>64</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="mainLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="headerLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>11</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>A new version is available!</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="versionAvailableLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Version %1 is now available - you have %2 . Would you like to download it now?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="releaseNotesLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Release Notes:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="releaseNotes"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="skipButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Skip This Version</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="delayButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remind Me Later</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="installButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Install Update</string>
|
||||||
|
</property>
|
||||||
|
<property name="default">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -21,19 +21,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "PrismExternalUpdater.h"
|
#include "PrismExternalUpdater.h"
|
||||||
#include <memory>
|
#include <QCoreApplication>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QProgressDialog>
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QTimer>
|
#include <QProgressDialog>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QCoreApplication>
|
#include <QTimer>
|
||||||
|
#include <memory>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
|
#include "ui/dialogs/UpdateAvailableDialog.h"
|
||||||
|
|
||||||
class PrismExternalUpdater::Private {
|
class PrismExternalUpdater::Private {
|
||||||
public:
|
public:
|
||||||
QDir appDir;
|
QDir appDir;
|
||||||
@ -78,12 +81,11 @@ PrismExternalUpdater::~PrismExternalUpdater()
|
|||||||
|
|
||||||
void PrismExternalUpdater::checkForUpdates()
|
void PrismExternalUpdater::checkForUpdates()
|
||||||
{
|
{
|
||||||
QProgressDialog progress(tr("Checking for updates..."), "", 0, -1);
|
QProgressDialog progress(tr("Checking for updates..."), "", 0, 0);
|
||||||
progress.setCancelButton(nullptr);
|
progress.setCancelButton(nullptr);
|
||||||
progress.show();
|
progress.show();
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
|
||||||
QProcess proc;
|
QProcess proc;
|
||||||
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
@ -98,14 +100,16 @@ void PrismExternalUpdater::checkForUpdates()
|
|||||||
auto result_start = proc.waitForStarted(5000);
|
auto result_start = proc.waitForStarted(5000);
|
||||||
if (!result_start) {
|
if (!result_start) {
|
||||||
auto err = proc.error();
|
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();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
auto result_finished = proc.waitForFinished(60000);
|
auto result_finished = proc.waitForFinished(60000);
|
||||||
if (!result_finished) {
|
if (!result_finished) {
|
||||||
auto err = proc.error();
|
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();
|
auto exit_code = proc.exitCode();
|
||||||
@ -116,6 +120,9 @@ void PrismExternalUpdater::checkForUpdates()
|
|||||||
qDebug() << "captured output:" << std_output;
|
qDebug() << "captured output:" << std_output;
|
||||||
qDebug() << "captured error:" << std_error;
|
qDebug() << "captured error:" << std_error;
|
||||||
|
|
||||||
|
progress.hide();
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
switch (exit_code) {
|
switch (exit_code) {
|
||||||
case 0:
|
case 0:
|
||||||
// no update available
|
// no update available
|
||||||
@ -134,12 +141,14 @@ void PrismExternalUpdater::checkForUpdates()
|
|||||||
{
|
{
|
||||||
auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n');
|
auto [first_line, remainder1] = StringUtils::splitFirst(std_output, '\n');
|
||||||
auto [second_line, remainder2] = StringUtils::splitFirst(remainder1, '\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_name = StringUtils::splitFirst(first_line, ": ").second;
|
||||||
auto version_tag = StringUtils::splitFirst(second_line, ": ").second;
|
auto version_tag = StringUtils::splitFirst(second_line, ": ").second;
|
||||||
auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate);
|
auto release_timestamp = QDateTime::fromString(StringUtils::splitFirst(third_line, ": ").second, Qt::ISODate);
|
||||||
qDebug() << "Update available:" << version_name << version_tag << release_timestamp;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -153,48 +162,55 @@ void PrismExternalUpdater::checkForUpdates()
|
|||||||
priv->settings->sync();
|
priv->settings->sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() {
|
bool PrismExternalUpdater::getAutomaticallyChecksForUpdates()
|
||||||
|
{
|
||||||
return priv->autoCheck;
|
return priv->autoCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
double PrismExternalUpdater::getUpdateCheckInterval() {
|
double PrismExternalUpdater::getUpdateCheckInterval()
|
||||||
|
{
|
||||||
return priv->updateInterval;
|
return priv->updateInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrismExternalUpdater::getBetaAllowed() {
|
bool PrismExternalUpdater::getBetaAllowed()
|
||||||
|
{
|
||||||
return priv->allowBeta;
|
return priv->allowBeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) {
|
void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check)
|
||||||
|
{
|
||||||
priv->autoCheck = check;
|
priv->autoCheck = check;
|
||||||
priv->settings->setValue("auto_check", check);
|
priv->settings->setValue("auto_check", check);
|
||||||
priv->settings->sync();
|
priv->settings->sync();
|
||||||
resetAutoCheckTimer();
|
resetAutoCheckTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::setUpdateCheckInterval(double seconds) {
|
void PrismExternalUpdater::setUpdateCheckInterval(double seconds)
|
||||||
|
{
|
||||||
priv->updateInterval = seconds;
|
priv->updateInterval = seconds;
|
||||||
priv->settings->setValue("update_interval", seconds);
|
priv->settings->setValue("update_interval", seconds);
|
||||||
priv->settings->sync();
|
priv->settings->sync();
|
||||||
resetAutoCheckTimer();
|
resetAutoCheckTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::setBetaAllowed(bool allowed) {
|
void PrismExternalUpdater::setBetaAllowed(bool allowed)
|
||||||
|
{
|
||||||
priv->allowBeta = allowed;
|
priv->allowBeta = allowed;
|
||||||
priv->settings->setValue("auto_beta", allowed);
|
priv->settings->setValue("auto_beta", allowed);
|
||||||
priv->settings->sync();
|
priv->settings->sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::resetAutoCheckTimer() {
|
void PrismExternalUpdater::resetAutoCheckTimer()
|
||||||
|
{
|
||||||
int timeoutDuration = 0;
|
int timeoutDuration = 0;
|
||||||
auto now = QDateTime::currentDateTime();
|
auto now = QDateTime::currentDateTime();
|
||||||
if (priv->autoCheck) {
|
if (priv->autoCheck) {
|
||||||
if (priv->lastCheck.isValid()) {
|
if (priv->lastCheck.isValid()) {
|
||||||
auto diff = priv->lastCheck.secsTo(now);
|
auto diff = priv->lastCheck.secsTo(now);
|
||||||
auto secs_left = priv->updateInterval - diff;
|
auto secs_left = priv->updateInterval - diff;
|
||||||
if (secs_left < 0)
|
if (secs_left < 0)
|
||||||
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";
|
qDebug() << "Auto update timer starting," << timeoutDuration / 1000 << "seconds left";
|
||||||
priv->updateTimer.start(timeoutDuration);
|
priv->updateTimer.start(timeoutDuration);
|
||||||
@ -202,18 +218,66 @@ void PrismExternalUpdater::resetAutoCheckTimer() {
|
|||||||
if (priv->updateTimer.isActive())
|
if (priv->updateTimer.isActive())
|
||||||
priv->updateTimer.stop();
|
priv->updateTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::connectTimer() {
|
void PrismExternalUpdater::connectTimer()
|
||||||
|
{
|
||||||
connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired);
|
connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::disconnectTimer() {
|
void PrismExternalUpdater::disconnectTimer()
|
||||||
|
{
|
||||||
disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired);
|
disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismExternalUpdater::autoCheckTimerFired() {
|
void PrismExternalUpdater::autoCheckTimerFired()
|
||||||
|
{
|
||||||
checkForUpdates();
|
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();
|
||||||
|
}
|
||||||
|
@ -34,7 +34,7 @@ class PrismExternalUpdater : public ExternalUpdater {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PrismExternalUpdater(const QString& appDir, const QString& dataDir);
|
PrismExternalUpdater(const QString& appDir, const QString& dataDir);
|
||||||
~PrismExternalUpdater() override;
|
~PrismExternalUpdater() override;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -82,7 +82,8 @@ class PrismExternalUpdater : public ExternalUpdater {
|
|||||||
void disconnectTimer();
|
void disconnectTimer();
|
||||||
void connectTimer();
|
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:
|
public slots:
|
||||||
void autoCheckTimerFired();
|
void autoCheckTimerFired();
|
||||||
|
@ -630,7 +630,7 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target)
|
|||||||
for (auto file : files) {
|
for (auto file : files) {
|
||||||
file_list.append(file.trimmed());
|
file_list.append(file.trimmed());
|
||||||
}
|
}
|
||||||
} catch (FS::FileSystemException) {
|
} catch (FS::FileSystemException&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,15 +675,23 @@ void PrismUpdaterApp::moveAndFinishUpdate(QDir target)
|
|||||||
if (error) {
|
if (error) {
|
||||||
logUpdate(tr("There were errors installing the update."));
|
logUpdate(tr("There were errors installing the update."));
|
||||||
auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail");
|
auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail");
|
||||||
FS::move(m_updateLogPath, fail_marker);
|
FS::copy(m_updateLogPath, fail_marker)();
|
||||||
} else {
|
} else {
|
||||||
logUpdate(tr("Update succeed."));
|
logUpdate(tr("Update succeed."));
|
||||||
auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success");
|
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");
|
auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock");
|
||||||
FS::deletePath(update_lock_path);
|
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);
|
exit(error ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,7 +905,7 @@ bool write_lock_file(const QString& path, QDateTime timestamp, QString from, QSt
|
|||||||
.arg(target)
|
.arg(target)
|
||||||
.arg(data_path)
|
.arg(data_path)
|
||||||
.toUtf8());
|
.toUtf8());
|
||||||
} catch (FS::FileSystemException err) {
|
} catch (FS::FileSystemException& err) {
|
||||||
qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause();
|
qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -922,7 +930,7 @@ void PrismUpdaterApp::performInstall(QFileInfo file)
|
|||||||
"\n"
|
"\n"
|
||||||
"This likely means that a previous update attempt failed. Please ensure your installation is in working order before "
|
"This likely means that a previous update attempt failed. Please ensure your installation is in working order before "
|
||||||
"proceeding.\n"
|
"proceeding.\n"
|
||||||
"Check the Prism Launcher updater log at \n"
|
"Check the Prism Launcher updater log at: \n"
|
||||||
"%7\n"
|
"%7\n"
|
||||||
"for details on the last update attempt.\n"
|
"for details on the last update attempt.\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -936,9 +944,9 @@ void PrismUpdaterApp::performInstall(QFileInfo file)
|
|||||||
msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel);
|
msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel);
|
||||||
msgBox.setDefaultButton(QMessageBox::Cancel);
|
msgBox.setDefaultButton(QMessageBox::Cancel);
|
||||||
switch (msgBox.exec()) {
|
switch (msgBox.exec()) {
|
||||||
case QMessageBox::Ignore:
|
case QMessageBox::AcceptRole:
|
||||||
break;
|
break;
|
||||||
case QMessageBox::Cancel:
|
case QMessageBox::RejectRole:
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
default:
|
default:
|
||||||
return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted"));
|
return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted"));
|
||||||
@ -1005,7 +1013,7 @@ void PrismUpdaterApp::backupAppDir()
|
|||||||
for (auto file : files) {
|
for (auto file : files) {
|
||||||
file_list.append(file.trimmed());
|
file_list.append(file.trimmed());
|
||||||
}
|
}
|
||||||
} catch (FS::FileSystemException) {
|
} catch (FS::FileSystemException&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user