From 5f781b3053c5ba8a25d354903acf2c31dc9a56c5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 8 May 2013 12:56:43 -0500 Subject: Implement basic game updater. Resolves MMC-4: https://jira.forkk.net/browse/MMC-4 --- libmultimc/src/gameupdatetask.cpp | 227 +++++++++++++++++++++++++++++++++++++- 1 file changed, 225 insertions(+), 2 deletions(-) (limited to 'libmultimc/src/gameupdatetask.cpp') diff --git a/libmultimc/src/gameupdatetask.cpp b/libmultimc/src/gameupdatetask.cpp index c152147e..34c8f670 100644 --- a/libmultimc/src/gameupdatetask.cpp +++ b/libmultimc/src/gameupdatetask.cpp @@ -15,8 +15,231 @@ #include "gameupdatetask.h" -GameUpdateTask::GameUpdateTask(const LoginResponse &response, QObject *parent) : - QObject(parent), m_response(response) +#include + +#include +#include +#include +#include + +#include + +#include "minecraftversionlist.h" + +#include "pathutils.h" +#include "netutils.h" + +GameUpdateTask::GameUpdateTask(const LoginResponse &response, Instance *inst, QObject *parent) : + Task(parent), m_response(response) +{ + m_inst = inst; + m_updateState = StateInit; + m_currentDownload = 0; +} + +void GameUpdateTask::executeTask() +{ + updateStatus(); + + QNetworkAccessManager networkMgr; + netMgr = &networkMgr; + + // Get a pointer to the version object that corresponds to the instance's version. + MinecraftVersion *targetVersion = (MinecraftVersion *)MinecraftVersionList::getMainList(). + findVersion(m_inst->intendedVersion()); + Q_ASSERT_X(targetVersion != NULL, "game update", "instance's intended version is not an actual version"); + + // Make directories + QDir binDir(m_inst->binDir()); + if (!binDir.exists() && !binDir.mkpath(".")) + { + error("Failed to create bin folder."); + return; + } + + + + ///////////////////////// + // BUILD DOWNLOAD LIST // + ///////////////////////// + // Build a list of URLs that will need to be downloaded. + + setState(StateDetermineURLs); + + + // Add the URL for minecraft.jar + + // This will be either 'minecraft' or the version number, depending on where + // we're downloading from. + QString jarFilename = "minecraft"; + + if (targetVersion->isForNewLauncher()) + jarFilename = targetVersion->descriptor(); + + QUrl mcJarURL = targetVersion->downloadURL() + jarFilename + ".jar"; + qDebug() << mcJarURL.toString(); + m_downloadList.append(FileToDownload(mcJarURL, PathCombine(m_inst->minecraftDir(), "bin/minecraft.jar"))); + + + + //////////////////// + // DOWNLOAD FILES // + //////////////////// + setState(StateDownloadFiles); + for (int i = 0; i < m_downloadList.length(); i++) + { + m_currentDownload = i; + if (!downloadFile(m_downloadList[i])) + return; + } + + + + /////////////////// + // INSTALL FILES // + /////////////////// + setState(StateInstall); + + // Nothing to do here yet + + + + ////////////// + // FINISHED // + ////////////// + setState(StateFinished); + emit gameUpdateComplete(m_response); +} + +bool GameUpdateTask::downloadFile(const FileToDownload &file) +{ + setSubStatus("Downloading " + file.url().toString()); + QNetworkReply *reply = netMgr->get(QNetworkRequest(file.url())); + + this->connect(reply, SIGNAL(downloadProgress(qint64,qint64)), + SLOT(updateDownloadProgress(qint64,qint64))); + + NetUtils::waitForNetRequest(reply); + + if (reply->error() == QNetworkReply::NoError) + { + QFile outFile = file.path(); + if (outFile.exists() && !outFile.remove()) + { + error("Can't delete old file " + file.path() + ": " + outFile.errorString()); + return false; + } + + if (!outFile.open(QIODevice::WriteOnly)) + { + error("Can't write to " + file.path() + ": " + outFile.errorString()); + return false; + } + + outFile.write(reply->readAll()); + outFile.close(); + } + else + { + error("Can't download " + file.url().toString() + ": " + reply->errorString()); + return false; + } + + // TODO: Check file integrity after downloading. + + return true; +} + +int GameUpdateTask::state() const +{ + return m_updateState; +} + +void GameUpdateTask::setState(int state, bool resetSubStatus) +{ + m_updateState = state; + if (resetSubStatus) + setSubStatus(""); + else // We only need to update if we're not resetting substatus becasue setSubStatus updates status for us. + updateStatus(); +} + +QString GameUpdateTask::subStatus() const +{ + return m_subStatusMsg; +} + +void GameUpdateTask::setSubStatus(const QString &msg) +{ + m_subStatusMsg = msg; + updateStatus(); +} + +QString GameUpdateTask::getStateMessage(int state) +{ + switch (state) + { + case StateInit: + return "Initializing"; + + case StateDetermineURLs: + return "Determining files to download"; + + case StateDownloadFiles: + return "Downloading files"; + + case StateInstall: + return "Installing"; + + case StateFinished: + return "Finished"; + + default: + return "Downloading instance files"; + } +} + +void GameUpdateTask::updateStatus() +{ + QString newStatus; + + newStatus = getStateMessage(state()); + if (!subStatus().isEmpty()) + newStatus += ": " + subStatus(); + else + newStatus += "..."; + + setStatus(newStatus); +} + + +void GameUpdateTask::error(const QString &msg) +{ + emit gameUpdateError(msg); +} + +void GameUpdateTask::updateDownloadProgress(qint64 current, qint64 total) +{ + // The progress on the current file is current / total + float currentDLProgress = (float) current / (float) total; // Cast ALL the values! + + // The overall progress is (current progress + files downloaded) / total files to download + float overallDLProgress = ((currentDLProgress + m_currentDownload) / (float) m_downloadList.length()); + + // Multiply by 100 to make it a percentage. + setProgress((int)(overallDLProgress * 100)); +} + + + +FileToDownload::FileToDownload(const QUrl &url, const QString &path, QObject *parent) : + QObject(parent), m_dlURL(url), m_dlPath(path) +{ + +} + +FileToDownload::FileToDownload(const FileToDownload &other) : + QObject(other.parent()), m_dlURL(other.m_dlURL), m_dlPath(other.m_dlPath) { } -- cgit v1.2.3