# JavaRuntimeService ## Обзор класса `JavaRuntimeService` — каталог сборок Java, которые лаунчер умеет скачать: качает, кэширует в папке лаунчера и отдаёт из кэша, пока тот не устарел. Устроен так же, как [VersionManifestService](VersionManifestService.md). Источников два, и они дополняют друг друга. **Mojang** (категория «Java») — ровно тот рантайм, которым запускает игру официальный лаунчер: версий немного, зато они заведомо совместимы. **Eclipse Temurin** (категории JDK и JRE) — свежие сборки всех мажорных версий, включая те, до которых Mojang ещё не дошёл. Отдаются только сборки под текущие операционную систему и архитектуру: выбрать заведомо неработающую нечем. Исключение — macOS на Apple Silicon, где под Java 8 и 16 сборок `aarch64` нет вовсе и приходится брать `x64`, который работает через Rosetta. ## Место в проекте и зависимости Подключает [javaruntime.h](javaruntime.md): перечисление `JavaRuntimeKind` и структура `JavaRuntimeEntry` приходят оттуда. Экземпляр создаётся и принадлежит [LauncherBackend](LauncherBackend.md). Записями каталога пользуется [JavaInstaller](JavaInstaller.md). Путь к файлу кэша даёт `LauncherPaths::javaCatalogFile()` из [launcherpaths.h](launcherpaths.md). Требования сборки: `Qt6::Core` (`QDateTime`, `QHash`, `QSet`) и `Qt6::Network` (`QNetworkAccessManager`). ## Иерархия и роль Наследует `QObject`: мета-объектная система, два сигнала и владение по родителю. Виртуальных методов базового класса не переопределяет. ## Псевдонимы типов `JavaRuntimeService::Callback` — `std::function`. Как и в остальных каталогах лаунчера, `ok == true` с непустым `warning` означает данные из устаревшего кэша. ## Публичные методы #### explicit JavaRuntimeService(QObject \*parent = nullptr) Создаёт сервис и его `QNetworkAccessManager`. Кэш читается лениво. Конструктор помечен `explicit`. #### void ensureLoaded(Callback callback, bool forceRefresh = false) Единственная точка входа. Свежий кэш означает отложенный вызов колбэка без сети; иначе запускается обновление. Параметр `forceRefresh` обходит проверку свежести — так работает кнопка обновления каталога. Одно обновление складывается из нескольких запросов сразу к обоим источникам. Ответы приходят вразнобой, поэтому они накапливаются, и каталог подменяется целиком только когда пришли все. #### QList<JavaRuntimeEntry> entries(JavaRuntimeKind kind) const Сборки одной категории: `Mojang`, `Jdk` или `Jre`. Новые версии идут первыми. #### std::optional<JavaRuntimeEntry> find(const QString &id) const Запись по идентификатору сборки; `std::nullopt`, если такой нет. Поиск идёт по внутреннему указателю. #### std::optional<JavaRuntimeEntry> bestFor(int major) const Что скачать, если для версии игры нужна Java указанной мажорной версии, а подходящей в системе нет. Правила выбора: точное совпадение мажорной версии предпочтительнее более новой, а JDK предпочтительнее JRE — установщики Forge и NeoForge иногда требуют инструментов из полного комплекта. `std::nullopt` означает, что предложить нечего. #### bool isRefreshing() const Идут ли сейчас запросы. Признак остаётся истинным, пока не завершится последний из них. #### bool hasData() const Есть ли в каталоге хоть что-то — из сети или из кэша. ## Сигналы #### catalogChanged() Каталог заменён новыми данными. Испускается один раз за обновление, когда пришли ответы от всех источников, а не по каждому из них. Обработчик перечитывает `entries()` для нужных категорий и обновляет интерфейс. #### refreshingChanged() Изменился признак обновления. Обработчик показывает или убирает индикатор загрузки. ## Владение и время жизни Класс наследует `QObject` и принимает `parent` — родитель его и удалит. `QNetworkAccessManager` создаётся в конструкторе с сервисом в роли родителя. Отложенные колбэки хранятся до завершения текущего обновления. Уничтожение сервиса с незавершёнными запросами обрывает их вместе с менеджером сети, и колбэки не вызываются. ## Потокобезопасность Только поток GUI. Чтение и запись кэша выполняются синхронно в вызывающем потоке. ## Взаимодействие с другими классами `LauncherBackend` вызывает `ensureLoaded()` при открытии окна выбора Java и по кнопке обновления, а сигналы переправляет в свойства, которые читает QML. Перед отдачей в интерфейс он сводит записи каталога с уже установленными сборками из `JavaRuntimeStore` — окно выбора показывает статус, не считая ничего само. [JavaInstaller](JavaInstaller.md) получает запись каталога и по ней скачивает и распаковывает сборку. `bestFor()` используется, когда для запуска не хватает Java и лаунчер должен сам предложить, что поставить. Требования версий игры к Java берутся из пространства имён `JavaRequirement` в [javaruntime.h](javaruntime.md). ## Внешнее взаимодействие **Сеть, исходящие запросы.** Класс обращается к двум внешним каталогам: API Adoptium (сборки Temurin) и списку рантаймов Mojang. Формат обоих — JSON поверх HTTPS, разбор разделён на две функции. Запросов на одно обновление несколько: список сборок Temurin запрашивается по мажорным версиям, и на macOS с Apple Silicon неудачный запрос сборки `aarch64` может быть переспрошен для `x64` — это и есть тот самый случай Java 8 и 16. Общий счётчик незавершённых запросов сводит их воедино: пока он не обнулился, каталог не подменяется, а предупреждения от отдельных источников накапливаются. Все сигналы и колбэки приходят в поток GUI. ## Пример использования ```cpp auto *javaCatalog = new JavaRuntimeService(this); connect(javaCatalog, &JavaRuntimeService::catalogChanged, this, &Backend::rebuildJavaCatalog); javaCatalog->ensureLoaded([this, javaCatalog](bool ok, const QString &warning) { if (!ok) { emit javaCatalogError(warning); return; } const auto best = javaCatalog->bestFor(version.javaMajor); if (best) emit suggestJavaInstall(best->id, best->version); }); ``` --- При создании этого документа использовался ИИ.