documentation rewrite

Signed-off-by: galeon <dertiellis@gmail.com>
This commit is contained in:
galeon committed 2026-10-11 22:32:13 +03:00
1 parent 1943996608
commit f10b654953
34 files changed
+2099 -4576

No files matched your search

+45 -145
View File
@@ -1,167 +1,67 @@
# VersionManifestService
## Обзор класса
`versionmanifestservice.h`, `versionmanifestservice.cpp`
Каталог версий Minecraft — это манифест Mojang: около тысячи записей от альф 2010 года до
свежайших снапшотов. `VersionManifestService` отвечает за него целиком: скачивает манифест,
кэширует в папке лаунчера и отдаёт из кэша, пока тот не устарел.
Каталог версий Mojang. Скачивает `version_manifest_v2.json`, хранит его копию в папке лаунчера и
отвечает из неё, пока копия свежая.
Класс нужен двум потребителям: окну выбора версии, которому нужен весь список, и установщику,
которому по идентификатору версии нужна ссылка на её описание.
Экземпляр держит [LauncherBackend](LauncherBackend.md): из каталога он строит список для окна
выбора версии. [VersionInstaller](VersionInstaller.md) берёт из каталога ссылку на описание версии
через `find()`.
## Место в проекте и зависимости
## RemoteVersionEntry
Экземпляр создаётся и принадлежит [LauncherBackend](LauncherBackend.md). Ссылку на него получает
[VersionInstaller](VersionInstaller.md) — установщик берёт из манифеста адрес описания версии.
| Поле | Что это |
|------|---------|
| `id` | `1.21.8`, `25w33a`, `b1.7.3` |
| `type` | `release`, `snapshot`, `old_beta`, `old_alpha` |
| `url` | адрес `<id>.json` |
| `sha1` | хеш этого json |
| `releaseTime` | |
Путь к файлу кэша даёт `LauncherPaths::versionManifestFile()` из
[launcherpaths.h](launcherpaths.md).
Записи без `id` или с адресом не по `https` отбрасываются. Список отсортирован по `releaseTime`,
новые сверху. При одинаковом времени сортировка идёт по `id`.
Требования сборки: `Qt6::Core` (`QDateTime`, `QHash`, `QList`, `QUrl`) и `Qt6::Network`
(`QNetworkAccessManager`).
## ensureLoaded(callback, forceRefresh = false)
## Иерархия и роль
Через этот метод сервис получает данные; других способов нет.
Наследует `QObject`: мета-объектная система, два сигнала и владение по родителю. Виртуальных
методов базового класса не переопределяет.
1. При первом вызове читается кэш `<root>/cache/version_manifest.json`. Если он разобрался,
сразу испускается `versionsChanged`, и окно показывает список ещё до ответа сети.
2. Если данные моложе 6 часов и `forceRefresh` не задан, колбэк вызывается через
`QTimer::singleShot(0)` с `ok = true`. Сеть не трогается.
3. Иначе колбэк встаёт в очередь. Если запрос уже идёт, второй не отправляется.
## Публичные структуры
Ответ сети разбирается, кэш перезаписывается через `QSaveFile`, испускается `versionsChanged`, и
все колбэки из очереди получают результат.
### RemoteVersionEntry
Если сеть не ответила, но старые данные есть, колбэк получит `ok = true` и непустой `warning`:
работать можно, просто список, возможно, неполный. Если данных нет совсем, придёт `ok = false`.
Бэкенд показывает `warning` пользователю только во втором случае.
Одна строка манифеста Mojang.
Таймаут запроса 20 секунд.
| Поле | Тип | Описание |
|------|-----|----------|
| `id` | `QString` | Идентификатор версии: `1.21.8`, `25w33a`, `b1.7.3` |
| `type` | `QString` | Категория: `release`, `snapshot`, `old_beta` или `old_alpha` |
| `url` | `QUrl` | Адрес `<id>.json` с описанием версии |
| `sha1` | `QString` | Контрольная сумма самого описания |
| `releaseTime` | `QDateTime` | Дата выпуска; по ней список сортируется новыми вперёд |
## Остальное
Категории `type` — те же ключи, по которым окно выбора версии делит каталог на вкладки; версии, не
попавшие ни в одну из четырёх, интерфейс относит к категории «прочие».
- `versions()` — весь список.
- `find(id)` — запись по id (через хеш-таблицу, а не перебором).
- `hasData()`, `isRefreshing()`, `fetchedAt()`.
- `refreshingChanged` испускается в начале и в конце запроса. Бэкенд превращает его в
`catalogLoading`.
## Псевдонимы типов
## Формат кэша
`VersionManifestService::Callback` — `std::function<void(bool ok, const QString &warning)>`.
Сочетание `ok == true` с непустым `warning` означает особый случай: данные отдали, но из
устаревшего кэша — сеть недоступна, а показать что-то нужно.
## Публичные методы
#### explicit VersionManifestService(QObject \*parent = nullptr)
Создаёт сервис и его `QNetworkAccessManager`. Манифест при этом не читается — чтение кэша
откладывается до первого обращения. Конструктор помечен `explicit`.
#### QList&lt;RemoteVersionEntry&gt; versions() const
Текущий список версий. Возвращает копию; пустой список означает, что данных ещё нет.
#### bool hasData() const
Есть ли хоть какие-то данные — из сети или из кэша.
#### bool isRefreshing() const
Идёт ли сейчас сетевое обновление. Интерфейс показывает по этому признаку строку загрузки вместо
пустого списка.
#### QDateTime fetchedAt() const
Когда данные были получены. По этой отметке решается, устарел ли кэш.
#### std::optional&lt;RemoteVersionEntry&gt; find(const QString &id) const
Запись по идентификатору версии; `std::nullopt`, если такой версии в манифесте нет. Из неё
установщик берёт ссылку на описание версии. Поиск идёт по внутреннему указателю, а не перебором.
#### void ensureLoaded(Callback callback, bool forceRefresh = false)
Единственная точка входа. Свежий кэш означает отложенный вызов колбэка без обращения к сети; иначе
запускается один сетевой запрос на всех, кто успел попросить, — колбэки накапливаются и вызываются
все разом по его завершении.
Параметр `forceRefresh` обходит проверку свежести кэша: так работает кнопка принудительного
обновления.
Колбэк вызывается ровно один раз и всегда в потоке GUI, в том числе когда данные уже есть.
## Сигналы
#### versionsChanged()
Список версий изменился — пришли новые данные из сети или прочитан кэш.
Обработчик перечитывает `versions()` и обновляет интерфейс. В лаунчере на этот сигнал завязано
свойство каталога версий, которое читает окно выбора.
#### refreshingChanged()
Изменился признак сетевого обновления. Обработчик показывает или убирает индикатор загрузки.
## Владение и время жизни
Класс наследует `QObject` и принимает `parent` — родитель его и удалит.
`QNetworkAccessManager` создаётся в конструкторе с сервисом в роли родителя.
Отложенные колбэки хранятся в списке до завершения текущего запроса. Уничтожение сервиса с
незавершённым запросом обрывает его вместе с менеджером сети, и накопленные колбэки не
вызываются.
Ссылку на сервис держит установщик версий; уничтожать сервис раньше установщика нельзя.
## Потокобезопасность
Только поток GUI — так же, как [AuthService](AuthService.md). Чтение и запись кэша выполняются в
вызывающем потоке, поэтому первое обращение к манифесту делает короткую файловую операцию
синхронно.
## Взаимодействие с другими классами
`LauncherBackend` вызывает `ensureLoaded()` при открытии окна выбора версии и по кнопке
обновления, а сигналы `versionsChanged` и `refreshingChanged` переправляет в свойства, которые
читает QML. Список из `versions()` он сводит с установленными версиями и отдаёт в интерфейс уже
готовыми строками.
[VersionInstaller](VersionInstaller.md) обращается к `find()`, чтобы получить адрес описания
версии перед началом загрузки.
## Внешнее взаимодействие
**Сеть, исходящие запросы.** Класс скачивает манифест версий Mojang через
`QNetworkAccessManager`. Формат — JSON поверх HTTPS, запрос инициирует лаунчер.
Стратегия при недоступной сети встроена в контракт колбэка: если есть устаревший кэш, он
отдаётся с `ok == true` и заполненным `warning`, и интерфейс показывает список вместо ошибки.
Полное отсутствие данных даёт `ok == false`.
**Файловый кэш.** Манифест сохраняется в файл, путь к которому даёт
`LauncherPaths::versionManifestFile()`, вместе с отметкой времени получения.
Сигналы и колбэки приходят в поток GUI.
## Пример использования
```cpp
auto *manifest = new VersionManifestService(this);
connect(manifest, &VersionManifestService::versionsChanged, this, &Backend::rebuildCatalog);
manifest->ensureLoaded([this, manifest](bool ok, const QString &warning) {
if (!ok) {
emit catalogError(warning);
return;
}
if (!warning.isEmpty())
showStatus(warning); // список из устаревшего кэша
const auto entry = manifest->find(QStringLiteral("1.21.8"));
if (entry)
startDownload(entry->url);
});
```json
{
"fetchedAt": "2026-10-11T08:00:00Z",
"versions": [
{ "id": "1.21.8", "type": "release", "url": "https://...", "sha1": "...", "releaseTime": "..." }
]
}
```
Хранится только то, что нужно лаунчеру, а не весь манифест Mojang.
---
При создании этого документа использовался ИИ.