Skip to content

Commit

Permalink
Merge branch 'PrismLauncher:develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Rhelvetican authored Jun 8, 2024
2 parents 200d133 + 3ef5cd2 commit 1f80c7a
Show file tree
Hide file tree
Showing 28 changed files with 361 additions and 176 deletions.
1 change: 0 additions & 1 deletion buildconfig/BuildConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ class Config {

QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
QString AUTH_BASE = "https://authserver.mojang.com/";
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
Expand Down
9 changes: 2 additions & 7 deletions launcher/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,20 +395,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{
static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log";
static const QString logBase = FS::PathCombine("logs", baseLogFile);
auto moveFile = [](const QString& oldName, const QString& newName) {
QFile::remove(newName);
QFile::copy(oldName, newName);
QFile::remove(oldName);
};
if (FS::ensureFolderPathExists("logs")) { // if this did not fail
for (auto i = 0; i <= 4; i++)
if (auto oldName = baseLogFile.arg(i);
QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there
moveFile(oldName, logBase.arg(i));
FS::move(oldName, logBase.arg(i));
}

for (auto i = 4; i > 0; i--)
moveFile(logBase.arg(i - 1), logBase.arg(i));
FS::move(logBase.arg(i - 1), logBase.arg(i));

logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
Expand Down
3 changes: 2 additions & 1 deletion launcher/BaseInstaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <QFile>

#include "BaseInstaller.h"
#include "FileSystem.h"
#include "minecraft/MinecraftInstance.h"

BaseInstaller::BaseInstaller() {}
Expand All @@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to)

bool BaseInstaller::remove(MinecraftInstance* from)
{
return QFile::remove(filename(from->instanceRoot()));
return FS::deletePath(filename(from->instanceRoot()));
}

QString BaseInstaller::filename(const QString& root) const
Expand Down
26 changes: 20 additions & 6 deletions launcher/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,20 +647,34 @@ void ExternalLinkFileProcess::runLinkFile()
qDebug() << "Process exited";
}

bool moveByCopy(const QString& source, const QString& dest)
{
if (!copy(source, dest)()) { // copy
qDebug() << "Copy of" << source << "to" << dest << "failed!";
return false;
}
if (!deletePath(source)) { // remove original
qDebug() << "Deletion of" << source << "failed!";
return false;
};
return true;
}

bool move(const QString& source, const QString& dest)
{
std::error_code err;

ensureFilePathExists(dest);
fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err);

if (err) {
qWarning() << "Failed to move file:" << QString::fromStdString(err.message());
qDebug() << "Source file:" << source;
qDebug() << "Destination file:" << dest;
if (err.value() != 0) {
if (moveByCopy(source, dest))
return true;
qDebug() << "Move of" << source << "to" << dest << "failed!";
qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value());
return false;
}

return err.value() == 0;
return true;
}

bool deletePath(QString path)
Expand Down
3 changes: 2 additions & 1 deletion launcher/InstanceCreationTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QDebug>
#include <QFile>
#include "FileSystem.h"

void InstanceCreationTask::executeTask()
{
Expand Down Expand Up @@ -45,7 +46,7 @@ void InstanceCreationTask::executeTask()
if (!QFile::exists(path))
continue;
qDebug() << "Removing" << path;
if (!QFile::remove(path)) {
if (!FS::deletePath(path)) {
qCritical() << "Couldn't remove the old conflicting files.";
emitFailed(tr("Failed to remove old conflicting files."));
return;
Expand Down
162 changes: 87 additions & 75 deletions launcher/InstanceImportTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@

#include <QtConcurrentRun>
#include <algorithm>
#include <memory>

#include <quazip/quazipdir.h>

Expand All @@ -68,15 +69,8 @@ bool InstanceImportTask::abort()
if (!canAbort())
return false;

if (m_filesNetJob)
m_filesNetJob->abort();
if (m_extractFuture.isRunning()) {
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
// but we can use this call to check the state when the extraction finishes.
m_extractFuture.cancel();
m_extractFuture.waitForFinished();
}

if (task)
task->abort();
return Task::abort();
}

Expand All @@ -89,123 +83,139 @@ void InstanceImportTask::executeTask()
processZipPack();
} else {
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true;

downloadFromUrl();
}
}

void InstanceImportTask::downloadFromUrl()
{
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());

auto entry = APPLICATION->metacache()->resolveEntry("general", path);
entry->setStale(true);
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
m_archivePath = entry->getFullPath();

connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
m_filesNetJob->start();
}
auto filesNetJob = makeShared<NetJob>(tr("Modpack download"), APPLICATION->network());
filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));

void InstanceImportTask::downloadSucceeded()
{
processZipPack();
m_filesNetJob.reset();
connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack);
connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress);
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
task.reset(filesNetJob);
filesNetJob->start();
}

