sneedmc/launcher/minecraft/MinecraftUpdate.cpp
flow 5da87d1904
fix: add missing connections to the abort signal
Beginning with efa3fbff39, we separated
the failing and the aborting signals, as they can mean different
things in certain contexts. Still, some places are not yet changed to
reflect this modification. This can cause aborting of progress dialogs
to not work, instead making the application hang in an unusable satte.

This goes through some places where it's not hooked up yet, fixing their
behaviour in those kinds of situation.
2022-06-22 20:20:39 -03:00

184 lines
4.8 KiB
C++

/* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MinecraftUpdate.h"
#include "MinecraftInstance.h"
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QDataStream>
#include "BaseInstance.h"
#include "minecraft/PackProfile.h"
#include "minecraft/Library.h"
#include <FileSystem.h>
#include "update/FoldersTask.h"
#include "update/LibrariesTask.h"
#include "update/FMLLibrariesTask.h"
#include "update/AssetUpdateTask.h"
#include <meta/Index.h>
#include <meta/Version.h>
MinecraftUpdate::MinecraftUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
}
void MinecraftUpdate::executeTask()
{
m_tasks.clear();
// create folders
{
m_tasks.append(std::make_shared<FoldersTask>(m_inst));
}
// add metadata update task if necessary
{
auto components = m_inst->getPackProfile();
components->reload(Net::Mode::Online);
auto task = components->getCurrentTask();
if(task)
{
m_tasks.append(task.unwrap());
}
}
// libraries download
{
m_tasks.append(std::make_shared<LibrariesTask>(m_inst));
}
// FML libraries download and copy into the instance
{
m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst));
}
// assets update
{
m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
}
if(!m_preFailure.isEmpty())
{
emitFailed(m_preFailure);
return;
}
next();
}
void MinecraftUpdate::next()
{
if(m_abort)
{
emitFailed(tr("Aborted by user."));
return;
}
if(m_failed_out_of_order)
{
emitFailed(m_fail_reason);
return;
}
m_currentTask ++;
if(m_currentTask > 0)
{
auto task = m_tasks[m_currentTask - 1];
disconnect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
disconnect(task.get(), &Task::aborted, this, &Task::abort);
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
}
if(m_currentTask == m_tasks.size())
{
emitSucceeded();
return;
}
auto task = m_tasks[m_currentTask];
// if the task is already finished by the time we look at it, skip it
if(task->isFinished())
{
qCritical() << "MinecraftUpdate: Skipping finished subtask" << m_currentTask << ":" << task.get();
next();
}
connect(task.get(), &Task::succeeded, this, &MinecraftUpdate::subtaskSucceeded);
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
connect(task.get(), &Task::aborted, this, &Task::abort);
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
// if the task is already running, do not start it again
if(!task->isRunning())
{
task->start();
}
}
void MinecraftUpdate::subtaskSucceeded()
{
if(isFinished())
{
qCritical() << "MinecraftUpdate: Subtask" << sender() << "succeeded, but work was already done!";
return;
}
auto senderTask = QObject::sender();
auto currentTask = m_tasks[m_currentTask].get();
if(senderTask != currentTask)
{
qDebug() << "MinecraftUpdate: Subtask" << sender() << "succeeded out of order.";
return;
}
next();
}
void MinecraftUpdate::subtaskFailed(QString error)
{
if(isFinished())
{
qCritical() << "MinecraftUpdate: Subtask" << sender() << "failed, but work was already done!";
return;
}
auto senderTask = QObject::sender();
auto currentTask = m_tasks[m_currentTask].get();
if(senderTask != currentTask)
{
qDebug() << "MinecraftUpdate: Subtask" << sender() << "failed out of order.";
m_failed_out_of_order = true;
m_fail_reason = error;
return;
}
emitFailed(error);
}
bool MinecraftUpdate::abort()
{
if(!m_abort)
{
m_abort = true;
auto task = m_tasks[m_currentTask];
if(task->canAbort())
{
return task->abort();
}
}
return true;
}
bool MinecraftUpdate::canAbort() const
{
return true;
}