Files
minecraft-launcher/doc/cpp/GameLauncher.md
T
2026-09-03 09:16:56 +03:00

201 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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);
```
---
При создании этого документа использовался ИИ.