Finish implementing update installation.

Also add the option to update on exit.
This commit is contained in:
Andrew 2013-12-06 12:59:58 -06:00
parent e90f1a2756
commit 6ac94ddcb6
8 changed files with 128 additions and 10 deletions

View File

@ -2,10 +2,12 @@
#include "MultiMC.h" #include "MultiMC.h"
#include <iostream> #include <iostream>
#include <QDir> #include <QDir>
#include <QFileInfo>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QTranslator> #include <QTranslator>
#include <QLibraryInfo> #include <QLibraryInfo>
#include <QMessageBox> #include <QMessageBox>
#include <QStringList>
#include "gui/MainWindow.h" #include "gui/MainWindow.h"
#include "gui/dialogs/VersionSelectDialog.h" #include "gui/dialogs/VersionSelectDialog.h"
@ -409,6 +411,65 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist()
return m_javalist; return m_javalist;
} }
#ifdef WINDOWS
#define UPDATER_BIN "updater.exe"
#elif LINUX
#define UPDATER_BIN "updater"
#elif OSX
#define UPDATER_BIN "updater"
#else
#error Unsupported operating system.
#endif
void MultiMC::installUpdates(const QString& updateFilesDir, bool restartOnFinish)
{
QLOG_INFO() << "Installing updates.";
#if LINUX
// On Linux, the MultiMC executable file is actually in the bin folder inside the installation directory.
// This means that MultiMC's *actual* install path is the parent folder.
// We need to tell the updater to run with this directory as the install path, rather than the bin folder where the executable is.
// On other operating systems, we'll just use the path to the executable.
QString appDir = QFileInfo(MMC->applicationDirPath()).dir().path();
// On Linux, we also need to set the finish command to the launch script, rather than the binary.
QString finishCmd = PathCombine(appDir, "MultiMC");
#else
QString appDir = MMC->applicationDirPath();
QString finishCmd = MMC->applicationFilePath();
#endif
// Build the command we'll use to run the updater.
// Note, the above comment about the app dir path on Linux is irrelevant here because the updater binary is always in the
// same folder as the main binary.
QString updaterBinary = PathCombine(MMC->applicationDirPath(), UPDATER_BIN);
QStringList args;
// ./updater --install-dir $INSTALL_DIR --package-dir $UPDATEFILES_DIR --script $UPDATEFILES_DIR/file_list.xml --wait $PID --mode main
args << "--install-dir" << appDir;
args << "--package-dir" << updateFilesDir;
args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
args << "--wait" << QString::number(MMC->applicationPid());
if (restartOnFinish)
args << "--finish-cmd" << finishCmd;
QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" ");
QProcess::startDetached(updaterBinary, args);
// Now that we've started the updater, quit MultiMC.
MMC->quit();
}
void MultiMC::setUpdateOnExit(const QString& updateFilesDir)
{
m_updateOnExitPath = updateFilesDir;
}
QString MultiMC::getExitUpdatePath() const
{
return m_updateOnExitPath;
}
int main_gui(MultiMC &app) int main_gui(MultiMC &app)
{ {
// show main window // show main window
@ -417,7 +478,13 @@ int main_gui(MultiMC &app)
mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray())); mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
mainWin.show(); mainWin.show();
mainWin.checkSetDefaultJava(); mainWin.checkSetDefaultJava();
return app.exec(); auto exitCode = app.exec();
// Update if necessary.
if (!app.getExitUpdatePath().isEmpty())
app.installUpdates(app.getExitUpdatePath(), false);
return exitCode;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])

View File

