PrismLauncher/api/logic/net/NetJob.cpp
kb1000 8021fb25d0 GH-469 Implement support for importing and searching for Technic Platform and Solder modpacks
This does not support any custom modpack.jar for 1.6 or newer, it simply uses standard Forge then.
Supports Forge and Fabric, and JAR mods for 1.5 and older.
2020-10-13 21:34:16 +02:00

219 lines
5.9 KiB
C++

/* Copyright 2013-2019 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 "NetJob.h"
#include "Download.h"
#include <QDebug>
void NetJob::partSucceeded(int index)
{
// do progress. all slots are 1 in size at least
auto &slot = parts_progress[index];
partProgress(index, slot.total_progress, slot.total_progress);
m_doing.remove(index);
m_done.insert(index);
downloads[index].get()->disconnect(this);
startMoreParts();
}
void NetJob::partFailed(int index)
{
m_doing.remove(index);
auto &slot = parts_progress[index];
if (slot.failures == 3)
{
m_failed.insert(index);
}
else
{
slot.failures++;
m_todo.enqueue(index);
}
downloads[index].get()->disconnect(this);
startMoreParts();
}
void NetJob::partAborted(int index)
{
m_aborted = true;
m_doing.remove(index);
m_failed.insert(index);
downloads[index].get()->disconnect(this);
startMoreParts();
}
void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal)
{
auto &slot = parts_progress[index];
slot.current_progress = bytesReceived;
slot.total_progress = bytesTotal;
int done = m_done.size();
int doing = m_doing.size();
int all = parts_progress.size();
qint64 bytesAll = 0;
qint64 bytesTotalAll = 0;
for(auto & partIdx: m_doing)
{
auto part = parts_progress[partIdx];
// do not count parts with unknown/nonsensical total size
if(part.total_progress <= 0)
{
continue;
}
bytesAll += part.current_progress;
bytesTotalAll += part.total_progress;
}
qint64 inprogress = (bytesTotalAll == 0) ? 0 : (bytesAll * 1000) / bytesTotalAll;
auto current = done * 1000 + doing * inprogress;
auto current_total = all * 1000;
// HACK: make sure it never jumps backwards.
// FAIL: This breaks if the size is not known (or is it something else?) and jumps to 1000, so if it is 1000 reset it to inprogress
if(m_current_progress == 1000) {
m_current_progress = inprogress;
}
if(m_current_progress > current)
{
current = m_current_progress;
}
m_current_progress = current;
setProgress(current, current_total);
}
void NetJob::executeTask()
{
// hack that delays early failures so they can be caught easier
QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection);
}
void NetJob::startMoreParts()
{
if(!isRunning())
{
// this actually makes sense. You can put running downloads into a NetJob and then not start it until much later.
return;
}
// OK. We are actively processing tasks, proceed.
// Check for final conditions if there's nothing in the queue.
if(!m_todo.size())
{
if(!m_doing.size())
{
if(!m_failed.size())
{
emitSucceeded();
}
else if(m_aborted)
{
emitAborted();
}
else
{
emitFailed(tr("Job '%1' failed to process:\n%2").arg(objectName()).arg(getFailedFiles().join("\n")));
}
}
return;
}
// There's work to do, try to start more parts.
while (m_doing.size() < 6)
{
if(!m_todo.size())
return;
int doThis = m_todo.dequeue();
m_doing.insert(doThis);
auto part = downloads[doThis];
// connect signals :D
connect(part.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
connect(part.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
connect(part.get(), SIGNAL(aborted(int)), SLOT(partAborted(int)));
connect(part.get(), SIGNAL(netActionProgress(int, qint64, qint64)),
SLOT(partProgress(int, qint64, qint64)));
part->start();
}
}
QStringList NetJob::getFailedFiles()
{
QStringList failed;
for (auto index: m_failed)
{
failed.push_back(downloads[index]->url().toString());
}
failed.sort();
return failed;
}
bool NetJob::canAbort() const
{
bool canFullyAbort = true;
// can abort the waiting?
for(auto index: m_todo)
{
auto part = downloads[index];
canFullyAbort &= part->canAbort();
}
// can abort the active?
for(auto index: m_doing)
{
auto part = downloads[index];
canFullyAbort &= part->canAbort();
}
return canFullyAbort;
}
bool NetJob::abort()
{
bool fullyAborted = true;
// fail all waiting
m_failed.unite(m_todo.toSet());
m_todo.clear();
// abort active
auto toKill = m_doing.toList();
for(auto index: toKill)
{
auto part = downloads[index];
fullyAborted &= part->abort();
}
return fullyAborted;
}
bool NetJob::addNetAction(NetActionPtr action)
{
action->m_index_within_job = downloads.size();
downloads.append(action);
part_info pi;
parts_progress.append(pi);
partProgress(parts_progress.count() - 1, action->currentProgress(), action->totalProgress());
if(action->isRunning())
{
connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64)));
}
else
{
m_todo.append(parts_progress.size() - 1);
}
return true;
}
NetJob::~NetJob() = default;