Files
minecraft-launcher/modloaderversionservice.h
T

97 lines
4.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
};