153 lines
9.5 KiB
Markdown
153 lines
9.5 KiB
Markdown
|
|
# ModLoaderVersionService
|
|||
|
|
|
|||
|
|
## Обзор класса
|
|||
|
|
|
|||
|
|
Каждый модлоадер публикует свой список версий, и у каждого он устроен по-своему. `ModLoaderVersionService`
|
|||
|
|
приводит все четыре к одному виду: качает списки, кэширует в папке лаунчера и отдаёт из кэша, пока
|
|||
|
|
тот не устарел. Устроен так же, как [VersionManifestService](VersionManifestService.md).
|
|||
|
|
|
|||
|
|
Главная особенность класса — в том, чего в нём нет: отдельной проверки совместимости с версией
|
|||
|
|
игры. Совместимость заложена в структуру данных. Fabric и Quilt отдают список сразу под нужную
|
|||
|
|
версию игры, а `maven-metadata` Forge и NeoForge раскладывается по версиям игры при разборе.
|
|||
|
|
Версии игры, под которую сборок нет, соответствует пустой список — выбрать несовместимый лоадер
|
|||
|
|
физически нечем.
|
|||
|
|
|
|||
|
|
## Место в проекте и зависимости
|
|||
|
|
|
|||
|
|
Подключает [modloader.h](modloader.md): перечисление `ModLoader` и структура `LoaderVersionEntry`
|
|||
|
|
приходят оттуда.
|
|||
|
|
|
|||
|
|
Экземпляр создаётся и принадлежит [LauncherBackend](LauncherBackend.md). Ссылку на него получает
|
|||
|
|
[ModLoaderInstaller](ModLoaderInstaller.md) — из записи списка он берёт адрес `installer.jar`.
|
|||
|
|
|
|||
|
|
Путь к файлу кэша даёт `LauncherPaths::loaderCacheFile()` из [launcherpaths.h](launcherpaths.md).
|
|||
|
|
|
|||
|
|
Требования сборки: `Qt6::Core` (`QDateTime`, `QHash`, `QSet`) и `Qt6::Network`
|
|||
|
|
(`QNetworkAccessManager`).
|
|||
|
|
|
|||
|
|
## Иерархия и роль
|
|||
|
|
|
|||
|
|
Наследует `QObject`: мета-объектная система, два сигнала и владение по родителю. Виртуальных
|
|||
|
|
методов базового класса не переопределяет.
|
|||
|
|
|
|||
|
|
## Псевдонимы типов
|
|||
|
|
|
|||
|
|
`ModLoaderVersionService::Callback` — `std::function<void(bool ok, const QString &warning)>`.
|
|||
|
|
Как и у сервиса манифеста, `ok == true` с непустым `warning` означает, что данные отдали из
|
|||
|
|
устаревшего кэша.
|
|||
|
|
|
|||
|
|
## Публичные методы
|
|||
|
|
|
|||
|
|
#### explicit ModLoaderVersionService(QObject \*parent = nullptr)
|
|||
|
|
|
|||
|
|
Создаёт сервис и его `QNetworkAccessManager`. Кэши читаются лениво, при первом обращении к
|
|||
|
|
конкретному лоадеру. Конструктор помечен `explicit`.
|
|||
|
|
|
|||
|
|
#### void ensureLoaded(ModLoader loader, const QString &gameVersion, Callback callback, bool forceRefresh = false)
|
|||
|
|
|
|||
|
|
Единственная точка входа. Свежий кэш означает отложенный вызов колбэка без сети; иначе запускается
|
|||
|
|
один сетевой запрос на всех, кто успел попросить.
|
|||
|
|
|
|||
|
|
Свежесть считается по-разному в зависимости от лоадера: Fabric и Quilt спрашиваются по каждой
|
|||
|
|
версии игры отдельно, поэтому отметка времени у них своя на каждую версию; Forge и NeoForge
|
|||
|
|
приходят одним `maven-metadata` на все версии сразу, и отметка у них одна на весь лоадер.
|
|||
|
|
|
|||
|
|
Параметр `forceRefresh` обходит проверку свежести.
|
|||
|
|
|
|||
|
|
#### QList<LoaderVersionEntry> versions(ModLoader loader, const QString &gameVersion) const
|
|||
|
|
|
|||
|
|
Список сборок лоадера под конкретную версию игры. Новые сборки идут первыми, поэтому первая строка
|
|||
|
|
— самая свежая; именно её интерфейс подставляет по умолчанию.
|
|||
|
|
|
|||
|
|
Пустой список означает, что лоадер эту версию игры не поддерживает.
|
|||
|
|
|
|||
|
|
#### bool isRefreshing(ModLoader loader, const QString &gameVersion) const
|
|||
|
|
|
|||
|
|
Идёт ли сейчас запрос по этой паре. Интерфейс по этому признаку отличает «ещё грузим» от «не
|
|||
|
|
поддерживается» — оба случая выглядят пустым списком.
|
|||
|
|
|
|||
|
|
#### std::optional<LoaderVersionEntry> find(ModLoader loader, const QString &gameVersion, const QString &loaderVersion) const
|
|||
|
|
|
|||
|
|
Запись по версии лоадера; `std::nullopt`, если такой нет. Из неё установщик берёт ссылку на
|
|||
|
|
`installer.jar`.
|
|||
|
|
|
|||
|
|
## Сигналы
|
|||
|
|
|
|||
|
|
#### versionsChanged(const QString &loaderKey, const QString &gameVersion)
|
|||
|
|
|
|||
|
|
Список версий изменился. Параметры сужают событие до конкретной пары: `loaderKey` принимает
|
|||
|
|
значения `forge`, `fabric`, `neoforge`, `quilt`.
|
|||
|
|
|
|||
|
|
Обработчик должен сверить оба параметра со своим текущим состоянием и перечитать `versions()`,
|
|||
|
|
только если они совпадают, — иначе обновление относится к другой строке лоадера. Именно так
|
|||
|
|
поступает [LoaderRow](../qml/LoaderRow.md).
|
|||
|
|
|
|||
|
|
#### refreshingChanged()
|
|||
|
|
|
|||
|
|
Изменился признак сетевого обновления у какой-либо пары. Обработчик перечитывает
|
|||
|
|
`isRefreshing()` для интересующей его пары.
|
|||
|
|
|
|||
|
|
## Владение и время жизни
|
|||
|
|
|
|||
|
|
Класс наследует `QObject` и принимает `parent` — родитель его и удалит.
|
|||
|
|
`QNetworkAccessManager` создаётся в конструкторе с сервисом в роли родителя.
|
|||
|
|
|
|||
|
|
Отложенные колбэки хранятся по ключу запроса до завершения соответствующего обращения к сети.
|
|||
|
|
Уничтожение сервиса с незавершённым запросом обрывает его, и накопленные колбэки не вызываются.
|
|||
|
|
|
|||
|
|
Ссылку на сервис держит установщик модлоадеров; уничтожать сервис раньше установщика нельзя.
|
|||
|
|
|
|||
|
|
## Потокобезопасность
|
|||
|
|
|
|||
|
|
Только поток GUI. Чтение и запись кэша выполняются синхронно в вызывающем потоке.
|
|||
|
|
|
|||
|
|
## Взаимодействие с другими классами
|
|||
|
|
|
|||
|
|
`LauncherBackend` оборачивает сервис тремя методами, доступными из QML: получить список, запросить
|
|||
|
|
обновление и узнать, идёт ли загрузка. Сигнал `versionsChanged` он переправляет в QML под тем же
|
|||
|
|
именем, поэтому строка лоадера в карточке сборки подписывается прямо на него.
|
|||
|
|
|
|||
|
|
[ModLoaderInstaller](ModLoaderInstaller.md) обращается к `find()` за адресом установщика перед
|
|||
|
|
началом установки.
|
|||
|
|
|
|||
|
|
## Внешнее взаимодействие
|
|||
|
|
|
|||
|
|
**Сеть, исходящие запросы.** Класс обращается к четырём разным источникам метаданных, и форматы
|
|||
|
|
ответов различаются: у Fabric и Quilt это JSON, у Forge и NeoForge — XML `maven-metadata`. Разбор
|
|||
|
|
разделён на две функции соответственно.
|
|||
|
|
|
|||
|
|
Испорченный ответ разбирается в пустой результат, и хороший кэш им не затирается — это сознательное
|
|||
|
|
решение: лучше показать вчерашний список, чем стереть его из-за сбоя на сервере.
|
|||
|
|
|
|||
|
|
При недоступной сети данные отдаются из устаревшего кэша с `ok == true` и заполненным `warning`.
|
|||
|
|
|
|||
|
|
Все сигналы и колбэки приходят в поток GUI.
|
|||
|
|
|
|||
|
|
## Пример использования
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
auto *loaders = new ModLoaderVersionService(this);
|
|||
|
|
|
|||
|
|
connect(loaders, &ModLoaderVersionService::versionsChanged,
|
|||
|
|
this, [this](const QString &key, const QString &game) {
|
|||
|
|
if (key == loaderKey(ModLoader::Fabric) && game == m_gameVersion)
|
|||
|
|
emit fabricVersionsChanged();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
loaders->ensureLoaded(ModLoader::Fabric, QStringLiteral("1.21.1"),
|
|||
|
|
[this, loaders](bool ok, const QString &warning) {
|
|||
|
|
if (!ok) {
|
|||
|
|
showStatus(warning);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const auto list = loaders->versions(ModLoader::Fabric,
|
|||
|
|
QStringLiteral("1.21.1"));
|
|||
|
|
if (!list.isEmpty())
|
|||
|
|
selectVersion(list.first().loaderVersion);
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
При создании этого документа использовался ИИ.
|