@ -98,6 +98,22 @@ public:
std::shared_ptr<JavaVersionList> javalist(); std::shared_ptr<JavaVersionList> javalist();
/*!
* Installs update from the given update files directory.
*/
void installUpdates(const QString& updateFilesDir, bool restartOnFinish=false);
/*!
* Sets MultiMC to install updates from the given directory when it exits.
*/
void setUpdateOnExit(const QString& updateFilesDir);
/*!
* Gets the path to install updates from on exit.
* If this is an empty string, no updates should be installed on exit.
*/
QString getExitUpdatePath() const;
private: private:
void initLogger(); void initLogger();
@ -124,6 +140,8 @@ private:
QsLogging::DestinationPtr m_fileDestination; QsLogging::DestinationPtr m_fileDestination;
QsLogging::DestinationPtr m_debugDestination; QsLogging::DestinationPtr m_debugDestination;
QString m_updateOnExitPath;
Status m_status = MultiMC::Failed; Status m_status = MultiMC::Failed;
MultiMCVersion m_version; MultiMCVersion m_version;
}; };

View File

@ -439,20 +439,31 @@ void MainWindow::updateAvailable(QString repo, QString versionName, int versionI
QLOG_INFO() << "Update will be installed later."; QLOG_INFO() << "Update will be installed later.";
break; break;
case UPDATE_NOW: case UPDATE_NOW:
{ downloadUpdates(repo, versionId);
QLOG_INFO() << "Installing update.";
ProgressDialog updateDlg(this);
DownloadUpdateTask updateTask(repo, versionId, &updateDlg);
updateDlg.exec(&updateTask);
}
break; break;
case UPDATE_ONEXIT: case UPDATE_ONEXIT:
// TODO: Implement installing updates on exit. downloadUpdates(repo, versionId, true);
QLOG_INFO() << "Installing on exit is not implemented yet.";
break; break;
} }
} }
void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit)
{
QLOG_INFO() << "Downloading updates.";
// TODO: If the user chooses to update on exit, we should download updates in the background.
// Doing so is a bit complicated, because we'd have to make sure it finished downloading before actually exiting MultiMC.
ProgressDialog updateDlg(this);
DownloadUpdateTask updateTask(repo, versionId, &updateDlg);
// If the task succeeds, install the updates.
if (updateDlg.exec(&updateTask))
{
if (installOnExit)
MMC->setUpdateOnExit(updateTask.updateFilesDir());
else
MMC->installUpdates(updateTask.updateFilesDir());
}
}
void MainWindow::onCatToggled(bool state) void MainWindow::onCatToggled(bool state)
{ {
setCatBackground(state); setCatBackground(state);

View File

@ -168,6 +168,11 @@ slots:
void changeActiveAccount(); void changeActiveAccount();
void repopulateAccountsMenu(); void repopulateAccountsMenu();
/*!
* Runs the DownloadUpdateTask and installs updates.
*/
void downloadUpdates(QString repo, int versionId, bool installOnExit=false);
protected: protected:
bool eventFilter(QObject *obj, QEvent *ev); bool eventFilter(QObject *obj, QEvent *ev);

View File

@ -41,6 +41,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="btnUpdateOnExit">
<property name="text">
<string>Update after MultiMC closes</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="btnUpdateLater"> <widget class="QPushButton" name="btnUpdateLater">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -391,3 +391,8 @@ void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 tota
setProgress((int)(((float)current / (float)total)*100)); setProgress((int)(((float)current / (float)total)*100));
} }
QString DownloadUpdateTask::updateFilesDir()
{
return m_updateFilesDir.path();
}

View File

@ -28,6 +28,11 @@ class DownloadUpdateTask : public Task
public: public:
explicit DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent=0); explicit DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent=0);
/*!
* Gets the directory that contains the update files.
*/
QString updateFilesDir();
protected: protected:
// TODO: We should probably put these data structures into a separate header... // TODO: We should probably put these data structures into a separate header...

View File

@ -388,7 +388,7 @@ void UpdateInstaller::restartMainApp()
{ {
try try
{ {
std::string command = m_installDir + '/' + m_finishCmd; std::string command = m_finishCmd;
std::list<std::string> args; std::list<std::string> args;
if (!command.empty()) if (!command.empty())