1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-04-20 11:47:46 +00:00

Overhauled asset updating process (again)

Now the code is much cleaner, should be less error prone. And best of
all, the process is now user-visible in a nice progress box.
This commit is contained in:
Joshua Goins 2022-03-13 20:44:57 -04:00
parent 065580583d
commit d939d26280
2 changed files with 246 additions and 220 deletions

View file

@ -2,6 +2,8 @@
#include <QObject> #include <QObject>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QProgressDialog>
#include <QJsonArray>
class LauncherCore; class LauncherCore;
class QNetworkReply; class QNetworkReply;
@ -13,9 +15,9 @@ public:
AssetUpdater(LauncherCore& launcher); AssetUpdater(LauncherCore& launcher);
void update(const ProfileSettings& profile); void update(const ProfileSettings& profile);
void finishDownload(QNetworkReply* reply);
void beginInstall(); void beginInstall();
void checkIfCheckingIsDone();
void checkIfDalamudAssetsDone(); void checkIfDalamudAssetsDone();
void checkIfFinished(); void checkIfFinished();
@ -25,6 +27,8 @@ signals:
private: private:
LauncherCore& launcher; LauncherCore& launcher;
QProgressDialog* dialog;
const ProfileSettings* currentSettings = nullptr; const ProfileSettings* currentSettings = nullptr;
QString remoteDalamudVersion; QString remoteDalamudVersion;
@ -40,6 +44,9 @@ private:
bool needsDalamudInstall = false; bool needsDalamudInstall = false;
bool needsNativeInstall = false; bool needsNativeInstall = false;
int remoteDalamudAssetVersion; int remoteDalamudAssetVersion = -1;
QList<QString> dalamudAssetNeededFilenames; QList<QString> dalamudAssetNeededFilenames;
QJsonArray remoteDalamudAssetArray;
QString dataDir;
}; };

View file

