7.5 KiB
GameLauncher
gamelauncher.h, gamelauncher.cpp
Собирает командную строку, распаковывает нативные библиотеки и запускает JVM с игрой. К этому
моменту версия уже разобрана (minecraftversion.h), файлы скачаны, а игрок
вошёл в аккаунт. Всё остальное приходит в структуре LaunchOptions.
Экземпляр один, его держит LauncherBackend. Одновременно может работать только одна игра.
LaunchOptions
| Поле | По умолчанию | Что это |
|---|---|---|
gameDir |
папка игры, она же рабочий каталог процесса | |
versionId |
папка в versions/ |
|
playerName, uuid, accessToken, userType, clientToken, xuid |
из AuthResult | |
javaPath |
пусто | пусто — ищет JavaLocator |
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)
- Если игра уже идёт, отказ.
missingFiles(): если чего-то не хватает, отказ со списком.- Распаковка нативных библиотек в
<gameDir>/versions/<id>/natives. JavaLocator::select()сversion.javaMajorиoptions.javaPath. На Windows вместоjava.exeзапускаетсяjavaw.exe, чтобы не было консольного окна.QProcessс рабочим каталогомgameDirи объединёнными stdout и stderr.- Ожидание старта до 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)
Порядок аргументов:
-javaagent:<authlib-injector>=ely.byи-Dauthlibinjector.side=client, если инжектор задан;-Xms,-Xmx, потомextraJvmArgs;- JVM-аргументы из описания версии;
- аргумент логирования, если файл конфигурации логов скачан;
- главный класс;
- аргументы игры из описания версии;
--width/--height, если размер задан, а описание версии само его не передаёт;--fullscreen;- сервер:
--quickPlayMultiplayer host:portдля версий, которые это понимают, иначе--server host --port port(порт по умолчанию 25565).
Подстановки: auth_player_name, auth_uuid, auth_access_token, auth_session
(token:<токен>:<uuid>), 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, если его там ещё нет.
При создании этого документа использовался ИИ.