97 lines
4.6 KiB
C++
97 lines
4.6 KiB
C++
|
|
#pragma once
|
|||
|
|
|
|||
|
|
#include "modloader.h"
|
|||
|
|
|
|||
|
|
#include <QDateTime>
|
|||
|
|
#include <QHash>
|
|||
|
|
#include <QList>
|
|||
|
|
#include <QObject>
|
|||
|
|
#include <QSet>
|
|||
|
|
#include <QString>
|
|||
|
|
|
|||
|
|
#include <functional>
|
|||
|
|
|
|||
|
|
class QNetworkAccessManager;
|
|||
|
|
|
|||
|
|
// Списки версий модлоадеров: качает, кэширует в папке лаунчера и отдаёт из
|
|||
|
|
// кэша, пока тот не устарел. Устроен как VersionManifestService и работает в
|
|||
|
|
// GUI-потоке.
|
|||
|
|
//
|
|||
|
|
// Совместимость с версией игры не проверяется отдельной логикой, а заложена в
|
|||
|
|
// структуру данных: Fabric и Quilt отдают список сразу под нужную версию игры,
|
|||
|
|
// а maven-metadata Forge и NeoForge раскладывается по версиям игры при разборе.
|
|||
|
|
// Версии игры, под которую сборок нет, соответствует пустой список — выбрать
|
|||
|
|
// несовместимый лоадер физически нечем.
|
|||
|
|
class ModLoaderVersionService : public QObject
|
|||
|
|
{
|
|||
|
|
Q_OBJECT
|
|||
|
|
|
|||
|
|
public:
|
|||
|
|
// ok = true и непустой warning — данные отдали из устаревшего кэша.
|
|||
|
|
using Callback = std::function<void(bool ok, const QString &warning)>;
|
|||
|
|
|
|||
|
|
explicit ModLoaderVersionService(QObject *parent = nullptr);
|
|||
|
|
|
|||
|
|
// Единственная точка входа. Свежий кэш — колбэк отложенно, иначе один
|
|||
|
|
// сетевой запрос на всех, кто успел попросить.
|
|||
|
|
void ensureLoaded(ModLoader loader,
|
|||
|
|
const QString &gameVersion,
|
|||
|
|
Callback callback,
|
|||
|
|
bool forceRefresh = false);
|
|||
|
|
|
|||
|
|
// Новые сборки сверху: первая строка — самая свежая для этой версии игры.
|
|||
|
|
QList<LoaderVersionEntry> versions(ModLoader loader, const QString &gameVersion) const;
|
|||
|
|
bool isRefreshing(ModLoader loader, const QString &gameVersion) const;
|
|||
|
|
|
|||
|
|
// Запись по версии лоадера — из неё установщик берёт ссылку на installer.jar.
|
|||
|
|
std::optional<LoaderVersionEntry> find(ModLoader loader,
|
|||
|
|
const QString &gameVersion,
|
|||
|
|
const QString &loaderVersion) const;
|
|||
|
|
|
|||
|
|
signals:
|
|||
|
|
void versionsChanged(const QString &loaderKey, const QString &gameVersion);
|
|||
|
|
void refreshingChanged();
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
// Fabric и Quilt спрашиваются по каждой версии игры отдельно, Forge и
|
|||
|
|
// NeoForge — одним maven-metadata на все версии сразу. Отсюда два вида
|
|||
|
|
// ключа свежести: у первых — на версию игры, у вторых — на весь лоадер.
|
|||
|
|
static bool isPerGameVersion(ModLoader loader);
|
|||
|
|
|
|||
|
|
struct Bucket
|
|||
|
|
{
|
|||
|
|
QHash<QString, QList<LoaderVersionEntry>> byGame;
|
|||
|
|
QHash<QString, QDateTime> fetchedAtByGame; // Fabric, Quilt
|
|||
|
|
QDateTime fetchedAt; // Forge, NeoForge
|
|||
|
|
bool cacheRead = false;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
Bucket &bucket(ModLoader loader);
|
|||
|
|
const Bucket &bucket(ModLoader loader) const;
|
|||
|
|
|
|||
|
|
static QString requestKey(ModLoader loader, const QString &gameVersion);
|
|||
|
|
bool isFresh(ModLoader loader, const QString &gameVersion) const;
|
|||
|
|
|
|||
|
|
bool readCache(ModLoader loader);
|
|||
|
|
void writeCache(ModLoader loader);
|
|||
|
|
void startRefresh(ModLoader loader, const QString &gameVersion);
|
|||
|
|
void finishRefresh(ModLoader loader,
|
|||
|
|
const QString &gameVersion,
|
|||
|
|
bool ok,
|
|||
|
|
const QString &warning);
|
|||
|
|
|
|||
|
|
// Разбор ответов. Возвращают пустое при испорченном ответе — хороший кэш им
|
|||
|
|
// не затирается.
|
|||
|
|
static QList<LoaderVersionEntry> parseFabricLike(const QByteArray &payload,
|
|||
|
|
ModLoader loader,
|
|||
|
|
const QString &gameVersion);
|
|||
|
|
static QHash<QString, QList<LoaderVersionEntry>> parseMavenMetadata(const QByteArray &xml,
|
|||
|
|
ModLoader loader,
|
|||
|
|
bool legacyNeoForge);
|
|||
|
|
|
|||
|
|
QNetworkAccessManager *m_network;
|
|||
|
|
QHash<int, Bucket> m_buckets;
|
|||
|
|
QHash<QString, QList<Callback>> m_pending;
|
|||
|
|
QSet<QString> m_inFlight;
|
|||
|
|
};
|