@ -11,24 +11,32 @@
#include "launchercore.h" #include "launchercore.h"
const QString baseGoatcorpDomain = "https://goatcorp.github.io"; const QString baseGoatDomain = "https://goatcorp.github.io";
const QString dalamudRemotePath = baseGoatcorpDomain + "/dalamud-distrib"; const QString baseDalamudDistribution = baseGoatDomain + "/dalamud-distrib";
const QString dalamudVersion = "/latest"; const QString dalamudLatestPackageURL = baseDalamudDistribution + "/latest.zip";
const QString dalamudVersionPath = dalamudRemotePath + "/version"; const QString dalamudVersionManifestURL = baseDalamudDistribution + "/version";
const QString dalamudAssetRemotePath = baseGoatcorpDomain + "/DalamudAssets"; const QString baseDalamudAssetDistribution = baseGoatDomain + "/DalamudAssets";
const QString dalamudAssetManifestPath = dalamudAssetRemotePath + "/asset.json"; const QString dalamudAssetManifestURL = baseDalamudAssetDistribution + "/asset.json";
const QString nativeLauncherRemotePath = const QString baseNativeLauncherDistribution =
"https://github.com/redstrate/nativelauncher/releases/download/"; "https://github.com/redstrate/nativelauncher/releases/download/";
const QString nativeLauncherVersion = "v1.0.0"; const QString nativeLauncherLatestPackageURL =
baseNativeLauncherDistribution + "/latest/NativeLauncher.exe";
const QString dotnetRuntimePackageURL =
"https://dotnetcli.azureedge.net/dotnet/Runtime/%1/dotnet-runtime-%1-win-x64.zip";
const QString dotnetDesktopPackageURL =
"https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/%1/windowsdesktop-runtime-%1-win-x64.zip";
AssetUpdater::AssetUpdater(LauncherCore& launcher) : launcher(launcher) { AssetUpdater::AssetUpdater(LauncherCore& launcher) : launcher(launcher) {
connect(launcher.mgr, &QNetworkAccessManager::finished, this,
&AssetUpdater::finishDownload);
launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
dialog = new QProgressDialog("Updating assets...", "Cancel", 0, 0);
dataDir =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
} }
void AssetUpdater::update(const ProfileSettings& profile) { void AssetUpdater::update(const ProfileSettings& profile) {
@ -38,71 +46,23 @@ void AssetUpdater::update(const ProfileSettings& profile) {
return; return;
} }
const QString dataDir = // first, we want to collect all of the remote versions
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
qInfo() << "Starting update sequence..."; qInfo() << "Starting update sequence...";
dialog->setLabelText("Checking for updates...");
bool isDalamudUpdated = false; // dalamud assets
if (remoteDalamudVersion.isEmpty()) {
QNetworkRequest request(dalamudVersionPath);
auto reply = launcher.mgr->get(request);
reply->setObjectName("DalamudVersionCheck");
currentSettings = &profile; // TODO: this is dirty, should change
return;
} else {
if (launcher.dalamudVersion != remoteDalamudVersion) {
isDalamudUpdated = false;
} else {
qInfo() << "No need to update Dalamud.";
isDalamudUpdated = true;
}
if(launcher.runtimeVersion != remoteRuntimeVersion) {
doneDownloadingRuntimeCore = false;
doneDownloadingRuntimeDesktop = false;
needsRuntimeInstall = true;
// core
{ {
QNetworkRequest request(
QString("https://dotnetcli.azureedge.net/dotnet/Runtime/%1/dotnet-runtime-%1-win-x64.zip")
.arg(remoteRuntimeVersion));
auto reply = launcher.mgr->get(request);
reply->setObjectName("Dotnet-core");
}
// desktop
{
QNetworkRequest request(
QString("https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/%1/windowsdesktop-runtime-%1-win-x64.zip")
.arg(remoteRuntimeVersion));
auto reply = launcher.mgr->get(request);
reply->setObjectName("Dotnet-desktop");
}
} else {
qInfo() << "No need to update Runtime.";
doneDownloadingRuntimeCore = true;
doneDownloadingRuntimeDesktop = true;
}
}
{
qInfo() << "Checking Dalamud assets...";
// we want to prevent logging in before we actually check the version // we want to prevent logging in before we actually check the version
dalamudAssetNeededFilenames.append("dummy"); dalamudAssetNeededFilenames.append("dummy");
// first we want to fetch the list of assets required // first we want to fetch the list of assets required
QNetworkRequest request(dalamudAssetManifestPath); QNetworkRequest request(dalamudAssetManifestURL);
auto reply = launcher.mgr->get(request); auto reply = launcher.mgr->get(request);
connect(reply, &QNetworkReply::finished, [reply, this, profile] { connect(reply, &QNetworkReply::finished, [reply, this, profile] {
dialog->setLabelText("Checking for Dalamud asset updates...");
// lol, they actually provide invalid json. let's fix it if it's borked // lol, they actually provide invalid json. let's fix it if it's borked
QString badJson = reply->readAll(); QString badJson = reply->readAll();
@ -130,117 +90,21 @@ void AssetUpdater::update(const ProfileSettings& profile) {
remoteDalamudAssetVersion = doc.object()["Version"].toInt(); remoteDalamudAssetVersion = doc.object()["Version"].toInt();
if(remoteDalamudAssetVersion != launcher.dalamudAssetVersion) { remoteDalamudAssetArray = doc.object()["Assets"].toArray();
qInfo() << "Dalamud assets out of date.";
dalamudAssetNeededFilenames.clear(); checkIfCheckingIsDone();
for(auto assetObject : doc.object()["Assets"].toArray()) {
{
qInfo() << "Starting download for " << assetObject.toObject()["FileName"];
dalamudAssetNeededFilenames.append(assetObject.toObject()["FileName"].toString());
QNetworkRequest assetRequest(assetObject.toObject()["Url"].toString());
auto assetReply = launcher.mgr->get(assetRequest);
connect(assetReply, &QNetworkReply::finished, [this, assetReply, assetObject = assetObject.toObject()] {
const QString dataDir =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/DalamudAssets/";
if (!QDir().exists(dataDir))
QDir().mkdir(dataDir);
const QString fileName = assetObject["FileName"].toString();
const QList<QString> dirPath = fileName.left(fileName.lastIndexOf("/")).split('/');
qInfo() << "Needed directories: " << dirPath;
QString build = dataDir;
for(auto dir : dirPath) {
if (!QDir().exists(build + dir))
QDir().mkdir(build + dir);
build += dir + "/";
}
QFile file(dataDir + assetObject["FileName"].toString());
file.open(QIODevice::WriteOnly);
file.write(assetReply->readAll());
file.close();
dalamudAssetNeededFilenames.removeOne(assetObject["FileName"].toString());
checkIfDalamudAssetsDone();
});
}
}
} else {
dalamudAssetNeededFilenames.clear();
qInfo() << "Dalamud assets up to date.";
checkIfFinished();
}
}); });
} }
if(remoteDalamudVersion != launcher.dalamudVersion) { // dalamud injector / net runtime / nativelauncher
qInfo() << "Downloading Dalamud..."; // they're all updated in unison, so there's no reason to have multiple checks
doneDownloadingDalamud = false;
needsDalamudInstall = true;
QNetworkRequest request(dalamudRemotePath + dalamudVersion +
".zip");
auto reply = launcher.mgr->get(request);
reply->setObjectName("Dalamud");
}
const bool hasNative = QFile::exists(dataDir + "/NativeLauncher.exe");
if (!hasNative) {
// download nativelauncher release (needed to launch the game with fixed
// ACLs)
{ {
qInfo() << "Downloading NativeLauncher..."; QNetworkRequest request(dalamudVersionManifestURL);
doneDownloadingNativelauncher = false;
needsNativeInstall = true;
QNetworkRequest request(nativeLauncherRemotePath +
nativeLauncherVersion +
"/NativeLauncher.exe");
auto reply = launcher.mgr->get(request); auto reply = launcher.mgr->get(request);
reply->setObjectName("NativeLauncher"); connect(reply, &QNetworkReply::finished, [this, profile, reply] {
} dialog->setLabelText("Checking for Dalamud updates...");
}
}
void AssetUpdater::finishDownload(QNetworkReply* reply) {
if (reply->objectName() == "Dalamud") {
qInfo() << "Dalamud finished downloading!";
QFile file(tempDir.path() + "/latest.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingDalamud = true;
launcher.dalamudVersion = remoteDalamudVersion;
checkIfFinished();
} else if (reply->objectName() == "NativeLauncher") {
qInfo() << "NativeLauncher finished downloading!";
QFile file(tempDir.path() + "/NativeLauncher.exe");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingNativelauncher = true;
checkIfFinished();
} else if (reply->objectName() == "DalamudVersionCheck") {
QByteArray str = reply->readAll(); QByteArray str = reply->readAll();
// for some god forsaken reason, the version string comes back as raw // for some god forsaken reason, the version string comes back as raw
// bytes, ex: \xFF\xFE{\x00\"\x00""A\x00s\x00s\x00""e\x00m\x00 so we // bytes, ex: \xFF\xFE{\x00\"\x00""A\x00s\x00s\x00""e\x00m\x00 so we
@ -259,37 +123,12 @@ void AssetUpdater::finishDownload(QNetworkReply* reply) {
qInfo() << "Latest Dalamud version reported: " << remoteDalamudVersion; qInfo() << "Latest Dalamud version reported: " << remoteDalamudVersion;
qInfo() << "Latest NET runtime reported: " << remoteRuntimeVersion; qInfo() << "Latest NET runtime reported: " << remoteRuntimeVersion;
update(*currentSettings); checkIfCheckingIsDone();
currentSettings = nullptr; });
} else if(reply->objectName() == "Dotnet-core") {
qInfo() << "Dotnet-core finished downloading!";
QFile file(tempDir.path() + "/dotnet-core.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingRuntimeCore = true;
checkIfFinished();
} else if(reply->objectName() == "Dotnet-desktop") {
qInfo() << "Dotnet-desktop finished downloading!";
QFile file(tempDir.path() + "/dotnet-desktop.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingRuntimeDesktop = true;
checkIfFinished();
} }
} }
void AssetUpdater::beginInstall() { void AssetUpdater::beginInstall() {
const QString dataDir =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if(needsDalamudInstall) { if(needsDalamudInstall) {
bool success = !JlCompress::extractDir(tempDir.path() + "/latest.zip", bool success = !JlCompress::extractDir(tempDir.path() + "/latest.zip",
dataDir + "/Dalamud") dataDir + "/Dalamud")
@ -339,10 +178,7 @@ void AssetUpdater::checkIfDalamudAssetsDone() {
launcher.dalamudAssetVersion = remoteDalamudAssetVersion; launcher.dalamudAssetVersion = remoteDalamudAssetVersion;
const QString dataDir = QFile file(dataDir + "/DalamudAssets/" + "asset.ver");
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/DalamudAssets/";
QFile file(dataDir + "asset.ver");
file.open(QIODevice::WriteOnly | QIODevice::Text); file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write(QString::number(remoteDalamudAssetVersion).toUtf8()); file.write(QString::number(remoteDalamudAssetVersion).toUtf8());
file.close(); file.close();
@ -357,9 +193,192 @@ void AssetUpdater::checkIfFinished() {
doneDownloadingRuntimeCore && doneDownloadingRuntimeCore &&
doneDownloadingRuntimeDesktop && doneDownloadingRuntimeDesktop &&
dalamudAssetNeededFilenames.empty()) { dalamudAssetNeededFilenames.empty()) {
if(needsRuntimeInstall || needsNativeInstall || needsDalamudInstall) if (needsRuntimeInstall || needsNativeInstall || needsDalamudInstall) {
beginInstall(); beginInstall();
else } else {
dialog->setLabelText("Finished!");
dialog->close();
finishedUpdating(); finishedUpdating();
} }
} }
}
void AssetUpdater::checkIfCheckingIsDone() {
if(remoteDalamudVersion.isEmpty() || remoteRuntimeVersion.isEmpty() || remoteDalamudAssetVersion == -1) {
return;
}
// now that we got all the information we need, let's check if anything is
// updateable
dialog->setLabelText("Starting update...");
// dalamud injector / net runtime
if(launcher.runtimeVersion != remoteRuntimeVersion) {
doneDownloadingRuntimeCore = false;
doneDownloadingRuntimeDesktop = false;
needsRuntimeInstall = true;
// core
{
QNetworkRequest request(dotnetRuntimePackageURL.arg(remoteRuntimeVersion));
auto reply = launcher.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, reply] {
qInfo() << "Dotnet-core finished downloading!";
dialog->setLabelText("Updating Dotnet-core...");
QFile file(tempDir.path() + "/dotnet-core.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingRuntimeCore = true;
checkIfFinished();
});
}
// desktop
{
QNetworkRequest request(dotnetDesktopPackageURL.arg(remoteRuntimeVersion));
auto reply = launcher.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, reply] {
qInfo() << "Dotnet-desktop finished downloading!";
dialog->setLabelText("Updating Dotnet-desktop...");
QFile file(tempDir.path() + "/dotnet-desktop.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingRuntimeDesktop = true;
checkIfFinished();
});
}
} else {
doneDownloadingRuntimeCore = true;
doneDownloadingRuntimeDesktop = true;
needsRuntimeInstall = false;
checkIfFinished();
}
if(remoteDalamudVersion != launcher.dalamudVersion) {
qInfo() << "Downloading Dalamud...";
doneDownloadingDalamud = false;
needsDalamudInstall = true;
QNetworkRequest request(dalamudLatestPackageURL);
auto reply = launcher.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, reply] {
qInfo() << "Dalamud finished downloading!";
dialog->setLabelText("Updating Dalamud...");
QFile file(tempDir.path() + "/latest.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingDalamud = true;
launcher.dalamudVersion = remoteDalamudVersion;
checkIfFinished();
});
} else {
qInfo() << "No need to update Dalamud.";
doneDownloadingDalamud = true;
needsDalamudInstall = false;
checkIfFinished();
}
// dalamud assets
if(remoteDalamudAssetVersion != launcher.dalamudAssetVersion) {
qInfo() << "Dalamud assets out of date.";
dialog->setLabelText("Updating Dalamud assets...");
dalamudAssetNeededFilenames.clear();
for(auto assetObject : remoteDalamudAssetArray) {
{
qInfo() << "Starting download for " << assetObject.toObject()["FileName"];
dalamudAssetNeededFilenames.append(assetObject.toObject()["FileName"].toString());
QNetworkRequest assetRequest(assetObject.toObject()["Url"].toString());
auto assetReply = launcher.mgr->get(assetRequest);
connect(assetReply, &QNetworkReply::finished, [this, assetReply, assetObject = assetObject.toObject()] {
if (!QDir().exists(dataDir + "/DalamudAssets/"))
QDir().mkdir(dataDir + "/DalamudAssets/");
const QString fileName = assetObject["FileName"].toString();
const QList<QString> dirPath = fileName.left(fileName.lastIndexOf("/")).split('/');
qInfo() << "Needed directories: " << dirPath;
QString build = dataDir + "/DalamudAssets/";
for(auto dir : dirPath) {
if (!QDir().exists(build + dir))
QDir().mkdir(build + dir);
build += dir + "/";
}
QFile file(dataDir + "/DalamudAssets/" + assetObject["FileName"].toString());
file.open(QIODevice::WriteOnly);
file.write(assetReply->readAll());
file.close();
dalamudAssetNeededFilenames.removeOne(assetObject["FileName"].toString());
checkIfDalamudAssetsDone();
});
}
}
} else {
dalamudAssetNeededFilenames.clear();
qInfo() << "Dalamud assets up to date.";
checkIfFinished();
}
// nativelauncher
const bool hasNative = QFile::exists(dataDir + "/NativeLauncher.exe");
if (!hasNative) {
// download nativelauncher release (needed to launch the game with fixed
// ACLs)
qInfo() << "Downloading NativeLauncher...";
doneDownloadingNativelauncher = false;
needsNativeInstall = true;
QNetworkRequest request(nativeLauncherLatestPackageURL);
auto reply = launcher.mgr->get(request);
connect(reply, &QNetworkReply::finished, [this, reply] {
qInfo() << "NativeLauncher finished downloading!";
dialog->setLabelText("Updating Nativelauncher...");
QFile file(tempDir.path() + "/NativeLauncher.exe");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
doneDownloadingNativelauncher = true;
checkIfFinished();
});
}
}