Merge branch 'PrismLauncher:develop' into instance-accounts
This commit is contained in:
		
							
								
								
									
										3
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -546,11 +546,10 @@ jobs: | |||||||
|           submodules: 'true' |           submodules: 'true' | ||||||
|       - name: Build Flatpak (Linux) |       - name: Build Flatpak (Linux) | ||||||
|         if: inputs.build_type == 'Debug' |         if: inputs.build_type == 'Debug' | ||||||
|         uses: flatpak/flatpak-github-actions/flatpak-builder@v4 |         uses: flatpak/flatpak-github-actions/flatpak-builder@v5 | ||||||
|         with: |         with: | ||||||
|           bundle: "Prism Launcher.flatpak" |           bundle: "Prism Launcher.flatpak" | ||||||
|           manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml  |           manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml  | ||||||
|           cache-key: flatpak-${{ github.sha }}-x86_64 |  | ||||||
|  |  | ||||||
|   nix: |   nix: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|   | |||||||
| @@ -268,6 +268,8 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS) | |||||||
|     find_package(ghc_filesystem QUIET) |     find_package(ghc_filesystem QUIET) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | include(ECMQtDeclareLoggingCategory) | ||||||
|  |  | ||||||
| ####################################### Program Info ####################################### | ####################################### Program Info ####################################### | ||||||
|  |  | ||||||
| set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary") | set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary") | ||||||
|   | |||||||
| @@ -551,6 +551,24 @@ set(ATLAUNCHER_SOURCES | |||||||
|     modplatform/atlauncher/ATLShareCode.h |     modplatform/atlauncher/ATLShareCode.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | ######## Logging categories ######## | ||||||
|  |  | ||||||
|  | ecm_qt_declare_logging_category(CORE_SOURCES | ||||||
|  |     HEADER Logging.h | ||||||
|  |     IDENTIFIER authCredentials | ||||||
|  |     CATEGORY_NAME "launcher.auth.credentials" | ||||||
|  |     DEFAULT_SEVERITY Warning | ||||||
|  |     DESCRIPTION "Secrets and credentials for debugging purposes" | ||||||
|  |     EXPORT "${Launcher_Name}" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | if(KDE_INSTALL_LOGGINGCATEGORIESDIR)  # only install if there is a standard path for this | ||||||
|  |     ecm_qt_install_logging_categories( | ||||||
|  |         EXPORT "${Launcher_Name}" | ||||||
|  |         DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}" | ||||||
|  |     ) | ||||||
|  | endif() | ||||||
|  |  | ||||||
| ################################ COMPILE ################################ | ################################ COMPILE ################################ | ||||||
|  |  | ||||||
| set(LOGIC_SOURCES | set(LOGIC_SOURCES | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #include "Parsers.h" | #include "Parsers.h" | ||||||
| #include "Json.h" | #include "Json.h" | ||||||
|  | #include "Logging.h" | ||||||
|  |  | ||||||
| #include <QJsonDocument> | #include <QJsonDocument> | ||||||
| #include <QJsonArray> | #include <QJsonArray> | ||||||
| @@ -75,9 +76,7 @@ bool getBool(QJsonValue value, bool & out) { | |||||||
|  |  | ||||||
| bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString name) { | bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString name) { | ||||||
|     qDebug() << "Parsing" << name <<":"; |     qDebug() << "Parsing" << name <<":"; | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|     QJsonParseError jsonError; |     QJsonParseError jsonError; | ||||||
|     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); |     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); | ||||||
|     if(jsonError.error) { |     if(jsonError.error) { | ||||||
| @@ -137,9 +136,7 @@ bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, QString na | |||||||
|  |  | ||||||
| bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { | bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) { | ||||||
|     qDebug() << "Parsing Minecraft profile..."; |     qDebug() << "Parsing Minecraft profile..."; | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     QJsonParseError jsonError; |     QJsonParseError jsonError; | ||||||
|     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); |     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); | ||||||
| @@ -275,9 +272,7 @@ decoded base64 "value": | |||||||
|  |  | ||||||
| bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { | bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { | ||||||
|     qDebug() << "Parsing Minecraft profile..."; |     qDebug() << "Parsing Minecraft profile..."; | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     QJsonParseError jsonError; |     QJsonParseError jsonError; | ||||||
|     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); |     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); | ||||||
| @@ -389,9 +384,7 @@ bool parseMinecraftProfileMojang(QByteArray & data, MinecraftProfile &output) { | |||||||
|  |  | ||||||
| bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) { | bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) { | ||||||
|     qDebug() << "Parsing Minecraft entitlements..."; |     qDebug() << "Parsing Minecraft entitlements..."; | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     QJsonParseError jsonError; |     QJsonParseError jsonError; | ||||||
|     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); |     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); | ||||||
| @@ -424,9 +417,7 @@ bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) | |||||||
|  |  | ||||||
| bool parseRolloutResponse(QByteArray & data, bool& result) { | bool parseRolloutResponse(QByteArray & data, bool& result) { | ||||||
|     qDebug() << "Parsing Rollout response..."; |     qDebug() << "Parsing Rollout response..."; | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     QJsonParseError jsonError; |     QJsonParseError jsonError; | ||||||
|     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); |     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); | ||||||
| @@ -455,9 +446,7 @@ bool parseRolloutResponse(QByteArray & data, bool& result) { | |||||||
| bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { | bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) { | ||||||
|     QJsonParseError jsonError; |     QJsonParseError jsonError; | ||||||
|     qDebug() << "Parsing Mojang response..."; |     qDebug() << "Parsing Mojang response..."; | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); |     QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); | ||||||
|     if(jsonError.error) { |     if(jsonError.error) { | ||||||
|         qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString(); |         qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString(); | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| #include <QNetworkRequest> | #include <QNetworkRequest> | ||||||
| #include <QUuid> | #include <QUuid> | ||||||
|  |  | ||||||
|  | #include "Logging.h" | ||||||
| #include "minecraft/auth/AuthRequest.h" | #include "minecraft/auth/AuthRequest.h" | ||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
|  |  | ||||||
| @@ -41,9 +42,7 @@ void EntitlementsStep::onRequestDone( | |||||||
|     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); |     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); | ||||||
|     requestor->deleteLater(); |     requestor->deleteLater(); | ||||||
|  |  | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     // TODO: check presence of same entitlementsRequestId? |     // TODO: check presence of same entitlementsRequestId? | ||||||
|     // TODO: validate JWTs? |     // TODO: validate JWTs? | ||||||
|   | |||||||
| @@ -2,9 +2,10 @@ | |||||||
|  |  | ||||||
| #include <QNetworkRequest> | #include <QNetworkRequest> | ||||||
|  |  | ||||||
|  | #include "Logging.h" | ||||||
|  | #include "minecraft/auth/AccountTask.h" | ||||||
| #include "minecraft/auth/AuthRequest.h" | #include "minecraft/auth/AuthRequest.h" | ||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
| #include "minecraft/auth/AccountTask.h" |  | ||||||
| #include "net/NetUtils.h" | #include "net/NetUtils.h" | ||||||
|  |  | ||||||
| LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) { | LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) { | ||||||
| @@ -51,14 +52,10 @@ void LauncherLoginStep::onRequestDone( | |||||||
|     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); |     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); | ||||||
|     requestor->deleteLater(); |     requestor->deleteLater(); | ||||||
|  |  | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|     if (error != QNetworkReply::NoError) { |     if (error != QNetworkReply::NoError) { | ||||||
|         qWarning() << "Reply error:" << error; |         qWarning() << "Reply error:" << error; | ||||||
| #ifndef NDEBUG |         qCDebug(authCredentials()) << data; | ||||||
|         qDebug() << data; |  | ||||||
| #endif |  | ||||||
|         if (Net::isApplicationError(error)) { |         if (Net::isApplicationError(error)) { | ||||||
|             emit finished( |             emit finished( | ||||||
|                 AccountTaskState::STATE_FAILED_SOFT, |                 AccountTaskState::STATE_FAILED_SOFT, | ||||||
| @@ -76,9 +73,7 @@ void LauncherLoginStep::onRequestDone( | |||||||
|  |  | ||||||
|     if(!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { |     if(!Parsers::parseMojangResponse(data, m_data->yggdrasilToken)) { | ||||||
|         qWarning() << "Could not parse login_with_xbox response..."; |         qWarning() << "Could not parse login_with_xbox response..."; | ||||||
| #ifndef NDEBUG |         qCDebug(authCredentials()) << data; | ||||||
|         qDebug() << data; |  | ||||||
| #endif |  | ||||||
|         emit finished( |         emit finished( | ||||||
|             AccountTaskState::STATE_FAILED_SOFT, |             AccountTaskState::STATE_FAILED_SOFT, | ||||||
|             tr("Failed to parse the Minecraft access token response.") |             tr("Failed to parse the Minecraft access token response.") | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ | |||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
|  |  | ||||||
| #include "Application.h" | #include "Application.h" | ||||||
|  | #include "Logging.h" | ||||||
|  |  | ||||||
| using OAuth2 = Katabasis::DeviceFlow; | using OAuth2 = Katabasis::DeviceFlow; | ||||||
| using Activity = Katabasis::Activity; | using Activity = Katabasis::Activity; | ||||||
| @@ -117,14 +118,12 @@ void MSAStep::onOAuthActivityChanged(Katabasis::Activity activity) { | |||||||
|             // Succeeded or did not invalidate tokens |             // Succeeded or did not invalidate tokens | ||||||
|             emit hideVerificationUriAndCode(); |             emit hideVerificationUriAndCode(); | ||||||
|             QVariantMap extraTokens = m_oauth2->extraTokens(); |             QVariantMap extraTokens = m_oauth2->extraTokens(); | ||||||
| #ifndef NDEBUG |  | ||||||
|             if (!extraTokens.isEmpty()) { |             if (!extraTokens.isEmpty()) { | ||||||
|                 qDebug() << "Extra tokens in response:"; |                 qCDebug(authCredentials()) << "Extra tokens in response:"; | ||||||
|                 foreach (QString key, extraTokens.keys()) { |                 foreach (QString key, extraTokens.keys()) { | ||||||
|                     qDebug() << "\t" << key << ":" << extraTokens.value(key); |                     qCDebug(authCredentials()) << "\t" << key << ":" << extraTokens.value(key); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| #endif |  | ||||||
|             emit finished(AccountTaskState::STATE_WORKING, tr("Got ")); |             emit finished(AccountTaskState::STATE_WORKING, tr("Got ")); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| #include <QNetworkRequest> | #include <QNetworkRequest> | ||||||
|  |  | ||||||
|  | #include "Logging.h" | ||||||
| #include "minecraft/auth/AuthRequest.h" | #include "minecraft/auth/AuthRequest.h" | ||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
| #include "net/NetUtils.h" | #include "net/NetUtils.h" | ||||||
| @@ -40,9 +41,7 @@ void MinecraftProfileStep::onRequestDone( | |||||||
|     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); |     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); | ||||||
|     requestor->deleteLater(); |     requestor->deleteLater(); | ||||||
|  |  | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|     if (error == QNetworkReply::ContentNotFoundError) { |     if (error == QNetworkReply::ContentNotFoundError) { | ||||||
|         // NOTE: Succeed even if we do not have a profile. This is a valid account state. |         // NOTE: Succeed even if we do not have a profile. This is a valid account state. | ||||||
|         if(m_data->type == AccountType::Mojang) { |         if(m_data->type == AccountType::Mojang) { | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| #include <QNetworkRequest> | #include <QNetworkRequest> | ||||||
|  |  | ||||||
|  | #include "Logging.h" | ||||||
| #include "minecraft/auth/AuthRequest.h" | #include "minecraft/auth/AuthRequest.h" | ||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
| #include "net/NetUtils.h" | #include "net/NetUtils.h" | ||||||
| @@ -43,9 +44,7 @@ void MinecraftProfileStepMojang::onRequestDone( | |||||||
|     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); |     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); | ||||||
|     requestor->deleteLater(); |     requestor->deleteLater(); | ||||||
|  |  | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|     if (error == QNetworkReply::ContentNotFoundError) { |     if (error == QNetworkReply::ContentNotFoundError) { | ||||||
|         // NOTE: Succeed even if we do not have a profile. This is a valid account state. |         // NOTE: Succeed even if we do not have a profile. This is a valid account state. | ||||||
|         if(m_data->type == AccountType::Mojang) { |         if(m_data->type == AccountType::Mojang) { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
| #include <QJsonParseError> | #include <QJsonParseError> | ||||||
| #include <QJsonDocument> | #include <QJsonDocument> | ||||||
|  |  | ||||||
|  | #include "Logging.h" | ||||||
| #include "minecraft/auth/AuthRequest.h" | #include "minecraft/auth/AuthRequest.h" | ||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
| #include "net/NetUtils.h" | #include "net/NetUtils.h" | ||||||
| @@ -58,9 +59,7 @@ void XboxAuthorizationStep::onRequestDone( | |||||||
|     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); |     auto requestor = qobject_cast<AuthRequest *>(QObject::sender()); | ||||||
|     requestor->deleteLater(); |     requestor->deleteLater(); | ||||||
|  |  | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << data; | ||||||
|     qDebug() << data; |  | ||||||
| #endif |  | ||||||
|     if (error != QNetworkReply::NoError) { |     if (error != QNetworkReply::NoError) { | ||||||
|         qWarning() << "Reply error:" << error; |         qWarning() << "Reply error:" << error; | ||||||
|         if (Net::isApplicationError(error)) { |         if (Net::isApplicationError(error)) { | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| #include <QNetworkRequest> | #include <QNetworkRequest> | ||||||
| #include <QUrlQuery> | #include <QUrlQuery> | ||||||
|  |  | ||||||
|  | #include "Logging.h" | ||||||
| #include "minecraft/auth/AuthRequest.h" | #include "minecraft/auth/AuthRequest.h" | ||||||
| #include "minecraft/auth/Parsers.h" | #include "minecraft/auth/Parsers.h" | ||||||
| #include "net/NetUtils.h" | #include "net/NetUtils.h" | ||||||
| @@ -56,9 +56,7 @@ void XboxProfileStep::onRequestDone( | |||||||
|  |  | ||||||
|     if (error != QNetworkReply::NoError) { |     if (error != QNetworkReply::NoError) { | ||||||
|         qWarning() << "Reply error:" << error; |         qWarning() << "Reply error:" << error; | ||||||
| #ifndef NDEBUG |         qCDebug(authCredentials()) << data; | ||||||
|         qDebug() << data; |  | ||||||
| #endif |  | ||||||
|         if (Net::isApplicationError(error)) { |         if (Net::isApplicationError(error)) { | ||||||
|             emit finished( |             emit finished( | ||||||
|                 AccountTaskState::STATE_FAILED_SOFT, |                 AccountTaskState::STATE_FAILED_SOFT, | ||||||
| @@ -74,9 +72,7 @@ void XboxProfileStep::onRequestDone( | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifndef NDEBUG |     qCDebug(authCredentials()) << "XBox profile: " << data; | ||||||
|     qDebug() << "XBox profile: " << data; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile")); |     emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox profile")); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -44,6 +44,8 @@ | |||||||
| #include "MetadataHandler.h" | #include "MetadataHandler.h" | ||||||
| #include "Version.h" | #include "Version.h" | ||||||
|  |  | ||||||
|  | static ModPlatform::ProviderCapabilities ProviderCaps; | ||||||
|  |  | ||||||
| Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() | Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() | ||||||
| { | { | ||||||
|     m_enabled = (file.suffix() != "disabled"); |     m_enabled = (file.suffix() != "disabled"); | ||||||
| @@ -91,6 +93,11 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const | |||||||
|             if (this_ver < other_ver) |             if (this_ver < other_ver) | ||||||
|                 return { -1, type == SortType::VERSION }; |                 return { -1, type == SortType::VERSION }; | ||||||
|         } |         } | ||||||
|  |         case SortType::PROVIDER: { | ||||||
|  |             auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); | ||||||
|  |             if (compare_result != 0) | ||||||
|  |                 return { compare_result, type == SortType::PROVIDER }; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     return { 0, false }; |     return { 0, false }; | ||||||
| } | } | ||||||
| @@ -189,4 +196,11 @@ void Mod::finishResolvingWithDetails(ModDetails&& details) | |||||||
|     m_local_details = std::move(details); |     m_local_details = std::move(details); | ||||||
|     if (metadata) |     if (metadata) | ||||||
|         setMetadata(std::move(metadata)); |         setMetadata(std::move(metadata)); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | auto Mod::provider() const -> std::optional<QString> | ||||||
|  | { | ||||||
|  |     if (metadata()) | ||||||
|  |         return ProviderCaps.readableName(metadata()->provider); | ||||||
|  |     return {}; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,6 +39,8 @@ | |||||||
| #include <QFileInfo> | #include <QFileInfo> | ||||||
| #include <QList> | #include <QList> | ||||||
|  |  | ||||||
|  | #include <optional> | ||||||
|  |  | ||||||
| #include "Resource.h" | #include "Resource.h" | ||||||
| #include "ModDetails.h" | #include "ModDetails.h" | ||||||
|  |  | ||||||
| @@ -61,6 +63,7 @@ public: | |||||||
|     auto description() const -> QString; |     auto description() const -> QString; | ||||||
|     auto authors()     const -> QStringList; |     auto authors()     const -> QStringList; | ||||||
|     auto status()      const -> ModStatus; |     auto status()      const -> ModStatus; | ||||||
|  |     auto provider()    const -> std::optional<QString>; | ||||||
|  |  | ||||||
|     auto metadata() -> std::shared_ptr<Metadata::ModStruct>; |     auto metadata() -> std::shared_ptr<Metadata::ModStruct>; | ||||||
|     auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>; |     auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>; | ||||||
|   | |||||||
| @@ -48,10 +48,11 @@ | |||||||
|  |  | ||||||
| #include "minecraft/mod/tasks/LocalModParseTask.h" | #include "minecraft/mod/tasks/LocalModParseTask.h" | ||||||
| #include "minecraft/mod/tasks/ModFolderLoadTask.h" | #include "minecraft/mod/tasks/ModFolderLoadTask.h" | ||||||
|  | #include "modplatform/ModIndex.h" | ||||||
|  |  | ||||||
| ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed) : ResourceFolderModel(QDir(dir)), m_is_indexed(is_indexed) | ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed) : ResourceFolderModel(QDir(dir)), m_is_indexed(is_indexed) | ||||||
| { | { | ||||||
|     m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE }; |     m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; | ||||||
| } | } | ||||||
|  |  | ||||||
| QVariant ModFolderModel::data(const QModelIndex &index, int role) const | QVariant ModFolderModel::data(const QModelIndex &index, int role) const | ||||||
| @@ -82,7 +83,15 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const | |||||||
|         } |         } | ||||||
|         case DateColumn: |         case DateColumn: | ||||||
|             return m_resources[row]->dateTimeChanged(); |             return m_resources[row]->dateTimeChanged(); | ||||||
|  |         case ProviderColumn: { | ||||||
|  |             auto provider = at(row)->provider(); | ||||||
|  |             if (!provider.has_value()) { | ||||||
|  | 	            //: Unknown mod provider (i.e. not Modrinth, CurseForge, etc...) | ||||||
|  |                 return tr("Unknown"); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return provider.value(); | ||||||
|  |         } | ||||||
|         default: |         default: | ||||||
|             return QVariant(); |             return QVariant(); | ||||||
|         } |         } | ||||||
| @@ -118,6 +127,8 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in | |||||||
|             return tr("Version"); |             return tr("Version"); | ||||||
|         case DateColumn: |         case DateColumn: | ||||||
|             return tr("Last changed"); |             return tr("Last changed"); | ||||||
|  |         case ProviderColumn: | ||||||
|  |             return tr("Provider"); | ||||||
|         default: |         default: | ||||||
|             return QVariant(); |             return QVariant(); | ||||||
|         } |         } | ||||||
| @@ -133,6 +144,8 @@ QVariant ModFolderModel::headerData(int section, Qt::Orientation orientation, in | |||||||
|             return tr("The version of the mod."); |             return tr("The version of the mod."); | ||||||
|         case DateColumn: |         case DateColumn: | ||||||
|             return tr("The date and time this mod was last changed (or added)."); |             return tr("The date and time this mod was last changed (or added)."); | ||||||
|  |         case ProviderColumn: | ||||||
|  |             return tr("Where the mod was downloaded from."); | ||||||
|         default: |         default: | ||||||
|             return QVariant(); |             return QVariant(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -67,6 +67,7 @@ public: | |||||||
|         NameColumn, |         NameColumn, | ||||||
|         VersionColumn, |         VersionColumn, | ||||||
|         DateColumn, |         DateColumn, | ||||||
|  |         ProviderColumn, | ||||||
|         NUM_COLUMNS |         NUM_COLUMNS | ||||||
|     }; |     }; | ||||||
|     enum ModStatusAction { |     enum ModStatusAction { | ||||||
|   | |||||||
| @@ -20,7 +20,8 @@ enum class SortType { | |||||||
|     DATE, |     DATE, | ||||||
|     VERSION, |     VERSION, | ||||||
|     ENABLED, |     ENABLED, | ||||||
|     PACK_FORMAT |     PACK_FORMAT, | ||||||
|  |     PROVIDER | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class EnableAction { | enum class EnableAction { | ||||||
|   | |||||||
| @@ -14,6 +14,9 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include "ModListView.h" | #include "ModListView.h" | ||||||
|  |  | ||||||
|  | #include "minecraft/mod/ModFolderModel.h" | ||||||
|  |  | ||||||
| #include <QHeaderView> | #include <QHeaderView> | ||||||
| #include <QMouseEvent> | #include <QMouseEvent> | ||||||
| #include <QPainter> | #include <QPainter> | ||||||
| @@ -62,4 +65,17 @@ void ModListView::setModel ( QAbstractItemModel* model ) | |||||||
|         for(int i = 1; i < head->count(); i++) |         for(int i = 1; i < head->count(); i++) | ||||||
|             head->setSectionResizeMode(i, QHeaderView::ResizeToContents); |             head->setSectionResizeMode(i, QHeaderView::ResizeToContents); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     auto real_model = model; | ||||||
|  |     if (auto proxy_model = dynamic_cast<QSortFilterProxyModel*>(model); proxy_model) | ||||||
|  |         real_model = proxy_model->sourceModel(); | ||||||
|  |  | ||||||
|  |     if (auto mod_model = dynamic_cast<ModFolderModel*>(real_model); mod_model) { | ||||||
|  |         connect(mod_model, &ModFolderModel::updateFinished, this, [this, mod_model]{ | ||||||
|  |             auto mods = mod_model->allMods(); | ||||||
|  |             // Hide the 'Provider' column if no mod has a defined provider! | ||||||
|  |             setColumnHidden(ModFolderModel::Columns::ProviderColumn, | ||||||
|  |                     std::none_of(mods.constBegin(), mods.constEnd(), [](auto const mod){ return mod->provider().has_value(); })); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -38,6 +38,15 @@ set( katabasis_PUBLIC | |||||||
|     include/katabasis/RequestParameter.h |     include/katabasis/RequestParameter.h | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | ecm_qt_declare_logging_category(katabasis_PRIVATE | ||||||
|  |     HEADER KatabasisLogging.h  # NOTE: this won't be in src/, but CMAKE_BINARY_DIR/src isn't included by default so this should be fine | ||||||
|  |     IDENTIFIER katabasisCredentials | ||||||
|  |     CATEGORY_NAME "katabasis.credentials" | ||||||
|  |     DEFAULT_SEVERITY Warning | ||||||
|  |     DESCRIPTION "Secrets and credentials from Katabasis" | ||||||
|  |     EXPORT "Katabasis" | ||||||
|  | ) | ||||||
|  |  | ||||||
| add_library( Katabasis STATIC ${katabasis_PRIVATE} ${katabasis_PUBLIC} ) | add_library( Katabasis STATIC ${katabasis_PRIVATE} ${katabasis_PUBLIC} ) | ||||||
| target_link_libraries(Katabasis Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network) | target_link_libraries(Katabasis Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <QLoggingCategory> | ||||||
| #include <QNetworkAccessManager> | #include <QNetworkAccessManager> | ||||||
| #include <QNetworkRequest> | #include <QNetworkRequest> | ||||||
| #include <QNetworkReply> | #include <QNetworkReply> | ||||||
|   | |||||||
| @@ -19,9 +19,11 @@ | |||||||
| #include "katabasis/PollServer.h" | #include "katabasis/PollServer.h" | ||||||
| #include "katabasis/Globals.h" | #include "katabasis/Globals.h" | ||||||
|  |  | ||||||
|  | #include "KatabasisLogging.h" | ||||||
| #include "JsonResponse.h" | #include "JsonResponse.h" | ||||||
|  |  | ||||||
| namespace { | namespace { | ||||||
|  |  | ||||||
| // ref: https://tools.ietf.org/html/rfc8628#section-3.2 | // ref: https://tools.ietf.org/html/rfc8628#section-3.2 | ||||||
| // Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both. | // Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both. | ||||||
| bool hasMandatoryDeviceAuthParams(const QVariantMap& params) | bool hasMandatoryDeviceAuthParams(const QVariantMap& params) | ||||||
| @@ -333,9 +335,7 @@ QString DeviceFlow::refreshToken() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void DeviceFlow::setRefreshToken(const QString &v) { | void DeviceFlow::setRefreshToken(const QString &v) { | ||||||
| #ifndef NDEBUG |     qCDebug(katabasisCredentials) << "new refresh token:" << v; | ||||||
|     qDebug() << "DeviceFlow::setRefreshToken" << v << "..."; |  | ||||||
| #endif |  | ||||||
|     token_.refresh_token = v; |     token_.refresh_token = v; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,38 +19,49 @@ | |||||||
|     <p>Features:</p> |     <p>Features:</p> | ||||||
|     <ul> |     <ul> | ||||||
|       <li>Easily install game modifications, such as Fabric, Forge and Quilt</li> |       <li>Easily install game modifications, such as Fabric, Forge and Quilt</li> | ||||||
|       <li>Control your Java settings</li> |       <li>Easily install and update modpacks from the Launcher</li> | ||||||
|  |       <li>Control your Java settings, and enable Mangohud or Gamemode with a toggle</li> | ||||||
|       <li>Manage worlds and resource packs from the launcher</li> |       <li>Manage worlds and resource packs from the launcher</li> | ||||||
|       <li>See logs and other details easily</li> |       <li>See logs and other details easily through a dashboard</li> | ||||||
|       <li>Kill Minecraft in case of a crash/freeze</li> |       <li>Kill Minecraft in case of a crash/freeze</li> | ||||||
|       <li>Isolate Minecraft instances to keep everything clean</li> |       <li>Isolate Minecraft instances to keep everything clean</li> | ||||||
|       <li>Install and update mods directly from the launcher</li> |       <li>Install and update mods directly from the launcher</li> | ||||||
|  |       <li>Customize the launcher with themes, and more</li> | ||||||
|  |       <li>And cat :3</li> | ||||||
|     </ul> |     </ul> | ||||||
|   </description> |   </description> | ||||||
|   <screenshots> |   <screenshots> | ||||||
|     <screenshot type="default"> |     <screenshot type="default"> | ||||||
|       <caption>The main Prism Launcher window</caption> |       <caption>The main Prism Launcher window</caption> | ||||||
|       <image type="source" width="976" height="764">https://prismlauncher.org/img/screenshots/LauncherDark.png</image> |       <image type="source" width="1030" height="764">https://prismlauncher.org/img/screenshots/LauncherDark.png</image> | ||||||
|     </screenshot> |     </screenshot> | ||||||
|     <screenshot> |     <screenshot> | ||||||
|       <caption>Modpack installation</caption> |       <caption>Modpack installation</caption> | ||||||
|       <image type="source" width="1103" height="954">https://prismlauncher.org/img/screenshots/ModpackInstallDark.png</image> |       <image type="source" width="1126" height="850">https://prismlauncher.org/img/screenshots/ModpackInstallDark.png</image> | ||||||
|  |     </screenshot> | ||||||
|  |     <screenshot> | ||||||
|  |       <caption>Modpack updating</caption> | ||||||
|  |       <image type="source" width="930" height="677">https://prismlauncher.org/img/screenshots/ModpackUpdateDark.png</image> | ||||||
|     </screenshot> |     </screenshot> | ||||||
|     <screenshot> |     <screenshot> | ||||||
|       <caption>Mod installation</caption> |       <caption>Mod installation</caption> | ||||||
|       <image type="source" width="1036" height="700">https://prismlauncher.org/img/screenshots/ModInstallDark.png</image> |       <image type="source" width="848" height="558">https://prismlauncher.org/img/screenshots/ModInstallDark.png</image> | ||||||
|     </screenshot> |     </screenshot> | ||||||
|     <screenshot> |     <screenshot> | ||||||
|       <caption>Mod updating</caption> |       <caption>Mod updating</caption> | ||||||
|       <image type="source" width="930" height="858">https://prismlauncher.org/img/screenshots/ModUpdateDark.png</image> |       <image type="source" width="860" height="748">https://prismlauncher.org/img/screenshots/ModUpdateDark.png</image> | ||||||
|     </screenshot> |     </screenshot> | ||||||
|     <screenshot> |     <screenshot> | ||||||
|       <caption>Instance management</caption> |       <caption>Instance management</caption> | ||||||
|       <image type="source" width="1083" height="735">https://prismlauncher.org/img/screenshots/PropertiesDark.png</image> |       <image type="source" width="960" height="659">https://prismlauncher.org/img/screenshots/PropertiesDark.png</image> | ||||||
|     </screenshot> |     </screenshot> | ||||||
|     <screenshot> |     <screenshot> | ||||||
|       <caption>Cat :)</caption> |       <caption>Cat :3</caption> | ||||||
|       <image type="source" width="931" height="759">https://prismlauncher.org/img/screenshots/LauncherCatDark.png</image> |       <image type="source" width="1042" height="754">https://prismlauncher.org/img/screenshots/LauncherCatDark.png</image> | ||||||
|  |     </screenshot> | ||||||
|  |     <screenshot> | ||||||
|  |       <caption>Customization</caption> | ||||||
|  |       <image type="source" width="1040" height="752">https://prismlauncher.org/img/screenshots/CustomizeDark.png</image> | ||||||
|     </screenshot> |     </screenshot> | ||||||
|   </screenshots> |   </screenshots> | ||||||
|   <releases> |   <releases> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Aaron Sonin
					Aaron Sonin