148 lines
5.0 KiB
C++
148 lines
5.0 KiB
C++
|
|
#pragma once
|
|||
|
|
|
|||
|
|
#include "minecraftversion.h"
|
|||
|
|
|
|||
|
|
#include <QCryptographicHash>
|
|||
|
|
#include <QJsonObject>
|
|||
|
|
#include <QList>
|
|||
|
|
#include <QObject>
|
|||
|
|
#include <QPair>
|
|||
|
|
#include <QQueue>
|
|||
|
|
#include <QString>
|
|||
|
|
#include <QStringList>
|
|||
|
|
#include <QUrl>
|
|||
|
|
|
|||
|
|
#include <memory>
|
|||
|
|
|
|||
|
|
class QNetworkAccessManager;
|
|||
|
|
class QNetworkReply;
|
|||
|
|
class QSaveFile;
|
|||
|
|
class QTimer;
|
|||
|
|
class VersionManifestService;
|
|||
|
|
|
|||
|
|
// Один файл, который нужно положить в .minecraft.
|
|||
|
|
struct DownloadTask
|
|||
|
|
{
|
|||
|
|
enum Kind { ClientJar, Library, AssetIndex, AssetObject, LoggingConfig };
|
|||
|
|
|
|||
|
|
Kind kind = ClientJar;
|
|||
|
|
QUrl url;
|
|||
|
|
QString path; // абсолютный путь назначения
|
|||
|
|
QString sha1; // пусто — не проверяем
|
|||
|
|
qint64 size = 0; // 0 — размер неизвестен
|
|||
|
|
QString label; // что показать в панели прогресса
|
|||
|
|
int attempts = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Файл в процессе скачивания. Пишем потоком: держать десятки мегабайт в памяти
|
|||
|
|
// незачем, а нескольких параллельных загрузок хватило бы на сотни.
|
|||
|
|
struct ActiveDownload
|
|||
|
|
{
|
|||
|
|
DownloadTask task;
|
|||
|
|
QNetworkReply *reply = nullptr;
|
|||
|
|
std::unique_ptr<QSaveFile> file;
|
|||
|
|
QCryptographicHash hash{QCryptographicHash::Sha1};
|
|||
|
|
qint64 received = 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Фоновая установка версии в .minecraft: описание версии, клиентский jar,
|
|||
|
|
// библиотеки, индекс ресурсов и сами ресурсы. Одна версия за раз, остальные
|
|||
|
|
// ждут в очереди. Всё в GUI-потоке, как и остальной сетевой код лаунчера.
|
|||
|
|
class VersionInstaller : public QObject
|
|||
|
|
{
|
|||
|
|
Q_OBJECT
|
|||
|
|
|
|||
|
|
public:
|
|||
|
|
explicit VersionInstaller(VersionManifestService *manifest, QObject *parent = nullptr);
|
|||
|
|
~VersionInstaller() override;
|
|||
|
|
|
|||
|
|
bool isRunning() const { return m_running; }
|
|||
|
|
QString versionId() const { return m_versionId; }
|
|||
|
|
QString stage() const { return m_stage; }
|
|||
|
|
QString currentFile() const { return m_currentLabel; }
|
|||
|
|
|
|||
|
|
qint64 bytesDone() const;
|
|||
|
|
qint64 bytesTotal() const { return m_bytesTotal; }
|
|||
|
|
double fraction() const; // 0..1, либо -1 пока итог неизвестен
|
|||
|
|
|
|||
|
|
void install(const QString &gameDir, const QString &versionId);
|
|||
|
|
bool isQueued(const QString &versionId) const;
|
|||
|
|
void cancel();
|
|||
|
|
|
|||
|
|
signals:
|
|||
|
|
void started(const QString &versionId);
|
|||
|
|
void progressChanged(); // не чаще десяти раз в секунду
|
|||
|
|
void finished(const QString &versionId);
|
|||
|
|
void failed(const QString &versionId, const QString &message);
|
|||
|
|
void canceled(const QString &versionId);
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
void startNext();
|
|||
|
|
void resolveManifestEntry();
|
|||
|
|
void fetchVersionJson(const QUrl &url, const QString &expectedSha1);
|
|||
|
|
bool prepareTasks();
|
|||
|
|
void enqueue(DownloadTask task);
|
|||
|
|
|
|||
|
|
void schedulePump();
|
|||
|
|
void pump();
|
|||
|
|
void startTask(DownloadTask task);
|
|||
|
|
void handleReplyFinished(const std::shared_ptr<ActiveDownload> &context);
|
|||
|
|
bool retryOrFail(DownloadTask task, const QString &reason);
|
|||
|
|
void expandAssetIndex(const QString &indexPath);
|
|||
|
|
|
|||
|
|
void afterDownloads();
|
|||
|
|
void startLegacyCopy();
|
|||
|
|
void legacyTick();
|
|||
|
|
void complete();
|
|||
|
|
|
|||
|
|
void setStage(const QString &stage);
|
|||
|
|
void scheduleProgress();
|
|||
|
|
void flushProgress();
|
|||
|
|
|
|||
|
|
void resetInstallState();
|
|||
|
|
void failInstall(const QString &message);
|
|||
|
|
void abandonCurrent(); // версия уступает место родителю, без сигналов
|
|||
|
|
void finalizeCancel();
|
|||
|
|
|
|||
|
|
VersionManifestService *m_manifest;
|
|||
|
|
QNetworkAccessManager *m_network;
|
|||
|
|
|
|||
|
|
QQueue<QPair<QString, QString>> m_pendingInstalls; // {gameDir, versionId}
|
|||
|
|
QString m_gameDir;
|
|||
|
|
QString m_versionId;
|
|||
|
|
QJsonObject m_versionJson;
|
|||
|
|
MinecraftVersion m_version;
|
|||
|
|
|
|||
|
|
QQueue<DownloadTask> m_queue;
|
|||
|
|
QList<std::shared_ptr<ActiveDownload>> m_active;
|
|||
|
|
QStringList m_warnings;
|
|||
|
|
|
|||
|
|
qint64 m_bytesSettled = 0;
|
|||
|
|
qint64 m_bytesTotal = 0;
|
|||
|
|
qint64 m_assetsDeclaredTotal = 0;
|
|||
|
|
|
|||
|
|
bool m_running = false;
|
|||
|
|
bool m_canceling = false;
|
|||
|
|
bool m_pumpScheduled = false;
|
|||
|
|
// Существующие файлы сверяются только по размеру: перехеширование сотен
|
|||
|
|
// мегабайт при каждом добавлении версии дороже, чем риск битого файла.
|
|||
|
|
// Флаг оставлен под будущую кнопку «проверить файлы».
|
|||
|
|
bool m_verifyExisting = false;
|
|||
|
|
|
|||
|
|
QString m_stage;
|
|||
|
|
QString m_currentLabel;
|
|||
|
|
|
|||
|
|
QTimer *m_progressTimer;
|
|||
|
|
bool m_progressDirty = false;
|
|||
|
|
|
|||
|
|
// Ресурсы до 1.6 игра ищет не в assets/objects, а в плоской папке.
|
|||
|
|
QJsonObject m_assetObjects;
|
|||
|
|
bool m_assetsVirtual = false;
|
|||
|
|
bool m_assetsMapToResources = false;
|
|||
|
|
QList<QPair<QString, QString>> m_legacyCopies; // {источник, назначение}
|
|||
|
|
int m_legacyIndex = 0;
|
|||
|
|
QTimer *m_legacyTimer;
|
|||
|
|
|
|||
|
|
int m_inheritDepth = 0;
|
|||
|
|
};
|