542 lines
36 KiB
Markdown
542 lines
36 KiB
Markdown
# LauncherBackend
|
||
|
||
## Обзор класса
|
||
|
||
`LauncherBackend` — единственный класс проекта, видимый из QML, и центр всего приложения. Интерфейс
|
||
лаунчера ничего не знает ни о сети, ни о файлах, ни о процессах: он читает свойства этого класса,
|
||
вызывает его методы и слушает его сигналы.
|
||
|
||
Сам бэкенд почти ничего не делает руками. Он владеет двенадцатью сервисами — авторизация, запуск
|
||
игры, каталоги версий, модлоадеров, Java и сезонных сборок, три установщика, загрузчик паков и
|
||
переключатель сборок — и отвечает за то, чтобы они работали в правильном порядке. Кроме того, он
|
||
хранит состояние лаунчера: профили игрока, пользовательские сборки и настройки запуска, которые
|
||
читает и пишет в файлы папки лаунчера.
|
||
|
||
Ещё одна его задача — приводить данные к виду, удобному QML. Каталоги отдаются в интерфейс уже
|
||
сведёнными с локальным состоянием: строка версии знает, скачана ли она, строка сезонной сборки —
|
||
установлена ли и не устарела ли. Окно показывает статус, не считая ничего само.
|
||
|
||
## Место в проекте и зависимости
|
||
|
||
Единственный экземпляр создаётся декларативно в [Main.qml](../qml/Main.md); в `main.cpp` он не
|
||
упоминается.
|
||
|
||
Владеет двенадцатью сервисами, каждому из которых посвящена своя страница:
|
||
|
||
| Поле | Класс | Роль |
|
||
|------|-------|------|
|
||
| `m_auth` | [AuthService](AuthService.md) | вход через Ely.by и офлайн |
|
||
| `m_msa` | [MsaAuthService](MsaAuthService.md) | вход через Microsoft |
|
||
| `m_launcher` | [GameLauncher](GameLauncher.md) | запуск JVM с игрой |
|
||
| `m_manifest` | [VersionManifestService](VersionManifestService.md) | каталог версий Mojang |
|
||
| `m_installer` | [VersionInstaller](VersionInstaller.md) | установка версии игры |
|
||
| `m_loaderMeta` | [ModLoaderVersionService](ModLoaderVersionService.md) | списки версий модлоадеров |
|
||
| `m_loaderInstaller` | [ModLoaderInstaller](ModLoaderInstaller.md) | установка модлоадера |
|
||
| `m_switcher` | [BuildSwitcher](BuildSwitcher.md) | смена активной сборки |
|
||
| `m_javaMeta` | [JavaRuntimeService](JavaRuntimeService.md) | каталог сборок Java |
|
||
| `m_javaInstaller` | [JavaInstaller](JavaInstaller.md) | установка Java |
|
||
| `m_seasonalMeta` | [SeasonalBuildService](SeasonalBuildService.md) | каталог сезонных сборок |
|
||
| `m_packDownloader` | [SeasonalPackDownloader](SeasonalPackDownloader.md) | загрузка архива сезонной сборки |
|
||
|
||
Пути ко всем файлам состояния берутся из [launcherpaths.h](launcherpaths.md), описания версий — из
|
||
[minecraftversion.h](minecraftversion.md), словарь модлоадеров — из [modloader.h](modloader.md),
|
||
поиск системной Java — из [javalocator.md](javalocator.md).
|
||
|
||
Требования сборки: `Qt6::Core`, `Qt6::Gui`, `Qt6::Network`, `Qt6::CorePrivate` (опосредованно) и
|
||
`Qt6::Quick` — класс зарегистрирован в QML-модуле `Minecraft_launcher`, объявленном в
|
||
`CMakeLists.txt`.
|
||
|
||
## Иерархия и роль
|
||
|
||
Наследует `QObject`: мета-объектная система, свойства, сигналы и владение по родителю. Объявлен
|
||
виртуальный деструктор. Виртуальных методов базового класса не переопределяет.
|
||
|
||
## Свойства Q_PROPERTY
|
||
|
||
### Профили и сборки
|
||
|
||
| Свойство | Тип | READ | WRITE | NOTIFY | Описание |
|
||
|----------|-----|------|-------|--------|----------|
|
||
| `profileNames` | `QStringList` | `profileNames` | — | `profilesChanged` | Имена профилей игрока в порядке добавления. Только для чтения; модель выпадающего списка профилей |
|
||
| `customBuildNames` | `QStringList` | `customBuildNames` | — | `customBuildsChanged` | Имена пользовательских сборок. Только для чтения; модель списка в окне сборок |
|
||
| `activeBuildIndex` | `int` | `activeBuildIndex` | `setActiveBuildIndex` | `activeBuildChanged` | Сборка, которую запускает кнопка игры. Хранится по идентификатору сборки, а не по индексу: удаление соседней записи не должно переназначать активную. Запись в свойство запускает смену сборки |
|
||
| `activeBuildName` | `QString` | `activeBuildName` | — | `activeBuildChanged` | Имя активной сборки для подписи на кнопке. Только для чтения |
|
||
| `installedVersions` | `QStringList` | `installedVersions` | — | `installedVersionsChanged` | Версии, реально установленные в папке игры. Только для чтения |
|
||
|
||
### Занятость и смена сборки
|
||
|
||
| Свойство | Тип | READ | WRITE | NOTIFY | Описание |
|
||
|----------|-----|------|-------|--------|----------|
|
||
| `gameRunning` | `bool` | `gameRunning` | — | `gameRunningChanged` | Игра запущена. Только для чтения; выключает кнопку запуска |
|
||
| `switching` | `bool` | `switching` | — | `switchChanged` | Идёт архивация или распаковка `.minecraft` при смене сборки. Отдельно от `busy`, потому что на это время блокируется ещё и список сборок. Только для чтения |
|
||
| `switchProgress` | `double` | `switchProgress` | — | `switchChanged` | Доля выполнения смены сборки от `0` до `1`; `-1` — итог неизвестен. Только для чтения |
|
||
| `switchStage` | `QString` | `switchStage` | — | `switchChanged` | Этап смены сборки словами. Только для чтения |
|
||
| `switchStatus` | `QString` | `switchStatus` | — | `switchChanged` | Строка состояния смены сборки. Только для чтения |
|
||
| `busy` | `bool` | `busy` | — | `busyChanged` | Лаунчер занят: пока идёт загрузка версии, кнопка запуска гаснет. Только для чтения |
|
||
| `microsoftAvailable` | `bool` | `microsoftAvailable` | — | — | Собран ли лаунчер с Qt WebEngine. Константное свойство: без WebEngine окно входа Microsoft показать нечем, и интерфейс не должен предлагать этот путь |
|
||
|
||
### Каталог версий
|
||
|
||
| Свойство | Тип | READ | WRITE | NOTIFY | Описание |
|
||
|----------|-----|------|-------|--------|----------|
|
||
| `versionCatalog` | `QVariantList` | `versionCatalog` | — | `versionCatalogChanged` | Объединённый список для выбора версии: установленные, разделитель, затем весь каталог Mojang. Строка содержит поля `id`, `label`, `category`, `installed` и `search`. Только для чтения |
|
||
| `catalogLoading` | `bool` | `catalogLoading` | — | `catalogLoadingChanged` | Идёт загрузка манифеста версий. Только для чтения |
|
||
|
||
### Каталог Java
|
||
|
||
| Свойство | Тип | READ | WRITE | NOTIFY | Описание |
|
||
|----------|-----|------|-------|--------|----------|
|
||
| `javaCatalog` | `QVariantList` | `javaCatalog` | — | `javaCatalogChanged` | Сборки Java для окна выбора: скачиваемые из сети плюс те, что уже лежат в папке лаунчера. Строка содержит поля `id`, `label`, `kind`, `major`, `installed`, `downloadable`, `lts`, `sizeMb`, `detail`, `coverage` и `search`. Только для чтения |
|
||
| `javaCatalogLoading` | `bool` | `javaCatalogLoading` | — | `javaCatalogLoadingChanged` | Идёт загрузка каталога Java. Только для чтения |
|
||
|
||
### Сезонные сборки
|
||
|
||
| Свойство | Тип | READ | WRITE | NOTIFY | Описание |
|
||
|----------|-----|------|-------|--------|----------|
|
||
| `seasonalCatalog` | `QVariantList` | `seasonalCatalog` | — | `seasonalCatalogChanged` | Готовые сборки с сервера. Строки уже сведены с локальными записями: окно показывает статус, не считая ничего само. Только для чтения |
|
||
| `seasonalCatalogLoading` | `bool` | `seasonalCatalogLoading` | — | `seasonalCatalogLoadingChanged` | Идёт загрузка каталога сезонных сборок. Только для чтения |
|
||
| `seasonalCatalogError` | `QString` | `seasonalCatalogError` | — | `seasonalCatalogChanged` | Текст ошибки обращения к серверу сборок; пусто — всё в порядке. Только для чтения |
|
||
| `seasonalInstalling` | `bool` | `seasonalInstalling` | — | `seasonalInstallingChanged` | Идёт установка сезонной сборки: окно не даёт начать вторую. Только для чтения |
|
||
|
||
### Загрузка
|
||
|
||
Одного сигнала на все свойства загрузки достаточно: установщик уже ограничивает частоту, а QML всё
|
||
равно перечитывает их разом.
|
||
|
||
| Свойство | Тип | READ | WRITE | NOTIFY | Описание |
|
||
|----------|-----|------|-------|--------|----------|
|
||
| `downloading` | `bool` | `downloading` | — | `downloadChanged` | Идёт какая-либо загрузка. Только для чтения |
|
||
| `downloadProgress` | `double` | `downloadProgress` | — | `downloadChanged` | Доля выполнения от `0` до `1`; `-1` — итог неизвестен. Только для чтения |
|
||
| `downloadVersion` | `QString` | `downloadVersion` | — | `downloadChanged` | Что именно качается — версия, модлоадер, сборка Java или пак. Только для чтения |
|
||
| `downloadStatus` | `QString` | `downloadStatus` | — | `downloadChanged` | Строка состояния загрузки. Только для чтения |
|
||
| `downloadBytesDone` | `qint64` | `downloadBytesDone` | — | `downloadChanged` | Принято байт. Только для чтения |
|
||
| `downloadBytesTotal` | `qint64` | `downloadBytesTotal` | — | `downloadChanged` | Ожидаемый объём в байтах; `0` — неизвестен. Только для чтения |
|
||
|
||
Панель загрузки одна на все четыре источника: свойства отдают числа того установщика или
|
||
загрузчика, который работает сейчас.
|
||
|
||
## Методы Q_INVOKABLE
|
||
|
||
Все перечисленные ниже методы вызываются из QML.
|
||
|
||
### Профили
|
||
|
||
#### void addProfile(const QString &name, const QString &login, const QString &password, const QString &authType = "offline")
|
||
|
||
Добавляет профиль игрока. Параметр `authType` принимает значения `offline`, `elyby` и `microsoft`.
|
||
Для офлайн-профиля пароль не нужен, для профиля Microsoft не нужны ни логин, ни пароль. Испускает
|
||
`profilesChanged`.
|
||
|
||
#### void updateProfile(int index, const QString &name, const QString &login, const QString &password, const QString &authType = "offline")
|
||
|
||
Перезаписывает профиль по индексу теми же полями. Испускает `profilesChanged`.
|
||
|
||
#### QVariantMap profileAt(int index) const
|
||
|
||
Данные профиля для диалога редактирования: имя, логин, пароль, тип, а также признак наличия
|
||
действующей сессии Microsoft и ник, полученный при официальной авторизации.
|
||
|
||
#### void removeProfile(int index)
|
||
|
||
Удаляет профиль. Испускает `profilesChanged`.
|
||
|
||
### Вход через Microsoft
|
||
|
||
#### void startMicrosoftLogin(int profileIndex)
|
||
|
||
Начинает вход в аккаунт Microsoft: испускает `microsoftLoginUrlReady` с адресом страницы входа.
|
||
|
||
Значение `-1` в `profileIndex` означает, что вход ещё не привязан к профилю: профиль создастся по
|
||
нику, который вернут Minecraft Services.
|
||
|
||
#### void finishMicrosoftLogin(const QString &code)
|
||
|
||
Завершает вход по коду авторизации, перехваченному окном браузера. Итог приходит сигналом
|
||
`microsoftLoginSucceeded` или `microsoftLoginFailed`.
|
||
|
||
#### void cancelMicrosoftLogin()
|
||
|
||
Сбрасывает начатую сессию входа. Вызывается, когда пользователь закрыл окно или адрес возврата
|
||
пришёл без кода.
|
||
|
||
#### QVariantMap inspectMicrosoftRedirect(const QString &url) const
|
||
|
||
Разбирает адрес, на который встроенное окно возвращается после входа. Возвращает карту с полями
|
||
`matched` (является ли адрес адресом возврата), `code` и `error`.
|
||
|
||
Разбор живёт в C++, чтобы правила совпадения не разъезжались с теми, по которым сервис сам строит
|
||
`redirect_uri`.
|
||
|
||
### Сборки
|
||
|
||
#### void addCustomBuild(const QString &name, const QString &serverUrl, const QString &minecraftVersion = QString())
|
||
|
||
Создаёт пользовательскую сборку. Испускает `customBuildsChanged`.
|
||
|
||
#### void updateCustomBuild(int index, const QVariantMap &fields)
|
||
|
||
Мержит в сборку только присланные ключи: `name`, `serverUrl`, `minecraftVersion`, `loader`,
|
||
`loaderVersion`, `resolvedVersionId`. Остальные поля остаются как были — это и позволяет карточке
|
||
сборки сохранять правки по одному полю за раз.
|
||
|
||
#### QVariantMap customBuildAt(int index) const
|
||
|
||
Данные сборки для карточки редактирования.
|
||
|
||
#### QVariantMap customBuildRemovalInfo(int index) const
|
||
|
||
Что именно потеряется при удалении сборки — для текста предупреждения. Возвращает имя сборки и три
|
||
признака: есть ли у неё архив, активна ли она сейчас и последняя ли она.
|
||
|
||
#### void removeCustomBuild(int index)
|
||
|
||
Удаляет сборку вместе с её архивом. Испускает `customBuildsChanged`.
|
||
|
||
#### void installCustomBuild(int index)
|
||
|
||
Докачивает то, чего не хватает выбранной сборке: версию игры и, если он выбран, модлоадер.
|
||
Вынесено отдельной кнопкой, потому что карточка сборки сохраняет правки по ходу редактирования и
|
||
установка не должна начинаться сама при каждой правке.
|
||
|
||
#### QStringList checkInstallation(int buildIndex) const
|
||
|
||
Проверка комплектности без запуска — для подсказки в интерфейсе. Пустой список означает, что
|
||
сборку можно запускать; иначе возвращаются описания недостающих файлов.
|
||
|
||
### Запуск игры
|
||
|
||
#### void launchGame(int profileIndex, int buildIndex)
|
||
|
||
Главная кнопка. Проверяет выбор профиля и версии, комплектность `.minecraft`, при необходимости
|
||
авторизуется и стартует игру.
|
||
|
||
Авторизация асинхронна, поэтому метод возвращается сразу; дальнейший ход виден по сигналам
|
||
`launchProgress`, `launched`, `launchError` и `twoFactorRequired`.
|
||
|
||
#### void submitTwoFactorCode(const QString &code)
|
||
|
||
Продолжает прерванный запуск, отдавая одноразовый код двухфакторной аутентификации. Вызывается
|
||
после сигнала `twoFactorRequired`.
|
||
|
||
#### void cancelPendingLaunch()
|
||
|
||
Отменяет запуск, остановленный на ожидании кода двухфакторной аутентификации.
|
||
|
||
#### void stopGame()
|
||
|
||
Завершает процесс игры.
|
||
|
||
### Каталог версий
|
||
|
||
#### void refreshVersionCatalog(bool force = false)
|
||
|
||
Обновляет каталог версий. Вызывается при открытии окна выбора версии: свежий кэш отвечает без
|
||
сети. Параметр `force` обходит проверку свежести.
|
||
|
||
#### bool isVersionInstalled(const QString &versionId) const
|
||
|
||
Установлена ли версия в папке игры.
|
||
|
||
#### void installVersion(const QString &versionId)
|
||
|
||
Ставит версию игры в фоне. Ход виден по свойствам загрузки.
|
||
|
||
#### QVariantMap versionRemovalInfo(const QString &versionId) const
|
||
|
||
Что потеряется при удалении версии — для текста предупреждения. Возвращает признак установки,
|
||
занимаемый объём в мегабайтах, список зависящих профилей модлоадеров и список сборок, которые эту
|
||
версию используют.
|
||
|
||
#### void removeVersion(const QString &versionId)
|
||
|
||
Удаляет файлы версии из `versions/`. Библиотеки и ресурсы остаются: они общие для всех версий.
|
||
Испускает `installedVersionsChanged`.
|
||
|
||
#### void cancelDownload()
|
||
|
||
Отменяет текущую загрузку.
|
||
|
||
### Модлоадеры
|
||
|
||
#### QVariantList loaderVersions(const QString &loaderKey, const QString &gameVersion) const
|
||
|
||
Версии модлоадера для выбранной версии игры. Возвращает список карт с полями `version`, `label`,
|
||
`recommended` и `stable`.
|
||
|
||
Несовместимых строк в списке нет — отбор заложен в сам источник данных, поэтому проверять
|
||
совместимость вызывающему коду не нужно. Пустой список означает, что лоадер эту версию игры не
|
||
поддерживает.
|
||
|
||
Параметр `loaderKey` принимает значения `forge`, `fabric`, `neoforge` и `quilt`.
|
||
|
||
#### void refreshLoaderVersions(const QString &loaderKey, const QString &gameVersion, bool force = false)
|
||
|
||
Запрашивает обновление списка версий лоадера. Результат приходит сигналом `loaderVersionsChanged`.
|
||
|
||
#### bool loaderVersionsLoading(const QString &loaderKey, const QString &gameVersion) const
|
||
|
||
Идёт ли сейчас запрос по этой паре. Позволяет интерфейсу отличить «ещё грузим» от «не
|
||
поддерживается».
|
||
|
||
#### void installLoaderForBuild(int index)
|
||
|
||
Ставит модлоадер, выбранный в сборке, и записывает получившийся профиль в `resolvedVersionId`.
|
||
|
||
### Настройки
|
||
|
||
#### QVariantMap settings() const
|
||
|
||
Настройки запуска одной картой: `gameDir`, `javaPath`, `javaRuntime`, `minMemoryMb`,
|
||
`maxMemoryMb`, `jvmArgs`, `windowWidth`, `windowHeight`, `fullscreen`, `language` и вычисленный
|
||
`resolvedGameDir`.
|
||
|
||
#### void updateSettings(const QVariantMap &values)
|
||
|
||
Записывает настройки и сохраняет их на диск. Испускает `settingsChanged`.
|
||
|
||
Смену `language` после записи пробрасывает в [Localization](Localization.md) — порядок
|
||
«сохранили → переключили» гарантирует, что выбранный язык переживёт падение сразу после
|
||
переключения. Сам `Localization` в `settings.json` не пишет: файл ведёт только бэкенд.
|
||
|
||
#### QStringList detectedJava() const
|
||
|
||
Пути ко всем java, найденным в системе. Показывается справочной строкой в диалоге настроек.
|
||
|
||
### Сборки Java
|
||
|
||
#### void refreshJavaCatalog(bool force = false)
|
||
|
||
Обновляет каталог сборок Java. Вызывается при открытии окна выбора: свежий кэш отвечает без сети.
|
||
|
||
#### void installJavaRuntime(const QString &runtimeId)
|
||
|
||
Скачивает и распаковывает сборку Java. По завершении испускается `javaRuntimeInstalled`.
|
||
|
||
#### void removeJavaRuntime(const QString &runtimeId)
|
||
|
||
Удаляет скачанную сборку Java из папки лаунчера.
|
||
|
||
#### QVariantMap javaRuntimeInfo(const QString &runtimeId) const
|
||
|
||
Описание установленной сборки: подпись, версия, путь к java и признак установки. Пустая карта
|
||
означает, что сборки с таким идентификатором в папке лаунчера нет.
|
||
|
||
Метод не является привязкой и сам не пересчитывается, когда сборка докачается, — диалог настроек
|
||
обновляет его по сигналу `javaRuntimeInstalled`.
|
||
|
||
#### int requiredJavaMajor(int buildIndex) const
|
||
|
||
Минимальная мажорная версия Java для версии игры выбранной сборки; `0` — версия не выбрана.
|
||
Передаётся в окно выбора Java, чтобы пометить слишком старые сборки.
|
||
|
||
### Сезонные сборки
|
||
|
||
#### void refreshSeasonalCatalog(bool force = false)
|
||
|
||
Обновляет каталог сезонных сборок.
|
||
|
||
#### void installSeasonalBuild(const QString &seasonalId)
|
||
|
||
Ставит или обновляет сборку целиком одной цепочкой: запись сборки, версия игры, модлоадер, Java и
|
||
файлы.
|
||
|
||
Порядок шагов жёсткий: сначала сборка делается активной, затем ставится Java — она нужна
|
||
установщику Forge, — затем модлоадер, затем качается пак и только в конце его файлы раскатываются
|
||
поверх `.minecraft`. Раскатывать файлы имеет смысл только когда всё остальное на месте.
|
||
|
||
Строка каталога копируется на момент старта: обновление списка посреди установки не должно менять
|
||
то, что ставится. По завершении испускается `seasonalInstallFinished`.
|
||
|
||
#### void cancelSeasonalInstall()
|
||
|
||
Отменяет установку сезонной сборки.
|
||
|
||
### Папки
|
||
|
||
#### void openMinecraftFolder()
|
||
|
||
Открывает папку модов Minecraft в файловом менеджере системы.
|
||
|
||
#### void openGameFolder()
|
||
|
||
Открывает корневую папку игры в файловом менеджере системы.
|
||
|
||
## Сигналы
|
||
|
||
### Сигналы уведомления свойств
|
||
|
||
Эти сигналы объявлены как `NOTIFY` соответствующих свойств; обработчик перечитывает свойство.
|
||
`profilesChanged`, `customBuildsChanged`, `activeBuildChanged`, `switchChanged`,
|
||
`installedVersionsChanged`, `settingsChanged`, `gameRunningChanged`, `busyChanged`,
|
||
`versionCatalogChanged`, `catalogLoadingChanged`, `javaCatalogChanged`,
|
||
`javaCatalogLoadingChanged`, `seasonalCatalogChanged`, `seasonalCatalogLoadingChanged`,
|
||
`seasonalInstallingChanged` и `downloadChanged`.
|
||
|
||
#### loaderVersionsChanged(const QString &loaderKey, const QString &gameVersion)
|
||
|
||
Список версий модлоадера изменился. Параметры сужают событие до конкретной пары, поэтому
|
||
обработчик обязан сверить их со своим текущим состоянием: обновление может относиться к другой
|
||
строке лоадера или к прошлой версии игры. Именно так поступает [LoaderRow](../qml/LoaderRow.md).
|
||
|
||
### События установки
|
||
|
||
#### seasonalInstallFinished(const QString &seasonalId, const QString &buildName)
|
||
|
||
Сезонная сборка установлена и активна — можно запускать игру. Обработчик показывает сообщение
|
||
пользователю.
|
||
|
||
#### javaRuntimeInstalled(const QString &runtimeId)
|
||
|
||
Сборка Java установлена. Диалог настроек по этому сигналу обновляет подпись выбранной сборки, не
|
||
переоткрываясь.
|
||
|
||
### Запуск игры
|
||
|
||
#### launchProgress(const QString &message)
|
||
|
||
Описание текущего шага запуска. Обработчик показывает сообщение без таймаута: шаг может занять
|
||
заметное время, и сообщение должно держаться до следующего.
|
||
|
||
#### launched(const QString &profileName, const QString &buildName, const QString &serverUrl)
|
||
|
||
Игра запущена. Обработчик сообщает пользователю, какой профиль и какая сборка стартовали.
|
||
|
||
#### launchError(const QString &message)
|
||
|
||
Запуск не удался либо произошла ошибка, о которой нужно сказать пользователю. Через этот же сигнал
|
||
сообщается о проблемах записи файлов лаунчера.
|
||
|
||
#### twoFactorRequired(const QString &profileName)
|
||
|
||
Ely.by отклонил пароль с пометкой two factor. Обработчик открывает диалог ввода кода и передаёт
|
||
введённое значение в `submitTwoFactorCode()`; отказ должен вызвать `cancelPendingLaunch()`, иначе
|
||
запуск останется висеть в ожидании.
|
||
|
||
#### gameOutput(const QString &line)
|
||
|
||
Строка вывода процесса игры. Обработчик пишет её в журнал.
|
||
|
||
#### gameFinished(int exitCode, bool crashed)
|
||
|
||
Игра завершилась. Обработчик сообщает итог: ненулевой код или выставленный `crashed` показываются
|
||
как ошибка.
|
||
|
||
### Вход через Microsoft
|
||
|
||
#### microsoftLoginUrlReady(const QString &url)
|
||
|
||
Окну входа Microsoft: открыться на этом адресе. Обработчик создаёт окно (в сборке с Qt WebEngine)
|
||
и открывает его.
|
||
|
||
#### microsoftLoginSucceeded(const QString &playerName)
|
||
|
||
Вход выполнен. Обработчик сообщает об этом пользователю, но не трогает выбор в списке профилей:
|
||
новый профиль уже выбран тем, кто его создал, а повторный вход мог быть и не в последний профиль.
|
||
|
||
#### microsoftLoginFailed(const QString &message)
|
||
|
||
Вход не удался.
|
||
|
||
#### microsoftReloginRequired(int profileIndex)
|
||
|
||
Сессия профиля протухла настолько, что нужен повторный вход руками. Обработчик обычно сразу
|
||
вызывает `startMicrosoftLogin()` для этого профиля.
|
||
|
||
## Владение и время жизни
|
||
|
||
Класс наследует `QObject` и принимает `parent`. Экземпляр создаётся декларативно в QML, поэтому
|
||
временем его жизни управляет движок QML: объект живёт столько же, сколько главное окно.
|
||
|
||
Все двенадцать сервисов создаются в конструкторе с бэкендом в роли родителя и уничтожаются вместе
|
||
с ним. Порядок создания важен для двоих: [VersionInstaller](VersionInstaller.md) принимает в
|
||
конструктор сервис манифеста, а [ModLoaderInstaller](ModLoaderInstaller.md) — сервис версий
|
||
лоадеров и установщик версий; эти указатели не переходят во владение принимающей стороны.
|
||
|
||
Кэши каталогов помечены `mutable` и пересобираются лениво из константных геттеров: QML читает
|
||
свойства помногу раз за кадр, пока открыт список, и пересборка по каждому чтению обошлась бы
|
||
дорого.
|
||
|
||
## Потокобезопасность
|
||
|
||
Только поток GUI. Единственная работа в другом потоке — файловые операции над содержимым
|
||
`.minecraft`, и она полностью инкапсулирована в [BuildSwitcher](BuildSwitcher.md): сам бэкенд
|
||
общается с ним обычными сигналами и слотами.
|
||
|
||
## Доступ из QML
|
||
|
||
Класс зарегистрирован макросом `QML_ELEMENT` в модуле `Minecraft_launcher`, объявленном через
|
||
`qt_add_qml_module` в `CMakeLists.txt`. Имя типа в QML совпадает с именем класса —
|
||
`LauncherBackend`.
|
||
|
||
Из QML доступны все 26 свойств, все 42 метода `Q_INVOKABLE` и все сигналы, перечисленные выше.
|
||
Синглтоном тип не объявлен: экземпляр создаётся декларативно в [Main.qml](../qml/Main.md) и
|
||
передаётся во вложенные диалоги через их свойство `backend`. Все диалоги проекта объявляют его как
|
||
`required property var backend`.
|
||
|
||
Объект, созданный из QML, принадлежит движку QML — удалять его из C++ нельзя.
|
||
|
||
## Взаимодействие с другими классами
|
||
|
||
**Вниз, к сервисам.** Бэкенд подписан на сигналы всех двенадцати сервисов и сводит их к своим
|
||
свойствам. Четыре разных источника загрузки — установщик версий, установщик модлоадеров,
|
||
установщик Java и загрузчик паков — отображаются в одну группу свойств `download*`, поэтому панель
|
||
в интерфейсе не различает, кто работает; какой из источников показывать, бэкенд решает сам.
|
||
|
||
**Вверх, к QML.** Интерфейс не обращается ни к одному сервису напрямую. Каталоги отдаются уже
|
||
сведёнными с локальным состоянием: строка версии знает про `installed`, строка сезонной сборки —
|
||
про установленную ревизию и доступное обновление.
|
||
|
||
**Состояние на диске.** Профили, сборки и настройки читаются при создании и пишутся при каждом
|
||
изменении. Отсутствие файла — норма (первый запуск), а повреждённое содержимое отводится в файл с
|
||
расширением `.bak`, чтобы рабочий файл создался заново. Проблемы хранилища, замеченные на старте,
|
||
накапливаются и показываются одним сообщением, когда интерфейс уже подключился к сигналам.
|
||
|
||
Отдельно предусмотрена миграция: файл `versions.json` от прежней схемы именования переносится в
|
||
`customBuilds.json` при первом запуске после переименования.
|
||
|
||
**Восстановление после сбоя.** При старте бэкенд спрашивает у переключателя сборок, не было ли
|
||
прервано переключение, и предлагает доиграть его.
|
||
|
||
## Внешнее взаимодействие
|
||
|
||
Собственных сетевых обращений и дочерних процессов у класса нет: всё внешнее взаимодействие
|
||
делегировано сервисам — сеть у каталогов, установщиков и служб авторизации, процессы у
|
||
[GameLauncher](GameLauncher.md), [ModLoaderInstaller](ModLoaderInstaller.md) и
|
||
[JavaInstaller](JavaInstaller.md), файловые операции над `.minecraft` у
|
||
[BuildSwitcher](BuildSwitcher.md).
|
||
|
||
Единственное прямое обращение к системе — открытие папки игры в файловом менеджере методами
|
||
`openMinecraftFolder()` и `openGameFolder()`.
|
||
|
||
## Пример использования
|
||
|
||
Класс предназначен для создания из QML, а не из C++:
|
||
|
||
```qml
|
||
import QtQuick
|
||
import Minecraft_launcher
|
||
|
||
Window {
|
||
id: window
|
||
visible: true
|
||
|
||
LauncherBackend {
|
||
id: backend
|
||
|
||
onLaunchError: (message) => console.warn(message)
|
||
onTwoFactorRequired: (profileName) => twoFactorDialog.open()
|
||
onMicrosoftLoginUrlReady: (url) => window.openMicrosoftLogin(url)
|
||
}
|
||
|
||
Button {
|
||
text: backend.activeBuildName
|
||
enabled: !backend.busy && !backend.gameRunning
|
||
onClicked: backend.launchGame(profileBox.currentIndex, backend.activeBuildIndex)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
При создании этого документа использовался ИИ.
|