Auto-Updater

This commit is contained in:
EIntemporel
2022-10-26 20:13:20 +02:00
parent 33f9fad28b
commit 87f8e3e96c
6 changed files with 453 additions and 0 deletions

View File

@@ -341,6 +341,7 @@ TARGET_LINK_LIBRARIES (noggit
Qt5::OpenGL
Qt5::OpenGLExtensions
Qt5::Xml
Qt5::Network
ColorWidgets-qt5
FramelessHelper
qt_imgui_widgets

View File

@@ -128,6 +128,14 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application::
close();
}
);
_updater = new Noggit::Ui::CUpdater(this);
QObject::connect(_updater, &CUpdater::OpenUpdater, [=]()
{
_updater->setModal(true);
_updater->show();
});
}
void NoggitProjectSelectionWindow::handleContextMenuProjectListItemDelete(std::string const& project_path)

View File

@@ -11,6 +11,7 @@
#include <noggit/ui/windows/noggitWindow/NoggitWindow.hpp>
#include <noggit/ui/windows/projectCreation/NoggitProjectCreationDialog.h>
#include <noggit/ui/windows/settingsPanel/SettingsPanel.h>
#include <noggit/ui/windows/updater/Updater.h>
#include <ui_NoggitProjectSelectionWindow.h>
QT_BEGIN_NAMESPACE
@@ -45,6 +46,7 @@ namespace Noggit::Ui::Windows
::Ui::NoggitProjectSelectionWindow* _ui;
Noggit::Application::NoggitApplication* _noggit_application;
Noggit::Ui::settings* _settings;
Noggit::Ui::CUpdater* _updater;
std::unique_ptr<Noggit::Ui::Windows::NoggitWindow> _project_selection_page;
std::unique_ptr<Component::LoadProjectComponent> _load_project_component;

View File

@@ -0,0 +1,274 @@
#include "Updater.h"
#include "ui_Updater.h"
#include <noggit/Log.h>
namespace Noggit
{
namespace Ui
{
CUpdater::CUpdater(QWidget* parent) :
QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui = new ::Ui::Updater;
ui->setupUi(this);
// hide progress
ui->ProgressFile->hide();
ui->ProgressDownload->hide();
hide();
FileNeeded.clear();
LocalMD5.clear();
OnlineMD5.clear();
FileNeededCount = 0;
FileMissingCount = 0;
LocalCheck = false;
OnlineCheck = false;
NeedUpdate = false;
// connect ui button to update or close
connect(ui->Close, &QPushButton::clicked, this, [=]() { close(); });
connect(ui->Update, &QPushButton::clicked, this, [=]() { DownloadUpdate(); });
QNetworkRequest request((QUrl(StorageURL.arg("MD5"))));
QNetworkReply* reply = (new QNetworkAccessManager)->get(request);
connect(reply, SIGNAL(finished()), this, SLOT(GenerateOnlineMD5()));
GenerateLocalMD5();
}
QByteArray CUpdater::FileMD5(const QString& filename, QCryptographicHash::Algorithm algo)
{
QFile file(filename);
if (file.open(QFile::ReadOnly))
{
QCryptographicHash hash(algo);
if (hash.addData(&file))
return hash.result();
}
return QByteArray();
}
QString CUpdater::ToHashFile(const QString& name, const QString& hash)
{
return QString("%1 %2").arg(name, hash);
}
QUrl CUpdater::GenerateLink(const QString& name)
{
return QUrl(StorageURL.arg(name));
}
void CUpdater::GenerateLocalMD5()
{
QVector<QString> ignore = {
"md5",
"listfile.csv",
"log.txt"
};
QDirIterator it(QDir::currentPath(), QStringList() << "*", QDir::Files, QDirIterator::Subdirectories);
QFile MD5 = QDir::currentPath() + "/MD5";
if (MD5.open(QIODevice::WriteOnly))
{
QTextStream stream(&MD5);
while (it.hasNext())
{
const QString file = it.next();
if (ignore.contains(QFileInfo(file).fileName().toLower()))
continue;
QString name = file.mid(QDir::currentPath().size() + 1);
QString hash = FileMD5(file, QCryptographicHash::Md5).toHex();
LocalMD5[name] = hash;
stream << ToHashFile(name, hash) << Qt::endl;
}
MD5.flush();
MD5.close();
}
LocalCheck = true;
CompareMD5();
}
void CUpdater::GenerateOnlineMD5()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if (!reply || reply->size() == 0xE)
return;
QFile temp_md5(QDir::currentPath() + "online_MD5.txt");
if (temp_md5.open(QIODevice::WriteOnly))
{
QTextStream stream(&temp_md5);
stream << reply->readAll();
temp_md5.flush();
temp_md5.close();
}
if (temp_md5.open(QIODevice::ReadOnly))
{
QTextStream stream(&temp_md5);
while (!stream.atEnd())
{
QString line = stream.readLine();
QStringList split = line.split(R"( )");
OnlineMD5[split[0]] = split[1];
}
temp_md5.close();
}
QDir dir;
dir.remove(temp_md5.fileName());
OnlineCheck = true;
CompareMD5();
}
void CUpdater::CompareMD5()
{
if (!LocalCheck || !OnlineCheck)
return;
for (const auto& e : OnlineMD5.toStdMap())
{
if (LocalMD5[e.first] != e.second)
{
FileNeeded.push_back(e.first);
if (ui->FileList->toPlainText().isEmpty())
{
ui->FileList->append(QString(tr("This is all files needed to update.")));
}
ui->FileList->append(QString(" - %1 [%2 => %3]").arg(FileNeeded.last()).arg(LocalMD5[e.first]).arg(e.second));
}
}
if (FileNeeded.size() == 0)
{
NeedUpdate = false;
return;
}
FileNeededCount = FileNeeded.size();
NeedUpdate = true;
if (NeedUpdate)
{
ui->ProgressFile->setMaximum(FileNeededCount);
ui->ProgressFile->setFormat(QString("File %v/%1").arg(FileNeededCount));
ui->ProgressFile->show();
emit OpenUpdater();
}
}
void CUpdater::DownloadUpdate()
{
if (FileNeeded.size() == 0)
return;
ui->ProgressDownload->show();
QNetworkRequest request(GenerateLink(FileNeeded[0]));
QNetworkReply* reply = (new QNetworkAccessManager)->get(request);
connect(reply, &QNetworkReply::downloadProgress, reply, [this](qint64 received, qint64 total)
{
ui->ProgressDownload->setMaximum(total);
ui->ProgressDownload->setValue(received);
});
connect(reply, SIGNAL(finished()), this, SLOT(GetOnlineFile()));
}
void CUpdater::GetOnlineFile()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
ui->ProgressFile->setValue(ui->ProgressFile->value() + 1);
ui->FileList->append(QString("Downloading : %1 (%2 mb)").arg(FileNeeded[0]).arg(reply->size() / pow(10,6)));
if (!reply || reply->size() == 0xE)
{
FileMissingCount += 1;
FileNeeded.removeAt(0);
if (FileNeeded.size() > 0)
{
DownloadUpdate();
return;
}
StartExternalUpdater();
return;
}
QDir dir;
if (!dir.exists(QDir::currentPath() + TemporaryFolder))
dir.mkpath(QDir::currentPath() + TemporaryFolder);
auto index = FileNeeded[0].lastIndexOf(R"(/)");
if (index >= 0)
{
QDir dir;
if (!dir.exists(FileURL.arg(QDir::currentPath(), TemporaryFolder, FileNeeded[0].mid(0, index))))
dir.mkpath(FileURL.arg(QDir::currentPath(), TemporaryFolder, FileNeeded[0].mid(0, index)));
}
QFile file = FileURL.arg(QDir::currentPath(), TemporaryFolder, FileNeeded[0]);
if (file.open(QIODevice::WriteOnly))
{
file.write(reply->readAll());
file.flush();
file.close();
}
FileNeeded.removeAt(0);
if (FileNeeded.size() > 0)
{
DownloadUpdate();
return;
}
StartExternalUpdater();
}
void CUpdater::StartExternalUpdater()
{
Log << "Start External 0" << std::endl;
if (!NeedUpdate)
return;
Log << "Start External 1" << std::endl;
if (FileNeededCount == FileMissingCount)
return;
Log << "Start External 2" << std::endl;
{
QString exec(QDir::currentPath() + ExternalProcess);
Log << "Start External - " << exec.toStdString() << std::endl;
QProcess process;
process.setProgram(exec);
process.startDetached();
QCoreApplication::quit();
}
}
}
}

