#pragma once #include "minecraftversion.h" #include #include #include #include #include #include #include struct AuthResult; class AuthService; class GameLauncher; class VersionManifestService; class VersionInstaller; class LauncherBackend : public QObject { Q_OBJECT QML_ELEMENT Q_PROPERTY(QStringList profileNames READ profileNames NOTIFY profilesChanged) Q_PROPERTY(QStringList versionNames READ versionNames NOTIFY versionsChanged) Q_PROPERTY(QStringList installedVersions READ installedVersions NOTIFY installedVersionsChanged) Q_PROPERTY(bool gameRunning READ gameRunning NOTIFY gameRunningChanged) Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) // Объединённый список для выпадающего меню версий: установленные, разделитель, // затем весь каталог Mojang. Q_PROPERTY(QVariantList versionCatalog READ versionCatalog NOTIFY versionCatalogChanged) Q_PROPERTY(bool catalogLoading READ catalogLoading NOTIFY catalogLoadingChanged) // Одного сигнала на все свойства загрузки достаточно: установщик уже // ограничивает частоту, а QML всё равно перечитывает их разом. Q_PROPERTY(bool downloading READ downloading NOTIFY downloadChanged) Q_PROPERTY(double downloadProgress READ downloadProgress NOTIFY downloadChanged) Q_PROPERTY(QString downloadVersion READ downloadVersion NOTIFY downloadChanged) Q_PROPERTY(QString downloadStatus READ downloadStatus NOTIFY downloadChanged) Q_PROPERTY(qint64 downloadBytesDone READ downloadBytesDone NOTIFY downloadChanged) Q_PROPERTY(qint64 downloadBytesTotal READ downloadBytesTotal NOTIFY downloadChanged) public: explicit LauncherBackend(QObject *parent = nullptr); ~LauncherBackend() override; QStringList profileNames() const; QStringList versionNames() const; QStringList installedVersions() const; bool gameRunning() const; // Пока идёт загрузка версии, лаунчер тоже занят: кнопка запуска гаснет. bool busy() const; QVariantList versionCatalog() const; bool catalogLoading() const; bool downloading() const; double downloadProgress() const; QString downloadVersion() const; QString downloadStatus() const; qint64 downloadBytesDone() const; qint64 downloadBytesTotal() const; Q_INVOKABLE void addProfile(const QString &name, const QString &login, const QString &password, const QString &authType = QStringLiteral("offline")); Q_INVOKABLE void addVersion(const QString &name, const QString &serverUrl, const QString &versionId = QString()); Q_INVOKABLE void updateProfile(int index, const QString &name, const QString &login, const QString &password, const QString &authType = QStringLiteral("offline")); Q_INVOKABLE void updateVersion(int index, const QString &name, const QString &serverUrl, const QString &versionId = QString()); Q_INVOKABLE QVariantMap profileAt(int index) const; Q_INVOKABLE QVariantMap versionAt(int index) const; Q_INVOKABLE void removeProfile(int index); Q_INVOKABLE void removeVersion(int index); // Главная кнопка. Проверяет выбор профиля и версии, комплектность .minecraft, // при необходимости авторизуется и стартует игру. Q_INVOKABLE void launchGame(int profileIndex, int versionIndex); Q_INVOKABLE void submitTwoFactorCode(const QString &code); Q_INVOKABLE void cancelPendingLaunch(); Q_INVOKABLE void stopGame(); // Проверка без запуска — для подсказки в интерфейсе. Q_INVOKABLE QStringList checkInstallation(int versionIndex) const; // Вызывается при открытии выпадающего списка версий: свежий кэш — без сети. Q_INVOKABLE void refreshVersionCatalog(bool force = false); Q_INVOKABLE bool isVersionInstalled(const QString &versionId) const; Q_INVOKABLE void installVersion(const QString &versionId); Q_INVOKABLE void cancelDownload(); Q_INVOKABLE QVariantMap settings() const; Q_INVOKABLE void updateSettings(const QVariantMap &values); Q_INVOKABLE QStringList detectedJava() const; Q_INVOKABLE void openMinecraftFolder(); Q_INVOKABLE void openGameFolder(); signals: void profilesChanged(); void versionsChanged(); void installedVersionsChanged(); void settingsChanged(); void gameRunningChanged(); void busyChanged(); void versionCatalogChanged(); void catalogLoadingChanged(); void downloadChanged(); void launchProgress(const QString &message); void launched(const QString &profileName, const QString &versionName, const QString &serverUrl); void launchError(const QString &message); void twoFactorRequired(const QString &profileName); void gameOutput(const QString &line); void gameFinished(int exitCode, bool crashed); private: struct Profile { int id = 0; QString name, login, password; QString authType = QStringLiteral("offline"); // offline | elyby QString clientToken, accessToken, uuid, resolvedName, userType; }; struct Version { int id = 0; QString name, serverUrl, versionId; }; void loadData(); void loadSettings(); void ensureDataFiles(); void saveProfiles(); void saveVersions(); void saveSettings(); // Чтение файлов лаунчера. Отсутствие файла — норма (первый запуск), а вот // повреждённое содержимое отводится в .bak, чтобы файл создался заново. QJsonArray readArrayFile(const QString &path); QJsonObject readObjectFile(const QString &path); bool moveAsideBroken(const QString &path, const QString &reason); // Проверяет папку лаунчера и пишет документ. Об ошибке сообщает через launchError. bool writeJsonFile(const QString &path, const QJsonDocument &document); QString gameDir() const; QString versionIdOf(const Version &version) const; void invalidateCatalog(); void rebuildCatalog() const; // Версия выбрана, но её нет в .minecraft — ставим в фоне. void maybeInstall(const QString &versionId); void setBusy(bool busy); void failLaunch(const QString &message); void beginAuthentication(); void continueLaunch(const AuthResult &auth); QList m_profiles; QList m_versions; QVariantMap m_settings; // Проблемы хранилища, замеченные на старте: показываются одним сообщением, // когда интерфейс уже подключился к сигналам. QStringList m_storageIssues; int m_nextProfileId = 1; int m_nextVersionId = 1; AuthService *m_auth; GameLauncher *m_launcher; VersionManifestService *m_manifest; VersionInstaller *m_installer; // Каталог версий пересобирается только по изменениям: QML читает свойство // помногу раз за кадр, пока открыт список. mutable QVariantList m_catalog; mutable bool m_catalogDirty = true; bool m_busy = false; // Состояние запуска, начатого до асинхронной авторизации. int m_pendingProfile = -1; int m_pendingVersion = -1; MinecraftVersion m_pendingVersionData; QString m_pendingInjectorPath; };