Files
2026-10-11 22:32:13 +03:00

7.5 KiB
Raw Permalink Blame History

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)

  1. Если игра уже идёт, отказ.
  2. missingFiles(): если чего-то не хватает, отказ со списком.
  3. Распаковка нативных библиотек в <gameDir>/versions/<id>/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:<authlib-injector>=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:<токен>:<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, если его там ещё нет.


При создании этого документа использовался ИИ.