# GameLauncher ## Обзор класса `GameLauncher` — то, ради чего существует весь остальной лаунчер: он готовит и запускает JVM с Minecraft. К моменту его вызова уже известно всё — версия разобрана, файлы скачаны, пользователь авторизован, — и класс превращает это в командную строку и дочерний процесс. Кроме самого запуска класс умеет проверять комплектность `.minecraft` и распаковывать нативные библиотеки, без которых игра не стартует. Один экземпляр — одна игра одновременно. ## Место в проекте и зависимости Подключает [minecraftversion.h](minecraftversion.md): разобранная версия — половина входных данных запуска, вторая половина приходит структурой `LaunchOptions` из этого же заголовка. Экземпляр создаётся и принадлежит [LauncherBackend](LauncherBackend.md), который заполняет `LaunchOptions` из настроек, выбранной сборки и результата авторизации. Требования сборки: `Qt6::Core` (`QProcess`, `QStringList`) и `Qt6::CorePrivate` — последний нужен ради `QZipReader`, которым распаковываются нативные библиотеки LWJGL. Зависимость от приватного модуля привязывает проект к конкретной версии Qt; в `CMakeLists.txt` это осознанный выбор, и предупреждение о нём отключено. ## Иерархия и роль Наследует `QObject`: мета-объектная система, четыре сигнала и владение по родителю. Виртуальных методов базового класса не переопределяет. ## Публичные структуры ### LaunchOptions Всё, что лаунчер знает к моменту нажатия кнопки запуска. | Поле | Тип | По умолчанию | Описание | |------|-----|--------------|----------| | `gameDir` | `QString` | — | Папка `.minecraft` | | `versionId` | `QString` | — | Папка в `versions`, которую запускаем | | `playerName` | `QString` | — | Ник игрока из результата авторизации | | `uuid` | `QString` | — | UUID игрока | | `accessToken` | `QString` | — | Токен доступа | | `userType` | `QString` | — | Тип учётной записи: `legacy`, `msa` или `ELYBY` | | `clientToken` | `QString` | — | Токен клиента | | `xuid` | `QString` | — | Идентификатор Xbox; пустое значение заменяется на `0`, как в офлайне | | `javaPath` | `QString` | — | Путь к java; пустое значение означает «искать самим» | | `minMemoryMb` | `int` | `512` | Значение `-Xms` | | `maxMemoryMb` | `int` | `4096` | Значение `-Xmx` | | `extraJvmArgs` | `QStringList` | — | Дополнительные аргументы JVM из настроек | | `windowWidth` | `int` | `0` | Ширина окна игры; `0` — не передавать `--width` и `--height` | | `windowHeight` | `int` | `0` | Высота окна игры | | `fullscreen` | `bool` | `false` | Запускать в полноэкранном режиме | | `serverAddress` | `QString` | — | `host[:port]` для автоматического захода на сервер | | `authlibInjectorPath` | `QString` | — | Путь к `authlib-injector`; пустое значение — не подключать | | `authlibInjectorApi` | `QString` | `ely.by` | Сервер авторизации, на который перенаправляется игра | | `launcherName` | `QString` | `KishkaLauncher` | Имя лаунчера, которое видит игра | | `launcherVersion` | `QString` | `1.0` | Версия лаунчера | ## Публичные методы #### explicit GameLauncher(QObject \*parent = nullptr) Создаёт объект. Процесс игры при этом не запускается. Конструктор помечен `explicit`. #### bool isRunning() const Идёт ли сейчас игра. От этого зависит доступность кнопки запуска в интерфейсе. #### static QStringList missingFiles(const LaunchOptions &options, const MinecraftVersion &version, int limit = 12) Проверяет `.minecraft` на комплектность и возвращает описания недостающих файлов. Пустой список означает, что всё на месте и сборку можно запускать. Параметр `limit` ограничивает длину списка: перечислять все отсутствующие файлы у неустановленной версии бессмысленно, важен сам факт и пара примеров. Метод статический, ничего не меняет и вызывается интерфейсом для строки состояния сборки. #### static QStringList buildArguments(const LaunchOptions &options, const MinecraftVersion &version, const QString &nativesDir) Собирает аргументы ровно в том порядке, в котором их ждёт JVM: сначала аргументы JVM, затем главный класс, затем аргументы игры. Подстановки вида `${...}` из версии заменяются значениями из параметров запуска. Метод статический и не имеет побочных эффектов, поэтому годится и для показа собранной командной строки без запуска. #### static bool extractNatives(const LaunchOptions &options, const MinecraftVersion &version, const QString &nativesDir, QString \*error) Распаковывает файлы `.dll`, `.so` и `.dylib` из нативных библиотек в `<версия>/natives`. Учитывает поле `extractExclude` каждой библиотеки — перечисленные там префиксы не распаковываются. Возвращает `false` и заполняет `error` при неудаче. Без этого шага игра не стартует: LWJGL ищет нативные библиотеки именно в этой папке. #### bool launch(const LaunchOptions &options, const MinecraftVersion &version, QString \*error) Полный цикл запуска: проверка комплектности, распаковка нативных библиотек, поиск java, старт процесса. Возвращает `false` и заполняет `error`, если что-то из перечисленного не удалось; `true` означает, что процесс запущен — дальнейшая судьба игры приходит сигналами. Путь к java берётся из `options.javaPath`, а при пустом значении ищется через [JavaLocator](javalocator.md) с учётом требования версии. #### void terminate() Завершает процесс игры. Если игра не запущена, ничего не делает. ## Сигналы #### progress(const QString &message) Описание текущего шага подготовки: проверка файлов, распаковка нативных библиотек, поиск java. Обработчик показывает сообщение пользователю — подготовка занимает заметное время. #### output(const QString &line) Одна строка вывода процесса игры. Обработчик пишет её в журнал; в лаунчере это вывод в консоль. #### gameStarted(const QString &commandLine) Процесс запущен; в параметре — собранная командная строка целиком. Удобно для диагностики: по ней видно, с какими аргументами и какой java стартовала игра. #### gameFinished(int exitCode, bool crashed) Игра завершилась. `exitCode` — код выхода процесса, `crashed` отличает аварийное завершение от обычного. Обработчик снимает признак «игра идёт», возвращает доступность кнопки запуска и сообщает пользователю итог: ненулевой код или выставленный `crashed` показываются как ошибка. ## Владение и время жизни Класс наследует `QObject` и принимает `parent` — родитель его и удалит. Процесс игры хранится в поле `m_process` и создаётся при запуске. Один экземпляр рассчитан ровно на одну игру одновременно: повторный вызов `launch()` при работающем процессе не предусмотрен, и вызывающий код обязан проверять `isRunning()`. Дочерний процесс переживает уничтожение объекта не сам по себе — завершать игру перед выходом должен вызывающий код через `terminate()`. ## Потокобезопасность Только поток GUI. `QProcess` привязан к потоку, в котором создан, и все сигналы приходят туда же. Статические методы (`missingFiles()`, `buildArguments()`, `extractNatives()`) состояния не имеют, но выполняют файловый ввод-вывод и на большой версии могут заметно задержать вызывающий поток. ## Взаимодействие с другими классами `LauncherBackend` собирает `LaunchOptions` из трёх источников: настроек лаунчера, описания выбранной сборки и `AuthResult` от [AuthService](AuthService.md) или [MsaAuthService](MsaAuthService.md). Разобранную версию он получает через `VersionLoader::load()` из [minecraftversion.h](minecraftversion.md). Все четыре сигнала бэкенд переправляет в QML: `progress` и `gameFinished` попадают в плашку сообщений главного окна, `output` — в консоль, а `gameStarted` меняет признак `gameRunning`. Статический `missingFiles()` вызывается отдельно от запуска — из метода проверки комплектности сборки, результат которого показывает [BuildsDialog](../qml/BuildsDialog.md). ## Внешнее взаимодействие **Дочерний процесс.** Класс запускает java через `QProcess`. Аргументы собираются `buildArguments()`; для профилей Ely.by в них добавляется `-javaagent` с путём к `authlib-injector`. Стандартный вывод процесса читается построчно и отдаётся сигналом `output`, завершение — сигналом `gameFinished`. Направление обмена одностороннее: лаунчер запускает процесс и читает его вывод, ничего не передавая обратно после старта. Все сигналы процесса приходят в поток GUI. ## Пример использования ```cpp auto *launcher = new GameLauncher(this); connect(launcher, &GameLauncher::progress, this, &Backend::showStatus); connect(launcher, &GameLauncher::gameFinished, this, &Backend::onGameFinished); LaunchOptions options; options.gameDir = settings.resolvedGameDir; options.versionId = build.resolvedVersionId; options.playerName = auth.playerName; options.uuid = auth.uuid; options.accessToken = auth.accessToken; options.userType = auth.userType; options.maxMemoryMb = settings.maxMemoryMb; QString error; if (!launcher->launch(options, version, &error)) emit launchError(error); ``` --- При создании этого документа использовался ИИ.