# GameLauncher `gamelauncher.h`, `gamelauncher.cpp` Собирает командную строку, распаковывает нативные библиотеки и запускает JVM с игрой. К этому моменту версия уже разобрана ([minecraftversion.h](minecraftversion.md)), файлы скачаны, а игрок вошёл в аккаунт. Всё остальное приходит в структуре `LaunchOptions`. Экземпляр один, его держит [LauncherBackend](LauncherBackend.md). Одновременно может работать только одна игра. ## LaunchOptions | Поле | По умолчанию | Что это | |------|--------------|---------| | `gameDir` | | папка игры, она же рабочий каталог процесса | | `versionId` | | папка в `versions/` | | `playerName`, `uuid`, `accessToken`, `userType`, `clientToken`, `xuid` | | из [AuthResult](AuthService.md) | | `javaPath` | пусто | пусто — ищет [JavaLocator](javalocator.md) | | `minMemoryMb`, `maxMemoryMb` | 512, 4096 | `-Xms`, `-Xmx`; 0 — не передавать | | `extraJvmArgs` | | аргументы JVM из настроек | | `windowWidth`, `windowHeight` | 0, 0 | 0 — не передавать размер | | `fullscreen` | `false` | | | `serverAddress` | пусто | `host[:port]`, сервер для автоподключения | | `authlibInjectorPath` | пусто | путь к `authlib-injector.jar`; пусто — без него | | `authlibInjectorApi` | `ely.by` | сервер авторизации для инжектора | | `launcherName`, `launcherVersion` | `BiLauncher`, `LAUNCHER_VERSION` | бэкенд передаёт имя и версию из `QCoreApplication` | ## launch(options, version, &error) 1. Если игра уже идёт, отказ. 2. `missingFiles()`: если чего-то не хватает, отказ со списком. 3. Распаковка нативных библиотек в `/versions//natives`. 4. `JavaLocator::select()` с `version.javaMajor` и `options.javaPath`. На Windows вместо `java.exe` запускается `javaw.exe`, чтобы не было консольного окна. 5. `QProcess` с рабочим каталогом `gameDir` и объединёнными stdout и stderr. 6. Ожидание старта до 15 секунд. Если процесс не стартовал, отказ. Если всё прошло, метод возвращает `true` и испускает `gameStarted(commandLine)`. В этой строке значение после `--accessToken` заменено на `<скрыт>`, чтобы токен не попал в журнал. Других мест, где мог бы оказаться токен (например, в `${auth_session}`), замена не касается. Перед шагами 2–4 испускается `progress(message)`. Вывод игры читается кусками и испускается построчно в `output(line)`. Когда процесс завершится, придёт `gameFinished(exitCode, crashed)`, где `crashed` значит `QProcess::CrashExit`. `terminate()` вызывает `kill()`, а не `terminate()`. Игра не успеет ничего сохранить, это то же самое, что снять процесс в диспетчере задач. ## missingFiles(options, version, limit = 12) Проверяет, что на месте папка игры, клиентский jar, все библиотеки, индекс ресурсов и папка `assets/objects`. Возвращает до `limit` путей. Если папки игры нет, в списке будет только она. Размеры и хеши не проверяются, только наличие. Функция статическая, бэкенд вызывает её и для подсказки в интерфейсе, и перед запуском. ## extractNatives(options, version, nativesDir, &error) Для каждой библиотеки с `native == true` открывает jar через `QZipReader` (отсюда зависимость от `Qt6::CorePrivate`) и вынимает файлы `.dll`, `.so`, `.so.N`, `.dylib`, `.jnilib`. Пропускаются `META-INF/` и пути из `extract.exclude` описания версии. Все файлы кладутся в одну плоскую папку. Если файл такого же размера уже лежит, его не переписывают. ## buildArguments(options, version, nativesDir) Порядок аргументов: 1. `-javaagent:=ely.by` и `-Dauthlibinjector.side=client`, если инжектор задан; 2. `-Xms`, `-Xmx`, потом `extraJvmArgs`; 3. JVM-аргументы из описания версии; 4. аргумент логирования, если файл конфигурации логов скачан; 5. главный класс; 6. аргументы игры из описания версии; 7. `--width`/`--height`, если размер задан, а описание версии само его не передаёт; 8. `--fullscreen`; 9. сервер: `--quickPlayMultiplayer host:port` для версий, которые это понимают, иначе `--server host --port port` (порт по умолчанию 25565). Подстановки: `auth_player_name`, `auth_uuid`, `auth_access_token`, `auth_session` (`token:<токен>:`), `auth_xuid` (пусто → `0`), `clientid` (пусто → `0`), `user_type`, `user_properties` (`{}`), `version_name`, `version_type`, `game_directory`, `assets_root`, `assets_index_name`, `game_assets`, `natives_directory`, `library_directory`, `classpath`, `classpath_separator`, `primary_jar`, `launcher_name`, `launcher_version`, `resolution_width`, `resolution_height`. Аргумент, в котором после подстановки остался `${`, выбрасывается целиком. Так отсекаются переменные, о которых лаунчер не знает (например, связанные с демо-режимом). У версий со старыми ресурсами (`legacy`, `pre-1.6`) `game_assets` указывает на `assets/virtual/legacy`. Classpath — это все не-нативные библиотеки и в конце клиентский jar. ### Forge 1.17+ и `-DignoreList` Forge собирает слой модулей из classpath и пропускает файлы, имя которых начинается с записи в `-DignoreList`. В профиле Forge там записан `${version_name}.jar`. Официальный лаунчер кладёт в classpath jar самого профиля, а у нас своего jar у профиля нет, и в classpath попадает родительский, например `1.20.1.jar`. Если его имени нет в списке, он становится вторым модулем с классами Minecraft, и игра падает с ошибкой вида `Modules minecraft and _1._20._1 export package ...`. Поэтому к `-DignoreList` дописывается имя клиентского jar, если его там ещё нет. --- При создании этого документа использовался ИИ.