new docs for project

This commit is contained in:
2026-09-03 09:16:56 +03:00
parent f1a840174b
commit 00e7c957e4
34 changed files with 5323 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
# ModLoaderInstaller
## Обзор класса
`ModLoaderInstaller` ставит модлоадер в `.minecraft`. Под одним фасадом он прячет два совершенно
разных пути установки.
**Fabric и Quilt** отдают готовое описание версии: лаунчер кладёт его в
`versions/<id>/<id>.json` и передаёт дальше [VersionInstaller](VersionInstaller.md), который по
полю `inheritsFrom` сам поставит ванильную версию и библиотеки лоадера.
**Forge и NeoForge** так не умеют: их установка — это патч клиентского jar. Поэтому лаунчер
запускает официальный `installer.jar` найденной Java в headless-режиме и смотрит, какой профиль
появился в `versions`. Процессу установщика при этом подсовывается эталонный zlib — см.
[ZlibReference](zlibreference.md).
Оба пути начинаются одинаково: пока ванильная версия не скачана целиком, ставить лоадер некуда.
Набор геттеров прогресса намеренно повторяет `VersionInstaller`: панель загрузки в интерфейсе
читает их одинаково, независимо от того, кто сейчас работает.
## Место в проекте и зависимости
Подключает [modloader.h](modloader.md). В конструктор принимает
[ModLoaderVersionService](ModLoaderVersionService.md) — за адресом установщика — и
[VersionInstaller](VersionInstaller.md) — за базовой версией и за докачиванием того, что
`installer.jar` не положил.
Экземпляр создаётся и принадлежит [LauncherBackend](LauncherBackend.md).
Требования сборки: `Qt6::Core` (`QProcess`, `QFile`) и `Qt6::Network` (`QNetworkAccessManager`,
`QNetworkReply`).
## Иерархия и роль
Наследует `QObject`: мета-объектная система, шесть сигналов и владение по родителю. Объявлен
виртуальный деструктор — класс владеет процессом установщика и открытым файлом журнала.
## Публичные методы
#### ModLoaderInstaller(ModLoaderVersionService \*meta, VersionInstaller \*versionInstaller, QObject \*parent = nullptr)
Создаёт установщик поверх двух сервисов. Ни один из них не переходит во владение установщика —
оба обязаны пережить его.
#### bool isRunning() const
Идёт ли установка прямо сейчас.
#### QString label() const
Подпись текущей установки для интерфейса — название лоадера с версиями.
#### QString stage() const
Текущий этап словами.
#### QString currentFile() const
Файл, который обрабатывается сейчас.
#### qint64 bytesDone() const
Сколько байт уже получено.
#### qint64 bytesTotal() const
Ожидаемый общий объём; `0` — неизвестен.
#### double fraction() const
Доля выполнения от `0` до `1` либо `-1`, пока итог неизвестен. У пути с `installer.jar` доля
почти всё время равна `-1`: сколько работы осталось внутри чужого процесса, лаунчер не знает.
#### void install(const QString &gameDir, ModLoader loader, const QString &gameVersion, const QString &loaderVersion, const QString &javaPreference)
Ставит модлоадер. Параметр `javaPreference` — путь к java, указанный пользователем в настройках;
он проверяется первым, а при пустом или неподходящем значении java ищется сама. Java нужна даже на
пути Fabric и Quilt, потому что перед установкой лоадера скачивается базовая версия игры.
Перед запуском `installer.jar` установщик записывает заглушку `launcher_profiles.json`: без этого
файла официальные установщики Forge и NeoForge отказываются работать.
#### void cancel()
Отменяет установку. Если `installer.jar` уже успел отработать, за появившиеся в `versions` папки
отвечает лаунчер, и при отмене они убираются.
## Сигналы
#### started(const QString &label)
Установка началась. Обработчик показывает панель прогресса.
#### progressChanged()
Изменились числа прогресса. Обработчик перечитывает геттеры — тот же набор, что у
[VersionInstaller](VersionInstaller.md).
#### finished(const QString &loaderKey, const QString &gameVersion, const QString &loaderVersion, const QString &producedVersionId)
Модлоадер установлен. Последний параметр — идентификатор появившегося профиля `versions/<id>`.
Он важен именно для Forge: заранее этот идентификатор неизвестен, поэтому новый профиль ищется
разницей между списком версий до и после запуска установщика, а не угадыванием строки —
идентификаторы Forge отличаются по эпохам.
Обработчик записывает `producedVersionId` в сборку: именно этот профиль будет запускаться.
#### failed(const QString &label, const QString &message)
Установка не удалась. Текст ошибки по возможности объясняет причину: отдельно распознаётся случай,
когда в системе стоит zlib-ng, а подменить его было нечем — это почти наверняка причина
расхождения sha1 у Forge, и о ней стоит сказать прямым текстом.
#### canceled(const QString &label)
Установка отменена пользователем.
#### log(const QString &line)
Строка вывода процесса `installer.jar`. Обработчик пишет её в журнал; вывод также сохраняется в
файл, чтобы разбираться с неудачной установкой после закрытия лаунчера.
## Владение и время жизни
Класс наследует `QObject` и принимает `parent` — родитель его и удалит.
Указатели на `ModLoaderVersionService` и `VersionInstaller`, переданные в конструктор, **не
принадлежат** установщику: оба сервиса создаются раньше и живут дольше.
`QNetworkAccessManager` создаётся в конструкторе с установщиком в роли родителя; сетевой ответ,
процесс установщика и файл журнала создаются по ходу работы и закрываются в деструкторе.
Установка асинхронна и состоит из вложенных продолжений: `ensureBaseVersion()` принимает функцию,
которая будет вызвана после появления базовой версии. Уничтожение установщика посреди этой цепочки
обрывает её.
## Потокобезопасность
Только поток GUI. Дочерний процесс `installer.jar` работает параллельно, но общение с ним идёт
через сигналы `QProcess`, которые приходят в поток GUI.
## Взаимодействие с другими классами
`LauncherBackend` вызывает `install()` при установке сборки с модлоадером и переправляет сигналы
прогресса в те же свойства, что и у `VersionInstaller`, — панель загрузки не различает, кто
работает. По сигналу `finished` бэкенд записывает `producedVersionId` в поле сборки
`resolvedVersionId`, которое до установки пусто и означает запуск на чистой ванили.
[ModLoaderVersionService](ModLoaderVersionService.md) даёт адрес `installer.jar`,
[VersionInstaller](VersionInstaller.md) ставит базовую версию до начала работы и докачивает
недостающие библиотеки после неё, [ZlibReference](zlibreference.md) готовит окружение процесса.
## Внешнее взаимодействие
**Сеть, исходящие запросы.** Загрузка `installer.jar` по HTTPS для Forge и NeoForge. Для Fabric и
Quilt сеть используется только через сервис версий и `VersionInstaller`.
**Дочерний процесс.** `installer.jar` запускается java в headless-режиме через `QProcess`.
Окружение процесса готовится `ZlibReference::applyTo()`: на Linux с zlib-ng туда дописывается
`LD_PRELOAD` с эталонной библиотекой, иначе установщик Forge падает с сообщением «Processor
failed, invalid outputs» — он сверяет sha1 собранных им же jar-файлов с эталоном, посчитанным на
обычном zlib.
Вывод процесса читается построчно, отдаётся сигналом `log` и параллельно пишется в файл. Результат
установки определяется не кодом выхода, а появлением новой папки в `versions`.
Все сигналы приходят в поток GUI.
## Пример использования
```cpp
auto *loaderInstaller = new ModLoaderInstaller(loaderVersions, versionInstaller, this);
connect(loaderInstaller, &ModLoaderInstaller::finished, this,
[this](const QString &key, const QString &game,
const QString &loaderVersion, const QString &producedVersionId) {
m_build.resolvedVersionId = producedVersionId;
saveBuilds();
});
connect(loaderInstaller, &ModLoaderInstaller::failed, this,
[this](const QString &, const QString &message) { emit launchError(message); });
loaderInstaller->install(gameDir, ModLoader::Forge,
QStringLiteral("1.20.1"), QStringLiteral("47.4.0"),
settings.javaPath);
```
---
При создании этого документа использовался ИИ.