View File

@@ -0,0 +1,64 @@
#pragma once
#include <qpushbutton.h>
#include "qprocess.h"
#include <qfile.h>
#include <qdir.h>
#include <qdiriterator.h>
#include <QtWidgets/QDialog>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include "ui_Updater.h"
namespace Noggit
{
namespace Ui
{
class CUpdater : public QDialog
{
Q_OBJECT
::Ui::Updater* ui;
public:
CUpdater(QWidget* parent = nullptr);
private:
QByteArray FileMD5(const QString& filename, QCryptographicHash::Algorithm algo);
QString ToHashFile(const QString& name, const QString& hash);
QUrl GenerateLink(const QString& name);
void GenerateLocalMD5();
void CompareMD5();
void DownloadUpdate();
void StartExternalUpdater();
private slots:
void GenerateOnlineMD5();
void GetOnlineFile();
signals:
void OpenUpdater();
private:
const QString TemporaryFolder = "/temp";
const QString StorageURL = "https://raw.githubusercontent.com/Intemporel/NoggitRedBinaries/main/%1";
const QString FileURL = "%1%2/%3";
const QString ExternalProcess = "/noggit-updater.exe";
int FileNeededCount;
int FileMissingCount;
bool NeedUpdate;
bool LocalCheck, OnlineCheck;
QMap<QString, QString> LocalMD5;
QMap<QString, QString> OnlineMD5;
QVector<QString> FileNeeded;
};
}
}

View File

@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Updater</class>
<widget class="QDialog" name="Updater">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>376</height>
</rect>
</property>
<property name="windowTitle">
<string>NoggitRed Updater</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>12</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>An update was found, do you want to update ?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="Update">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="Close">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="ProgressFile">
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="format">
<string>File %v/10</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="ProgressDownload">
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="format">
<string>Bytes %v / %m</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="FileList"/>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>12</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>