void InstanceImportTask::downloadFailed(QString reason)
QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root)
{
emitFailed(reason);
m_filesNetJob.reset();
}
if (!isRunning()) {
return {};
}
QuaZipDir rootDir(zip, root);
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
setDetails(fileName);
if (fileName == "instance.cfg") {
qDebug() << "MultiMC:" << true;
m_modpackType = ModpackType::MultiMC;
return root;
}
if (fileName == "manifest.json") {
qDebug() << "Flame:" << true;
m_modpackType = ModpackType::Flame;
return root;
}

void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
{
setProgress(current, total);
}
QCoreApplication::processEvents();
}

void InstanceImportTask::downloadAborted()
{
emitAborted();
m_filesNetJob.reset();
// Recurse the search to non-ignored subfolders
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
if ("overrides/" == fileName)
continue;

QString result = getRootFromZip(zip, root + fileName);
if (!result.isEmpty())
return result;
}

return {};
}

void InstanceImportTask::processZipPack()
{
setStatus(tr("Extracting modpack"));
setStatus(tr("Attempting to determine instance type"));
QDir extractDir(m_stagingPath);
qDebug() << "Attempting to create instance from" << m_archivePath;

// open the zip and find relevant files in it
m_packZip.reset(new QuaZip(m_archivePath));
if (!m_packZip->open(QuaZip::mdUnzip)) {
auto packZip = std::make_shared<QuaZip>(m_archivePath);
if (!packZip->open(QuaZip::mdUnzip)) {
emitFailed(tr("Unable to open supplied modpack zip file."));
return;
}

QuaZipDir packZipDir(m_packZip.get());
QuaZipDir packZipDir(packZip.get());
qDebug() << "Attempting to determine instance type";

// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
bool modrinthFound = packZipDir.exists("/modrinth.index.json");
bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
QString root;

// NOTE: Prioritize modpack platforms that aren't searched for recursively.
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
if (modrinthFound) {
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
if (packZipDir.exists("/modrinth.index.json")) {
// process as Modrinth pack
qDebug() << "Modrinth:" << modrinthFound;
qDebug() << "Modrinth:" << true;
m_modpackType = ModpackType::Modrinth;
} else if (technicFound) {
} else if (packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json")) {
// process as Technic pack
qDebug() << "Technic:" << technicFound;
qDebug() << "Technic:" << true;
extractDir.mkpath("minecraft");
extractDir.cd("minecraft");
m_modpackType = ModpackType::Technic;
} else {
QStringList paths_to_ignore{ "overrides/" };

if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
// process as MultiMC instance/pack
qDebug() << "MultiMC:" << mmcRoot;
root = mmcRoot;
m_modpackType = ModpackType::MultiMC;
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore);
!flameRoot.isNull()) {
// process as Flame pack
qDebug() << "Flame:" << flameRoot;
root = flameRoot;
m_modpackType = ModpackType::Flame;
}
root = getRootFromZip(packZip.get());
setDetails("");
}
if (m_modpackType == ModpackType::Unknown) {
emitFailed(tr("Archive does not contain a recognized modpack type."));
return;
}
setStatus(tr("Extracting modpack"));

// make sure we extract just the pack
m_extractFuture =
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
m_extractFutureWatcher.setFuture(m_extractFuture);
auto zipTask = makeShared<MMCZip::ExtractZipTask>(packZip, extractDir, root);

auto progressStep = std::make_shared<TaskStepProgress>();
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
progressStep->state = TaskStepState::Succeeded;
stepProgress(*progressStep);
});

connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished);
connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
progressStep->state = TaskStepState::Failed;
stepProgress(*progressStep);
emitFailed(reason);
});
connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);

connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
progressStep->update(current, total);
stepProgress(*progressStep);
});
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
progressStep->status = status;
stepProgress(*progressStep);
});
task.reset(zipTask);
zipTask->start();
}

void InstanceImportTask::extractFinished()
{
m_packZip.reset();

if (m_extractFuture.isCanceled())
return;
if (!m_extractFuture.result().has_value()) {
emitFailed(tr("Failed to extract modpack"));
return;
}

QDir extractDir(m_stagingPath);

qDebug() << "Fixing permissions for extracted pack files...";
Expand Down Expand Up @@ -324,13 +334,15 @@ void InstanceImportTask::processMultiMC()
m_instIcon = instance.iconKey();

auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
if (importIconPath.isNull() || !QFile::exists(importIconPath))
importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), "icon.png");
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
// import icon
auto iconList = APPLICATION->icons();
if (iconList->iconFileExists(m_instIcon)) {
iconList->deleteIcon(m_instIcon);
}
iconList->installIcons({ importIconPath });
iconList->installIcon(importIconPath, m_instIcon);
}
}
emitSucceeded();
Expand Down
Loading

0 comments on commit 1f80c7a

Please sign in to comment.