Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into auth3
This commit is contained in:
commit
27ee8374e7
12
flake.lock
generated
12
flake.lock
generated
@ -75,16 +75,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1717774105,
|
"lastModified": 1718276985,
|
||||||
"narHash": "sha256-HV97wqUQv9wvptiHCb3Y0/YH0lJ60uZ8FYfEOIzYEqI=",
|
"narHash": "sha256-u1fA0DYQYdeG+5kDm1bOoGcHtX0rtC7qs2YA2N1X++I=",
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d226935fd75012939397c83f6c385e4d6d832288",
|
"rev": "3f84a279f1a6290ce154c5531378acc827836fbb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"ref": "nixpkgs-unstable",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
flake-parts = {
|
flake-parts = {
|
||||||
url = "github:hercules-ci/flake-parts";
|
url = "github:hercules-ci/flake-parts";
|
||||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
|
@ -1034,6 +1034,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/skins/SkinManageDialog.h
|
ui/dialogs/skins/SkinManageDialog.h
|
||||||
|
|
||||||
# GUI - widgets
|
# GUI - widgets
|
||||||
|
ui/widgets/CheckComboBox.cpp
|
||||||
|
ui/widgets/CheckComboBox.h
|
||||||
ui/widgets/Common.cpp
|
ui/widgets/Common.cpp
|
||||||
ui/widgets/Common.h
|
ui/widgets/Common.h
|
||||||
ui/widgets/CustomCommands.cpp
|
ui/widgets/CustomCommands.cpp
|
||||||
|
@ -815,20 +815,11 @@ QString NormalizePath(QString path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString removeDuplicates(QString a)
|
static const QString BAD_WIN_CHARS = "<>:\"|?*\r\n";
|
||||||
{
|
|
||||||
auto b = a.split("");
|
|
||||||
b.removeDuplicates();
|
|
||||||
return b.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QString BAD_WIN_CHARS = "\"?<>:*|\r\n";
|
|
||||||
|
|
||||||
static const QString BAD_FAT_CHARS = "<>:\"|?*+.,;=[]!";
|
|
||||||
static const QString BAD_NTFS_CHARS = "<>:\"|?*";
|
static const QString BAD_NTFS_CHARS = "<>:\"|?*";
|
||||||
static const QString BAD_HFS_CHARS = ":";
|
static const QString BAD_HFS_CHARS = ":";
|
||||||
|
|
||||||
static const QString BAD_FILENAME_CHARS = removeDuplicates(BAD_WIN_CHARS + BAD_FAT_CHARS + BAD_NTFS_CHARS + BAD_HFS_CHARS) + "\\/";
|
static const QString BAD_FILENAME_CHARS = BAD_WIN_CHARS + "\\/";
|
||||||
|
|
||||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||||
{
|
{
|
||||||
@ -847,9 +838,8 @@ QString RemoveInvalidPathChars(QString path, QChar replaceWith)
|
|||||||
|
|
||||||
// the null character is ignored in this check as it was not a problem until now
|
// the null character is ignored in this check as it was not a problem until now
|
||||||
switch (statFS(path).fsType) {
|
switch (statFS(path).fsType) {
|
||||||
case FilesystemType::FAT:
|
case FilesystemType::FAT: // similar to NTFS
|
||||||
invalidChars += BAD_FAT_CHARS;
|
/* fallthrough */
|
||||||
break;
|
|
||||||
case FilesystemType::NTFS:
|
case FilesystemType::NTFS:
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
|
case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
|
||||||
|
@ -84,7 +84,7 @@ void LaunchController::decideAccount()
|
|||||||
|
|
||||||
// Find an account to use.
|
// Find an account to use.
|
||||||
auto accounts = APPLICATION->accounts();
|
auto accounts = APPLICATION->accounts();
|
||||||
if (accounts->count() <= 0) {
|
if (accounts->count() <= 0 || !accounts->anyAccountIsValid()) {
|
||||||
// Tell the user they need to log in at least one account in order to play.
|
// Tell the user they need to log in at least one account in order to play.
|
||||||
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
||||||
tr("In order to play Minecraft, you must have at least one Microsoft "
|
tr("In order to play Minecraft, you must have at least one Microsoft "
|
||||||
@ -128,12 +128,63 @@ void LaunchController::decideAccount()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LaunchController::askPlayDemo()
|
||||||
|
{
|
||||||
|
QMessageBox box(m_parentWidget);
|
||||||
|
box.setWindowTitle(tr("Play demo?"));
|
||||||
|
box.setText(
|
||||||
|
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
||||||
|
"the demo?"));
|
||||||
|
box.setIcon(QMessageBox::Warning);
|
||||||
|
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
||||||
|
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
||||||
|
box.setDefaultButton(cancelButton);
|
||||||
|
|
||||||
|
box.exec();
|
||||||
|
return box.clickedButton() == demoButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok)
|
||||||
|
{
|
||||||
|
// we ask the user for a player name
|
||||||
|
QString message = tr("Choose your offline mode player name.");
|
||||||
|
if (demo) {
|
||||||
|
message = tr("Choose your demo mode player name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||||
|
QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName;
|
||||||
|
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
||||||
|
if (!ok)
|
||||||
|
return {};
|
||||||
|
if (name.length()) {
|
||||||
|
usedname = name;
|
||||||
|
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||||
|
}
|
||||||
|
return usedname;
|
||||||
|
}
|
||||||
|
|
||||||
void LaunchController::login()
|
void LaunchController::login()
|
||||||
{
|
{
|
||||||
decideAccount();
|
decideAccount();
|
||||||
|
|
||||||
// if no account is selected, we bail
|
|
||||||
if (!m_accountToUse) {
|
if (!m_accountToUse) {
|
||||||
|
// if no account is selected, ask about demo
|
||||||
|
if (!m_demo) {
|
||||||
|
m_demo = askPlayDemo();
|
||||||
|
}
|
||||||
|
if (m_demo) {
|
||||||
|
// we ask the user for a player name
|
||||||
|
bool ok = false;
|
||||||
|
auto name = askOfflineName("Player", m_demo, ok);
|
||||||
|
if (ok) {
|
||||||
|
m_session = std::make_shared<AuthSession>();
|
||||||
|
m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]")));
|
||||||
|
launchInstance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if no account is selected, we bail
|
||||||
emitFailed(tr("No account selected for launch."));
|
emitFailed(tr("No account selected for launch."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -180,24 +231,12 @@ void LaunchController::login()
|
|||||||
if (!m_session->wants_online) {
|
if (!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||||
QString message = tr("Choose your offline mode player name.");
|
|
||||||
if (m_session->demo) {
|
|
||||||
message = tr("Choose your demo mode player name.");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
|
||||||
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
|
||||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
tryagain = false;
|
tryagain = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (name.length()) {
|
m_session->MakeOffline(name);
|
||||||
usedname = name;
|
|
||||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
|
||||||
}
|
|
||||||
m_session->MakeOffline(usedname);
|
|
||||||
// offline flavored game from here :3
|
// offline flavored game from here :3
|
||||||
}
|
}
|
||||||
if (m_accountToUse->ownsMinecraft()) {
|
if (m_accountToUse->ownsMinecraft()) {
|
||||||
@ -217,20 +256,10 @@ void LaunchController::login()
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// play demo ?
|
// play demo ?
|
||||||
QMessageBox box(m_parentWidget);
|
if (!m_session->demo) {
|
||||||
box.setWindowTitle(tr("Play demo?"));
|
m_session->demo = askPlayDemo();
|
||||||
box.setText(
|
}
|
||||||
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
if (m_session->demo) { // play demo here
|
||||||
"the demo?"));
|
|
||||||
box.setIcon(QMessageBox::Warning);
|
|
||||||
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
|
||||||
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
|
||||||
box.setDefaultButton(cancelButton);
|
|
||||||
|
|
||||||
box.exec();
|
|
||||||
if (box.clickedButton() == demoButton) {
|
|
||||||
// play demo here
|
|
||||||
m_session->MakeDemo();
|
|
||||||
launchInstance();
|
launchInstance();
|
||||||
} else {
|
} else {
|
||||||
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
||||||
|
@ -74,6 +74,8 @@ class LaunchController : public Task {
|
|||||||
void login();
|
void login();
|
||||||
void launchInstance();
|
void launchInstance();
|
||||||
void decideAccount();
|
void decideAccount();
|
||||||
|
bool askPlayDemo();
|
||||||
|
QString askOfflineName(QString playerName, bool demo, bool& ok);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void readyForLaunch();
|
void readyForLaunch();
|
||||||
|
@ -213,7 +213,7 @@ class ExtractZipTask : public Task {
|
|||||||
{}
|
{}
|
||||||
virtual ~ExtractZipTask() = default;
|
virtual ~ExtractZipTask() = default;
|
||||||
|
|
||||||
typedef std::optional<QString> ZipResult;
|
using ZipResult = std::optional<QString>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void executeTask() override;
|
virtual void executeTask() override;
|
||||||
|
@ -30,8 +30,13 @@ bool AuthSession::MakeOffline(QString offline_playername)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthSession::MakeDemo()
|
void AuthSession::MakeDemo(QString name, QString u)
|
||||||
{
|
{
|
||||||
player_name = "Player";
|
wants_online = false;
|
||||||
demo = true;
|
demo = true;
|
||||||
}
|
uuid = u;
|
||||||
|
session = "-";
|
||||||
|
access_token = "0";
|
||||||
|
player_name = name;
|
||||||
|
status = PlayableOnline; // needs online to download the assets
|
||||||
|
};
|
@ -10,7 +10,7 @@ class QNetworkAccessManager;
|
|||||||
|
|
||||||
struct AuthSession {
|
struct AuthSession {
|
||||||
bool MakeOffline(QString offline_playername);
|
bool MakeOffline(QString offline_playername);
|
||||||
void MakeDemo();
|
void MakeDemo(QString name, QString uuid);
|
||||||
|
|
||||||
QString serializeUserProperties();
|
QString serializeUserProperties();
|
||||||
|
|
||||||
|
@ -83,8 +83,6 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username)
|
|||||||
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||||
account->data.yggdrasilToken.extra["userName"] = username;
|
account->data.yggdrasilToken.extra["userName"] = username;
|
||||||
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
|
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
|
||||||
account->data.minecraftEntitlement.ownsMinecraft = true;
|
|
||||||
account->data.minecraftEntitlement.canPlayMinecraft = true;
|
|
||||||
account->data.minecraftProfile.id = uuidFromUsername(username).toString().remove(QRegularExpression("[{}-]"));
|
account->data.minecraftProfile.id = uuidFromUsername(username).toString().remove(QRegularExpression("[{}-]"));
|
||||||
account->data.minecraftProfile.name = username;
|
account->data.minecraftProfile.name = username;
|
||||||
account->data.minecraftProfile.validity = Validity::Certain;
|
account->data.minecraftProfile.validity = Validity::Certain;
|
||||||
@ -253,6 +251,8 @@ void MinecraftAccount::fillSession(AuthSessionPtr session)
|
|||||||
session->player_name = data.profileName();
|
session->player_name = data.profileName();
|
||||||
// profile ID
|
// profile ID
|
||||||
session->uuid = data.profileId();
|
session->uuid = data.profileId();
|
||||||
|
if (session->uuid.isEmpty())
|
||||||
|
session->uuid = uuidFromUsername(session->player_name).toString().remove(QRegularExpression("[{}-]"));
|
||||||
// 'legacy' or 'mojang', depending on account type
|
// 'legacy' or 'mojang', depending on account type
|
||||||
session->user_type = typeString();
|
session->user_type = typeString();
|
||||||
if (!session->access_token.isEmpty()) {
|
if (!session->access_token.isEmpty()) {
|
||||||
|
@ -116,7 +116,7 @@ class MinecraftAccount : public QObject, public Usable {
|
|||||||
|
|
||||||
[[nodiscard]] AccountType accountType() const noexcept { return data.type; }
|
[[nodiscard]] AccountType accountType() const noexcept { return data.type; }
|
||||||
|
|
||||||
bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; }
|
bool ownsMinecraft() const { return data.type != AccountType::Offline && data.minecraftEntitlement.ownsMinecraft; }
|
||||||
|
|
||||||
bool hasProfile() const { return data.profileId().size() != 0; }
|
bool hasProfile() const { return data.profileId().size() != 0; }
|
||||||
|
|
||||||
|
@ -28,15 +28,52 @@
|
|||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
|
|
||||||
// Values taken from:
|
// Values taken from:
|
||||||
// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22
|
// https://minecraft.wiki/w/Pack_format#List_of_data_pack_formats
|
||||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { { 4, { Version("1.13"), Version("1.14.4") } },
|
||||||
{ 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } },
|
{ 5, { Version("1.15"), Version("1.16.1") } },
|
||||||
{ 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } },
|
{ 6, { Version("1.16.2"), Version("1.16.5") } },
|
||||||
{ 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } },
|
{ 7, { Version("1.17"), Version("1.17.1") } },
|
||||||
{ 10, { Version("1.19"), Version("1.19.3") } }, { 11, { Version("23w03a"), Version("23w05a") } },
|
{ 8, { Version("1.18"), Version("1.18.1") } },
|
||||||
{ 12, { Version("1.19.4"), Version("1.19.4") } }, { 13, { Version("23w12a"), Version("23w14a") } },
|
{ 9, { Version("1.18.2"), Version("1.18.2") } },
|
||||||
{ 14, { Version("23w16a"), Version("23w17a") } }, { 15, { Version("1.20"), Version("1.20") } },
|
{ 10, { Version("1.19"), Version("1.19.3") } },
|
||||||
};
|
{ 11, { Version("23w03a"), Version("23w05a") } },
|
||||||
|
{ 12, { Version("1.19.4"), Version("1.19.4") } },
|
||||||
|
{ 13, { Version("23w12a"), Version("23w14a") } },
|
||||||
|
{ 14, { Version("23w16a"), Version("23w17a") } },
|
||||||
|
{ 15, { Version("1.20"), Version("1.20.1") } },
|
||||||
|
{ 16, { Version("23w31a"), Version("23w31a") } },
|
||||||
|
{ 17, { Version("23w32a"), Version("23w35a") } },
|
||||||
|
{ 18, { Version("1.20.2"), Version("1.20.2") } },
|
||||||
|
{ 19, { Version("23w40a"), Version("23w40a") } },
|
||||||
|
{ 20, { Version("23w41a"), Version("23w41a") } },
|
||||||
|
{ 21, { Version("23w42a"), Version("23w42a") } },
|
||||||
|
{ 22, { Version("23w43a"), Version("23w43b") } },
|
||||||
|
{ 23, { Version("23w44a"), Version("23w44a") } },
|
||||||
|
{ 24, { Version("23w45a"), Version("23w45a") } },
|
||||||
|
{ 25, { Version("23w46a"), Version("23w46a") } },
|
||||||
|
{ 26, { Version("1.20.3"), Version("1.20.4") } },
|
||||||
|
{ 27, { Version("23w51a"), Version("23w51b") } },
|
||||||
|
{ 28, { Version("24w05a"), Version("24w05b") } },
|
||||||
|
{ 29, { Version("24w04a"), Version("24w04a") } },
|
||||||
|
{ 30, { Version("24w05a"), Version("24w05b") } },
|
||||||
|
{ 31, { Version("24w06a"), Version("24w06a") } },
|
||||||
|
{ 32, { Version("24w07a"), Version("24w07a") } },
|
||||||
|
{ 33, { Version("24w09a"), Version("24w09a") } },
|
||||||
|
{ 34, { Version("24w10a"), Version("24w10a") } },
|
||||||
|
{ 35, { Version("24w11a"), Version("24w11a") } },
|
||||||
|
{ 36, { Version("24w12a"), Version("24w12a") } },
|
||||||
|
{ 37, { Version("24w13a"), Version("24w13a") } },
|
||||||
|
{ 38, { Version("24w14a"), Version("24w14a") } },
|
||||||
|
{ 39, { Version("1.20.5-pre1"), Version("1.20.5-pre1") } },
|
||||||
|
{ 40, { Version("1.20.5-pre2"), Version("1.20.5-pre2") } },
|
||||||
|
{ 41, { Version("1.20.5"), Version("1.20.6") } },
|
||||||
|
{ 42, { Version("24w18a"), Version("24w18a") } },
|
||||||
|
{ 43, { Version("24w19a"), Version("24w19b") } },
|
||||||
|
{ 44, { Version("24w20a"), Version("24w20a") } },
|
||||||
|
{ 45, { Version("21w21a"), Version("21w21b") } },
|
||||||
|
{ 46, { Version("1.21-pre1"), Version("1.21-pre1") } },
|
||||||
|
{ 47, { Version("1.21-pre2"), Version("1.21-pre2") } },
|
||||||
|
{ 48, { Version("1.21"), Version("1.21") } } };
|
||||||
|
|
||||||
void DataPack::setPackFormat(int new_format_id)
|
void DataPack::setPackFormat(int new_format_id)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -52,4 +53,6 @@ class Metadata {
|
|||||||
static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); }
|
static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); }
|
||||||
|
|
||||||
static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); }
|
static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); }
|
||||||
|
|
||||||
|
static auto modSideToString(ModSide side) -> QString { return Packwiz::V1::sideToString(side); }
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -47,8 +48,7 @@
|
|||||||
#include "minecraft/mod/ModDetails.h"
|
#include "minecraft/mod/ModDetails.h"
|
||||||
#include "minecraft/mod/Resource.h"
|
#include "minecraft/mod/Resource.h"
|
||||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||||
|
#include "modplatform/ModIndex.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()
|
||||||
{
|
{
|
||||||
@ -103,6 +103,32 @@ int Mod::compare(const Resource& other, SortType type) const
|
|||||||
case SortType::PROVIDER: {
|
case SortType::PROVIDER: {
|
||||||
return QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
return QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
case SortType::SIDE: {
|
||||||
|
if (side() > cast_other->side())
|
||||||
|
return 1;
|
||||||
|
else if (side() < cast_other->side())
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SortType::LOADERS: {
|
||||||
|
if (loaders() > cast_other->loaders())
|
||||||
|
return 1;
|
||||||
|
else if (loaders() < cast_other->loaders())
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SortType::MC_VERSIONS: {
|
||||||
|
auto thisVersion = mcVersions().join(",");
|
||||||
|
auto otherVersion = cast_other->mcVersions().join(",");
|
||||||
|
return QString::compare(thisVersion, otherVersion, Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
case SortType::RELEASE_TYPE: {
|
||||||
|
if (releaseType() > cast_other->releaseType())
|
||||||
|
return 1;
|
||||||
|
else if (releaseType() < cast_other->releaseType())
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -222,7 +248,35 @@ void Mod::finishResolvingWithDetails(ModDetails&& details)
|
|||||||
auto Mod::provider() const -> std::optional<QString>
|
auto Mod::provider() const -> std::optional<QString>
|
||||||
{
|
{
|
||||||
if (metadata())
|
if (metadata())
|
||||||
return ProviderCaps.readableName(metadata()->provider);
|
return ModPlatform::ProviderCapabilities::readableName(metadata()->provider);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mod::side() const -> Metadata::ModSide
|
||||||
|
{
|
||||||
|
if (metadata())
|
||||||
|
return metadata()->side;
|
||||||
|
return Metadata::ModSide::UniversalSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mod::releaseType() const -> ModPlatform::IndexedVersionType
|
||||||
|
{
|
||||||
|
if (metadata())
|
||||||
|
return metadata()->releaseType;
|
||||||
|
return ModPlatform::IndexedVersionType::VersionType::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mod::loaders() const -> ModPlatform::ModLoaderTypes
|
||||||
|
{
|
||||||
|
if (metadata())
|
||||||
|
return metadata()->loaders;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mod::mcVersions() const -> QStringList
|
||||||
|
{
|
||||||
|
if (metadata())
|
||||||
|
return metadata()->mcVersions;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -47,6 +48,7 @@
|
|||||||
|
|
||||||
#include "ModDetails.h"
|
#include "ModDetails.h"
|
||||||
#include "Resource.h"
|
#include "Resource.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
class Mod : public Resource {
|
class Mod : public Resource {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -70,6 +72,10 @@ class Mod : public Resource {
|
|||||||
auto licenses() const -> const QList<ModLicense>&;
|
auto licenses() const -> const QList<ModLicense>&;
|
||||||
auto issueTracker() const -> QString;
|
auto issueTracker() const -> QString;
|
||||||
auto metaurl() const -> QString;
|
auto metaurl() const -> QString;
|
||||||
|
auto side() const -> Metadata::ModSide;
|
||||||
|
auto loaders() const -> ModPlatform::ModLoaderTypes;
|
||||||
|
auto mcVersions() const -> QStringList;
|
||||||
|
auto releaseType() const -> ModPlatform::IndexedVersionType;
|
||||||
|
|
||||||
/** Get the intneral path to the mod's icon file*/
|
/** Get the intneral path to the mod's icon file*/
|
||||||
QString iconPath() const { return m_local_details.icon_file; }
|
QString iconPath() const { return m_local_details.icon_file; }
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -47,12 +48,11 @@
|
|||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "StringUtils.h"
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
#include "minecraft/mod/Resource.h"
|
#include "minecraft/mod/Resource.h"
|
||||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||||
@ -64,14 +64,18 @@
|
|||||||
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
|
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
|
||||||
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
||||||
{
|
{
|
||||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size" });
|
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size", "Side", "Loaders",
|
||||||
m_column_names_translated =
|
"Minecraft Versions", "Release Type" });
|
||||||
QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size") });
|
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"),
|
||||||
|
tr("Size"), tr("Side"), tr("Loaders"), tr("Minecraft Versions"), tr("Release Type") });
|
||||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION,
|
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION,
|
||||||
SortType::DATE, SortType::PROVIDER, SortType::SIZE };
|
SortType::DATE, SortType::PROVIDER, SortType::SIZE, SortType::SIDE,
|
||||||
|
SortType::LOADERS, SortType::MC_VERSIONS, SortType::RELEASE_TYPE };
|
||||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||||
|
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive,
|
||||||
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||||
m_columnsHideable = { false, true, false, true, true, true, true };
|
m_columnsHideable = { false, true, false, true, true, true, true, true, true, true, true };
|
||||||
|
m_columnsHiddenByDefault = { false, false, false, false, false, false, false, true, true, true, true };
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||||
@ -109,6 +113,26 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
|
|
||||||
return provider.value();
|
return provider.value();
|
||||||
}
|
}
|
||||||
|
case SideColumn: {
|
||||||
|
return Metadata::modSideToString(at(row)->side());
|
||||||
|
}
|
||||||
|
case LoadersColumn: {
|
||||||
|
QStringList loaders;
|
||||||
|
auto modLoaders = at(row)->loaders();
|
||||||
|
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader,
|
||||||
|
ModPlatform::Fabric, ModPlatform::Quilt }) {
|
||||||
|
if (modLoaders & loader) {
|
||||||
|
loaders << getModLoaderAsString(loader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loaders.join(", ");
|
||||||
|
}
|
||||||
|
case McVersionsColumn: {
|
||||||
|
return at(row)->mcVersions().join(", ");
|
||||||
|
}
|
||||||
|
case ReleaseTypeColumn: {
|
||||||
|
return at(row)->releaseType().toString();
|
||||||
|
}
|
||||||
case SizeColumn:
|
case SizeColumn:
|
||||||
return m_resources[row]->sizeStr();
|
return m_resources[row]->sizeStr();
|
||||||
default:
|
default:
|
||||||
@ -165,6 +189,10 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio
|
|||||||
case DateColumn:
|
case DateColumn:
|
||||||
case ProviderColumn:
|
case ProviderColumn:
|
||||||
case ImageColumn:
|
case ImageColumn:
|
||||||
|
case SideColumn:
|
||||||
|
case LoadersColumn:
|
||||||
|
case McVersionsColumn:
|
||||||
|
case ReleaseTypeColumn:
|
||||||
case SizeColumn:
|
case SizeColumn:
|
||||||
return columnNames().at(section);
|
return columnNames().at(section);
|
||||||
default:
|
default:
|
||||||
@ -183,6 +211,14 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio
|
|||||||
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:
|
case ProviderColumn:
|
||||||
return tr("Where the mod was downloaded from.");
|
return tr("Where the mod was downloaded from.");
|
||||||
|
case SideColumn:
|
||||||
|
return tr("On what environment the mod is running.");
|
||||||
|
case LoadersColumn:
|
||||||
|
return tr("The mod loader.");
|
||||||
|
case McVersionsColumn:
|
||||||
|
return tr("The supported minecraft versions.");
|
||||||
|
case ReleaseTypeColumn:
|
||||||
|
return tr("The release type.");
|
||||||
case SizeColumn:
|
case SizeColumn:
|
||||||
return tr("The size of the mod.");
|
return tr("The size of the mod.");
|
||||||
default:
|
default:
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -61,7 +62,20 @@ class QFileSystemWatcher;
|
|||||||
class ModFolderModel : public ResourceFolderModel {
|
class ModFolderModel : public ResourceFolderModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS };
|
enum Columns {
|
||||||
|
ActiveColumn = 0,
|
||||||
|
ImageColumn,
|
||||||
|
NameColumn,
|
||||||
|
VersionColumn,
|
||||||
|
DateColumn,
|
||||||
|
ProviderColumn,
|
||||||
|
SizeColumn,
|
||||||
|
SideColumn,
|
||||||
|
LoadersColumn,
|
||||||
|
McVersionsColumn,
|
||||||
|
ReleaseTypeColumn,
|
||||||
|
NUM_COLUMNS
|
||||||
|
};
|
||||||
enum ModStatusAction { Disable, Enable, Toggle };
|
enum ModStatusAction { Disable, Enable, Toggle };
|
||||||
ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
|
ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
|
||||||
|
|
||||||
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@ -15,7 +50,7 @@ enum class ResourceType {
|
|||||||
LITEMOD, //!< The resource is a litemod
|
LITEMOD, //!< The resource is a litemod
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE };
|
enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE, SIDE, LOADERS, MC_VERSIONS, RELEASE_TYPE };
|
||||||
|
|
||||||
enum class EnableAction { ENABLE, DISABLE, TOGGLE };
|
enum class EnableAction { ENABLE, DISABLE, TOGGLE };
|
||||||
|
|
||||||
|
@ -539,6 +539,10 @@ void ResourceFolderModel::saveColumns(QTreeView* tree)
|
|||||||
|
|
||||||
void ResourceFolderModel::loadColumns(QTreeView* tree)
|
void ResourceFolderModel::loadColumns(QTreeView* tree)
|
||||||
{
|
{
|
||||||
|
for (auto i = 0; i < m_columnsHiddenByDefault.size(); ++i) {
|
||||||
|
tree->setColumnHidden(i, m_columnsHiddenByDefault[i]);
|
||||||
|
}
|
||||||
|
|
||||||
auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
|
auto const setting_name = QString("UI/%1_Page/Columns").arg(id());
|
||||||
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
|
auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name)
|
||||||
: m_instance->settings()->registerSetting(setting_name);
|
: m_instance->settings()->registerSetting(setting_name);
|
||||||
|
@ -201,6 +201,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||||
QHeaderView::Interactive };
|
QHeaderView::Interactive };
|
||||||
QList<bool> m_columnsHideable = { false, false, true, true };
|
QList<bool> m_columnsHideable = { false, false, true, true };
|
||||||
|
QList<bool> m_columnsHiddenByDefault = { false, false, false, false };
|
||||||
|
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
BaseInstance* m_instance;
|
BaseInstance* m_instance;
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
||||||
|
|
||||||
// Values taken from:
|
// Values taken from:
|
||||||
// https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
|
// https://minecraft.wiki/w/Pack_format#List_of_resource_pack_formats
|
||||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||||
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
|
{ 1, { Version("1.6.1"), Version("1.8.9") } }, { 2, { Version("1.9"), Version("1.10.2") } },
|
||||||
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
|
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
|
||||||
@ -19,7 +19,16 @@ static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
|||||||
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
|
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
|
||||||
{ 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("22w42a"), Version("22w44a") } },
|
{ 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("22w42a"), Version("22w44a") } },
|
||||||
{ 12, { Version("1.19.3"), Version("1.19.3") } }, { 13, { Version("1.19.4"), Version("1.19.4") } },
|
{ 12, { Version("1.19.3"), Version("1.19.3") } }, { 13, { Version("1.19.4"), Version("1.19.4") } },
|
||||||
{ 14, { Version("1.20"), Version("1.20") } }
|
{ 14, { Version("23w14a"), Version("23w16a") } }, { 15, { Version("1.20"), Version("1.20.1") } },
|
||||||
|
{ 16, { Version("23w31a"), Version("23w31a") } }, { 17, { Version("23w32a"), Version("23w35a") } },
|
||||||
|
{ 18, { Version("1.20.2"), Version("23w16a") } }, { 19, { Version("23w42a"), Version("23w42a") } },
|
||||||
|
{ 20, { Version("23w43a"), Version("23w44a") } }, { 21, { Version("23w45a"), Version("23w46a") } },
|
||||||
|
{ 22, { Version("1.20.3-pre1"), Version("23w51b") } }, { 24, { Version("24w03a"), Version("24w04a") } },
|
||||||
|
{ 25, { Version("24w05a"), Version("24w05b") } }, { 26, { Version("24w06a"), Version("24w07a") } },
|
||||||
|
{ 28, { Version("24w09a"), Version("24w10a") } }, { 29, { Version("24w11a"), Version("24w11a") } },
|
||||||
|
{ 30, { Version("24w12a"), Version("23w12a") } }, { 31, { Version("24w13a"), Version("1.20.5-pre3") } },
|
||||||
|
{ 32, { Version("1.20.5-pre4"), Version("1.20.6") } }, { 33, { Version("24w18a"), Version("24w20a") } },
|
||||||
|
{ 34, { Version("24w21a"), Version("1.21") } }
|
||||||
};
|
};
|
||||||
|
|
||||||
void ResourcePack::setPackFormat(int new_format_id)
|
void ResourcePack::setPackFormat(int new_format_id)
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "StringUtils.h"
|
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
|
|
||||||
#include "minecraft/mod/Resource.h"
|
#include "minecraft/mod/Resource.h"
|
||||||
@ -57,6 +56,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc
|
|||||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
|
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||||
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||||
m_columnsHideable = { false, true, false, true, true, true };
|
m_columnsHideable = { false, true, false, true, true, true };
|
||||||
|
m_columnsHiddenByDefault = { false, false, false, false, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
#include "minecraft/mod/MetadataHandler.h"
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
#include "modplatform/ResourceAPI.h"
|
#include "modplatform/ResourceAPI.h"
|
||||||
@ -44,6 +45,14 @@ static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
|||||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
|
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool checkDependencies(std::shared_ptr<GetModDependenciesTask::PackDependency> sel,
|
||||||
|
Version mcVersion,
|
||||||
|
ModPlatform::ModLoaderTypes loaders)
|
||||||
|
{
|
||||||
|
return (sel->pack->versions.isEmpty() || sel->version.mcVersion.contains(mcVersion.toString())) &&
|
||||||
|
(!loaders || !sel->version.loaders || sel->version.loaders & loaders);
|
||||||
|
}
|
||||||
|
|
||||||
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||||
BaseInstance* instance,
|
BaseInstance* instance,
|
||||||
ModFolderModel* folder,
|
ModFolderModel* folder,
|
||||||
@ -68,6 +77,7 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
|||||||
void GetModDependenciesTask::prepare()
|
void GetModDependenciesTask::prepare()
|
||||||
{
|
{
|
||||||
for (auto sel : m_selected) {
|
for (auto sel : m_selected) {
|
||||||
|
if (checkDependencies(sel, m_version, m_loaderType))
|
||||||
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
||||||
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@
|
|||||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
static ModrinthAPI modrinth_api;
|
static ModrinthAPI modrinth_api;
|
||||||
static FlameAPI flame_api;
|
static FlameAPI flame_api;
|
||||||
|
|
||||||
@ -162,10 +160,10 @@ void EnsureMetadataTask::executeTask()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (m_mods.size() > 1)
|
if (m_mods.size() > 1)
|
||||||
setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider)));
|
setStatus(tr("Requesting metadata information from %1...").arg(ModPlatform::ProviderCapabilities::readableName(m_provider)));
|
||||||
else if (!m_mods.empty())
|
else if (!m_mods.empty())
|
||||||
setStatus(tr("Requesting metadata information from %1 for '%2'...")
|
setStatus(tr("Requesting metadata information from %1 for '%2'...")
|
||||||
.arg(ProviderCaps.readableName(m_provider), m_mods.begin().value()->name()));
|
.arg(ModPlatform::ProviderCapabilities::readableName(m_provider), m_mods.begin().value()->name()));
|
||||||
|
|
||||||
m_current_task = version_task;
|
m_current_task = version_task;
|
||||||
version_task->start();
|
version_task->start();
|
||||||
@ -215,7 +213,7 @@ void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove)
|
|||||||
|
|
||||||
Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
||||||
{
|
{
|
||||||
auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
auto hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||||
|
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
|
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -58,7 +59,7 @@ IndexedVersionType::VersionType IndexedVersionType::enumFromString(const QString
|
|||||||
return s_indexed_version_type_names.value(type, IndexedVersionType::VersionType::Unknown);
|
return s_indexed_version_type_names.value(type, IndexedVersionType::VersionType::Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ProviderCapabilities::name(ResourceProvider p) -> const char*
|
const char* ProviderCapabilities::name(ResourceProvider p)
|
||||||
{
|
{
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case ResourceProvider::MODRINTH:
|
case ResourceProvider::MODRINTH:
|
||||||
@ -68,7 +69,8 @@ auto ProviderCapabilities::name(ResourceProvider p) -> const char*
|
|||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto ProviderCapabilities::readableName(ResourceProvider p) -> QString
|
|
||||||
|
QString ProviderCapabilities::readableName(ResourceProvider p)
|
||||||
{
|
{
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case ResourceProvider::MODRINTH:
|
case ResourceProvider::MODRINTH:
|
||||||
@ -78,7 +80,8 @@ auto ProviderCapabilities::readableName(ResourceProvider p) -> QString
|
|||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto ProviderCapabilities::hashType(ResourceProvider p) -> QStringList
|
|
||||||
|
QStringList ProviderCapabilities::hashType(ResourceProvider p)
|
||||||
{
|
{
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case ResourceProvider::MODRINTH:
|
case ResourceProvider::MODRINTH:
|
||||||
@ -90,34 +93,13 @@ auto ProviderCapabilities::hashType(ResourceProvider p) -> QStringList
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString type) -> QString
|
|
||||||
{
|
|
||||||
QCryptographicHash::Algorithm algo = QCryptographicHash::Sha1;
|
|
||||||
switch (p) {
|
|
||||||
case ResourceProvider::MODRINTH: {
|
|
||||||
algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Sha512;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ResourceProvider::FLAME:
|
|
||||||
algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Md5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
QCryptographicHash hash(algo);
|
|
||||||
if (!hash.addData(device))
|
|
||||||
qCritical() << "Failed to read JAR to create hash!";
|
|
||||||
|
|
||||||
Q_ASSERT(hash.result().length() == hash.hashLength(algo));
|
|
||||||
return { hash.result().toHex() };
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
||||||
{
|
{
|
||||||
return ((provider == ModPlatform::ResourceProvider::FLAME) ? "https://www.curseforge.com/projects/" : "https://modrinth.com/mod/") +
|
return ((provider == ModPlatform::ResourceProvider::FLAME) ? "https://www.curseforge.com/projects/" : "https://modrinth.com/mod/") +
|
||||||
projectID.toString();
|
projectID.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getModLoaderString(ModLoaderType type) -> const QString
|
auto getModLoaderAsString(ModLoaderType type) -> const QString
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NeoForge:
|
case NeoForge:
|
||||||
@ -138,4 +120,21 @@ auto getModLoaderString(ModLoaderType type) -> const QString
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto getModLoaderFromString(QString type) -> ModLoaderType
|
||||||
|
{
|
||||||
|
if (type == "neoforge")
|
||||||
|
return NeoForge;
|
||||||
|
if (type == "forge")
|
||||||
|
return Forge;
|
||||||
|
if (type == "cauldron")
|
||||||
|
return Cauldron;
|
||||||
|
if (type == "liteloader")
|
||||||
|
return LiteLoader;
|
||||||
|
if (type == "fabric")
|
||||||
|
return Fabric;
|
||||||
|
if (type == "quilt")
|
||||||
|
return Quilt;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ModPlatform
|
} // namespace ModPlatform
|
||||||
|
@ -40,13 +40,11 @@ enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
|||||||
|
|
||||||
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
|
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
|
||||||
|
|
||||||
class ProviderCapabilities {
|
namespace ProviderCapabilities {
|
||||||
public:
|
const char* name(ResourceProvider);
|
||||||
auto name(ResourceProvider) -> const char*;
|
QString readableName(ResourceProvider);
|
||||||
auto readableName(ResourceProvider) -> QString;
|
QStringList hashType(ResourceProvider);
|
||||||
auto hashType(ResourceProvider) -> QStringList;
|
}; // namespace ProviderCapabilities
|
||||||
auto hash(ResourceProvider, QIODevice*, QString type = "") -> QString;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ModpackAuthor {
|
struct ModpackAuthor {
|
||||||
QString name;
|
QString name;
|
||||||
@ -109,6 +107,7 @@ struct IndexedVersion {
|
|||||||
bool is_preferred = true;
|
bool is_preferred = true;
|
||||||
QString changelog;
|
QString changelog;
|
||||||
QList<Dependency> dependencies;
|
QList<Dependency> dependencies;
|
||||||
|
QString side; // this is for flame API
|
||||||
|
|
||||||
// For internal use, not provided by APIs
|
// For internal use, not provided by APIs
|
||||||
bool is_currently_selected = false;
|
bool is_currently_selected = false;
|
||||||
@ -183,7 +182,8 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
|||||||
|
|
||||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||||
|
|
||||||
auto getModLoaderString(ModLoaderType type) -> const QString;
|
auto getModLoaderAsString(ModLoaderType type) -> const QString;
|
||||||
|
auto getModLoaderFromString(QString type) -> ModLoaderType;
|
||||||
|
|
||||||
constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept
|
constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept
|
||||||
{
|
{
|
||||||
@ -191,6 +191,11 @@ constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept
|
|||||||
return x && !(x & (x - 1));
|
return x && !(x & (x - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Category {
|
||||||
|
QString name;
|
||||||
|
QString id;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ModPlatform
|
} // namespace ModPlatform
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -73,6 +74,8 @@ class ResourceAPI {
|
|||||||
std::optional<SortingMethod> sorting;
|
std::optional<SortingMethod> sorting;
|
||||||
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||||
std::optional<std::list<Version> > versions;
|
std::optional<std::list<Version> > versions;
|
||||||
|
std::optional<QString> side;
|
||||||
|
std::optional<QStringList> categoryIds;
|
||||||
};
|
};
|
||||||
struct SearchCallbacks {
|
struct SearchCallbacks {
|
||||||
std::function<void(QJsonDocument&)> on_succeed;
|
std::function<void(QJsonDocument&)> on_succeed;
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#include "FlameAPI.h"
|
#include "FlameAPI.h"
|
||||||
|
#include <memory>
|
||||||
#include "FlameModIndex.h"
|
#include "FlameModIndex.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
#include "net/ApiUpload.h"
|
#include "net/ApiUpload.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
@ -220,3 +222,42 @@ QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
|
|||||||
{ 7, "Category", QObject::tr("Sort by Category") },
|
{ 7, "Category", QObject::tr("Sort by Category") },
|
||||||
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
|
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr FlameAPI::getModCategories(std::shared_ptr<QByteArray> response)
|
||||||
|
{
|
||||||
|
auto netJob = makeShared<NetJob>(QString("Flame::GetCategories"), APPLICATION->network());
|
||||||
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl("https://api.curseforge.com/v1/categories?gameId=432&classId=6"), response));
|
||||||
|
QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; });
|
||||||
|
return netJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ModPlatform::Category> FlameAPI::loadModCategories(std::shared_ptr<QByteArray> response)
|
||||||
|
{
|
||||||
|
QList<ModPlatform::Category> categories;
|
||||||
|
QJsonParseError parse_error{};
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from categories at " << parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto obj = Json::requireObject(doc);
|
||||||
|
auto arr = Json::requireArray(obj, "data");
|
||||||
|
|
||||||
|
for (auto val : arr) {
|
||||||
|
auto cat = Json::requireObject(val);
|
||||||
|
auto id = Json::requireInteger(cat, "id");
|
||||||
|
auto name = Json::requireString(cat, "name");
|
||||||
|
categories.push_back({ name, QString::number(id) });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Json::JsonException& e) {
|
||||||
|
qCritical() << "Failed to parse response from a version request.";
|
||||||
|
qCritical() << e.what();
|
||||||
|
qDebug() << doc;
|
||||||
|
}
|
||||||
|
return categories;
|
||||||
|
};
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
@ -22,6 +23,9 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
||||||
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
||||||
|
|
||||||
|
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
|
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
|
||||||
@ -96,6 +100,9 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
get_arguments.append("sortOrder=desc");
|
get_arguments.append("sortOrder=desc");
|
||||||
if (args.loaders.has_value())
|
if (args.loaders.has_value())
|
||||||
get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value())));
|
get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value())));
|
||||||
|
if (args.categoryIds.has_value() && !args.categoryIds->empty())
|
||||||
|
get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(",")));
|
||||||
|
|
||||||
get_arguments.append(gameVersionStr);
|
get_arguments.append(gameVersionStr);
|
||||||
|
|
||||||
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
|
||||||
static FlameAPI api;
|
static FlameAPI api;
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||||
{
|
{
|
||||||
@ -80,10 +79,6 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
const BaseInstance* inst)
|
const BaseInstance* inst)
|
||||||
{
|
{
|
||||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
|
||||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
|
||||||
auto loaders = profile->getSupportedModLoaders();
|
|
||||||
|
|
||||||
for (auto versionIter : arr) {
|
for (auto versionIter : arr) {
|
||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
|
|
||||||
@ -91,8 +86,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
|||||||
if (!file.addonId.isValid())
|
if (!file.addonId.isValid())
|
||||||
file.addonId = pack.addonId;
|
file.addonId = pack.addonId;
|
||||||
|
|
||||||
if (file.fileId.isValid() &&
|
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
|
||||||
unsortedVersions.append(file);
|
unsortedVersions.append(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,19 +112,25 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||||||
|
|
||||||
if (str.contains('.'))
|
if (str.contains('.'))
|
||||||
file.mcVersion.append(str);
|
file.mcVersion.append(str);
|
||||||
auto loader = str.toLower();
|
|
||||||
if (loader == "neoforge")
|
if (auto loader = str.toLower(); loader == "neoforge")
|
||||||
file.loaders |= ModPlatform::NeoForge;
|
file.loaders |= ModPlatform::NeoForge;
|
||||||
if (loader == "forge")
|
else if (loader == "forge")
|
||||||
file.loaders |= ModPlatform::Forge;
|
file.loaders |= ModPlatform::Forge;
|
||||||
if (loader == "cauldron")
|
else if (loader == "cauldron")
|
||||||
file.loaders |= ModPlatform::Cauldron;
|
file.loaders |= ModPlatform::Cauldron;
|
||||||
if (loader == "liteloader")
|
else if (loader == "liteloader")
|
||||||
file.loaders |= ModPlatform::LiteLoader;
|
file.loaders |= ModPlatform::LiteLoader;
|
||||||
if (loader == "fabric")
|
else if (loader == "fabric")
|
||||||
file.loaders |= ModPlatform::Fabric;
|
file.loaders |= ModPlatform::Fabric;
|
||||||
if (loader == "quilt")
|
else if (loader == "quilt")
|
||||||
file.loaders |= ModPlatform::Quilt;
|
file.loaders |= ModPlatform::Quilt;
|
||||||
|
else if (loader == "server" || loader == "client") {
|
||||||
|
if (file.side.isEmpty())
|
||||||
|
file.side = loader;
|
||||||
|
else if (file.side != loader)
|
||||||
|
file.side = "both";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.addonId = Json::requireInteger(obj, "modId");
|
file.addonId = Json::requireInteger(obj, "modId");
|
||||||
@ -160,7 +160,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||||||
auto hash_list = Json::ensureArray(obj, "hashes");
|
auto hash_list = Json::ensureArray(obj, "hashes");
|
||||||
for (auto h : hash_list) {
|
for (auto h : hash_list) {
|
||||||
auto hash_entry = Json::ensureObject(h);
|
auto hash_entry = Json::ensureObject(h);
|
||||||
auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::FLAME);
|
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME);
|
||||||
auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm"));
|
auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm"));
|
||||||
if (hash_types.contains(hash_algo)) {
|
if (hash_types.contains(hash_algo)) {
|
||||||
file.hash = Json::requireString(hash_entry, "value");
|
file.hash = Json::requireString(hash_entry, "value");
|
||||||
|
@ -116,7 +116,7 @@ void FlamePackExportTask::collectHashes()
|
|||||||
|
|
||||||
if (relative.startsWith("resourcepacks/") &&
|
if (relative.startsWith("resourcepacks/") &&
|
||||||
(relative.endsWith(".zip") || relative.endsWith(".zip.disabled"))) { // is resourcepack
|
(relative.endsWith(".zip") || relative.endsWith(".zip.disabled"))) { // is resourcepack
|
||||||
auto hashTask = Hashing::createFlameHasher(file.absoluteFilePath());
|
auto hashTask = Hashing::createHasher(file.absoluteFilePath(), ModPlatform::ResourceProvider::FLAME);
|
||||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, relative, file](QString hash) {
|
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, relative, file](QString hash) {
|
||||||
if (m_state == Task::State::Running) {
|
if (m_state == Task::State::Running) {
|
||||||
pendingHashes.insert(hash, { relative, file.absoluteFilePath(), relative.endsWith(".zip") });
|
pendingHashes.insert(hash, { relative, file.absoluteFilePath(), relative.endsWith(".zip") });
|
||||||
@ -140,7 +140,7 @@ void FlamePackExportTask::collectHashes()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hashTask = Hashing::createFlameHasher(mod->fileinfo().absoluteFilePath());
|
auto hashTask = Hashing::createHasher(mod->fileinfo().absoluteFilePath(), ModPlatform::ResourceProvider::FLAME);
|
||||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) {
|
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) {
|
||||||
if (m_state == Task::State::Running) {
|
if (m_state == Task::State::Running) {
|
||||||
pendingHashes.insert(hash, { mod->name(), mod->fileinfo().absoluteFilePath(), mod->enabled(), true });
|
pendingHashes.insert(hash, { mod->name(), mod->fileinfo().absoluteFilePath(), mod->enabled(), true });
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
#include "HashUtils.h"
|
#include "HashUtils.h"
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "StringUtils.h"
|
|
||||||
|
|
||||||
#include <MurmurHash2.h>
|
#include <MurmurHash2.h>
|
||||||
|
|
||||||
namespace Hashing {
|
namespace Hashing {
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider)
|
Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider)
|
||||||
{
|
{
|
||||||
switch (provider) {
|
switch (provider) {
|
||||||
case ModPlatform::ResourceProvider::MODRINTH:
|
case ModPlatform::ResourceProvider::MODRINTH:
|
||||||
return createModrinthHasher(file_path);
|
return makeShared<Hasher>(file_path,
|
||||||
|
ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first());
|
||||||
case ModPlatform::ResourceProvider::FLAME:
|
case ModPlatform::ResourceProvider::FLAME:
|
||||||
return createFlameHasher(file_path);
|
return makeShared<Hasher>(file_path, Algorithm::Murmur2);
|
||||||
default:
|
default:
|
||||||
qCritical() << "[Hashing]"
|
qCritical() << "[Hashing]"
|
||||||
<< "Unrecognized mod platform!";
|
<< "Unrecognized mod platform!";
|
||||||
@ -26,119 +24,142 @@ Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Hasher::Ptr createModrinthHasher(QString file_path)
|
Hasher::Ptr createHasher(QString file_path, QString type)
|
||||||
{
|
{
|
||||||
return makeShared<ModrinthHasher>(file_path);
|
return makeShared<Hasher>(file_path, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Hasher::Ptr createFlameHasher(QString file_path)
|
class QIODeviceReader : public Murmur2::Reader {
|
||||||
|
public:
|
||||||
|
QIODeviceReader(QIODevice* device) : m_device(device) {}
|
||||||
|
virtual ~QIODeviceReader() = default;
|
||||||
|
virtual int read(char* s, int n) { return m_device->read(s, n); }
|
||||||
|
virtual bool eof() { return m_device->atEnd(); }
|
||||||
|
virtual void goToBeginning() { m_device->seek(0); }
|
||||||
|
virtual void close() { m_device->close(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QIODevice* m_device;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString algorithmToString(Algorithm type)
|
||||||
{
|
{
|
||||||
return makeShared<FlameHasher>(file_path);
|
switch (type) {
|
||||||
|
case Algorithm::Md4:
|
||||||
|
return "md4";
|
||||||
|
case Algorithm::Md5:
|
||||||
|
return "md5";
|
||||||
|
case Algorithm::Sha1:
|
||||||
|
return "sha1";
|
||||||
|
case Algorithm::Sha256:
|
||||||
|
return "sha256";
|
||||||
|
case Algorithm::Sha512:
|
||||||
|
return "sha512";
|
||||||
|
case Algorithm::Murmur2:
|
||||||
|
return "murmur2";
|
||||||
|
// case Algorithm::Unknown:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider)
|
Algorithm algorithmFromString(QString type)
|
||||||
{
|
{
|
||||||
return makeShared<BlockedModHasher>(file_path, provider);
|
if (type == "md4")
|
||||||
|
return Algorithm::Md4;
|
||||||
|
if (type == "md5")
|
||||||
|
return Algorithm::Md5;
|
||||||
|
if (type == "sha1")
|
||||||
|
return Algorithm::Sha1;
|
||||||
|
if (type == "sha256")
|
||||||
|
return Algorithm::Sha256;
|
||||||
|
if (type == "sha512")
|
||||||
|
return Algorithm::Sha512;
|
||||||
|
if (type == "murmur2")
|
||||||
|
return Algorithm::Murmur2;
|
||||||
|
return Algorithm::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type)
|
QString hash(QIODevice* device, Algorithm type)
|
||||||
{
|
{
|
||||||
auto hasher = makeShared<BlockedModHasher>(file_path, provider);
|
if (!device->isOpen() && !device->open(QFile::ReadOnly))
|
||||||
hasher->useHashType(type);
|
return "";
|
||||||
return hasher;
|
QCryptographicHash::Algorithm alg = QCryptographicHash::Sha1;
|
||||||
}
|
switch (type) {
|
||||||
|
case Algorithm::Md4:
|
||||||
void ModrinthHasher::executeTask()
|
alg = QCryptographicHash::Algorithm::Md4;
|
||||||
{
|
break;
|
||||||
QFile file(m_path);
|
case Algorithm::Md5:
|
||||||
|
alg = QCryptographicHash::Algorithm::Md5;
|
||||||
try {
|
break;
|
||||||
file.open(QFile::ReadOnly);
|
case Algorithm::Sha1:
|
||||||
} catch (FS::FileSystemException& e) {
|
alg = QCryptographicHash::Algorithm::Sha1;
|
||||||
qCritical() << QString("Failed to open JAR file in %1").arg(m_path);
|
break;
|
||||||
qCritical() << QString("Reason: ") << e.cause();
|
case Algorithm::Sha256:
|
||||||
|
alg = QCryptographicHash::Algorithm::Sha256;
|
||||||
emitFailed("Failed to open file for hashing.");
|
break;
|
||||||
return;
|
case Algorithm::Sha512:
|
||||||
}
|
alg = QCryptographicHash::Algorithm::Sha512;
|
||||||
|
break;
|
||||||
auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
case Algorithm::Murmur2: { // CF-specific
|
||||||
m_hash = ProviderCaps.hash(ModPlatform::ResourceProvider::MODRINTH, &file, hash_type);
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (m_hash.isEmpty()) {
|
|
||||||
emitFailed("Empty hash!");
|
|
||||||
} else {
|
|
||||||
emitSucceeded();
|
|
||||||
emit resultsReady(m_hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameHasher::executeTask()
|
|
||||||
{
|
|
||||||
// CF-specific
|
|
||||||
auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); };
|
auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); };
|
||||||
|
auto reader = std::make_unique<QIODeviceReader>(device);
|
||||||
|
auto result = QString::number(Murmur2::hash(reader.get(), 4 * MiB, should_filter_out));
|
||||||
|
device->close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case Algorithm::Unknown:
|
||||||
|
device->close();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
std::ifstream file_stream(StringUtils::toStdString(m_path).c_str(), std::ifstream::binary);
|
QCryptographicHash hash(alg);
|
||||||
// TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread.
|
if (!hash.addData(device))
|
||||||
// How do we make this non-blocking then?
|
qCritical() << "Failed to read JAR to create hash!";
|
||||||
m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out));
|
|
||||||
|
|
||||||
if (m_hash.isEmpty()) {
|
Q_ASSERT(hash.result().length() == hash.hashLength(alg));
|
||||||
|
auto result = hash.result().toHex();
|
||||||
|
device->close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString hash(QString fileName, Algorithm type)
|
||||||
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
return hash(&file, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString hash(QByteArray data, Algorithm type)
|
||||||
|
{
|
||||||
|
QBuffer buff(&data);
|
||||||
|
return hash(&buff, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hasher::executeTask()
|
||||||
|
{
|
||||||
|
m_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return hash(m_path, m_alg); });
|
||||||
|
connect(&m_watcher, &QFutureWatcher<QString>::finished, this, [this] {
|
||||||
|
if (m_future.isCanceled()) {
|
||||||
|
emitAborted();
|
||||||
|
} else if (m_result = m_future.result(); m_result.isEmpty()) {
|
||||||
emitFailed("Empty hash!");
|
emitFailed("Empty hash!");
|
||||||
} else {
|
} else {
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
emit resultsReady(m_hash);
|
emit resultsReady(m_result);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
m_watcher.setFuture(m_future);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) : Hasher(file_path), provider(provider)
|
bool Hasher::abort()
|
||||||
{
|
{
|
||||||
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
|
if (m_future.isRunning()) {
|
||||||
hash_type = ProviderCaps.hashType(provider).first();
|
m_future.cancel();
|
||||||
}
|
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not
|
||||||
|
// occur immediately.
|
||||||
void BlockedModHasher::executeTask()
|
|
||||||
{
|
|
||||||
QFile file(m_path);
|
|
||||||
|
|
||||||
try {
|
|
||||||
file.open(QFile::ReadOnly);
|
|
||||||
} catch (FS::FileSystemException& e) {
|
|
||||||
qCritical() << QString("Failed to open JAR file in %1").arg(m_path);
|
|
||||||
qCritical() << QString("Reason: ") << e.cause();
|
|
||||||
|
|
||||||
emitFailed("Failed to open file for hashing.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hash = ProviderCaps.hash(provider, &file, hash_type);
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (m_hash.isEmpty()) {
|
|
||||||
emitFailed("Empty hash!");
|
|
||||||
} else {
|
|
||||||
emitSucceeded();
|
|
||||||
emit resultsReady(m_hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList BlockedModHasher::getHashTypes()
|
|
||||||
{
|
|
||||||
return ProviderCaps.hashType(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BlockedModHasher::useHashType(QString type)
|
|
||||||
{
|
|
||||||
auto types = ProviderCaps.hashType(provider);
|
|
||||||
if (types.contains(type)) {
|
|
||||||
hash_type = type;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
qDebug() << "Bad hash type " << type << " for provider";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Hashing
|
} // namespace Hashing
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QFutureWatcher>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
@ -7,61 +10,42 @@
|
|||||||
|
|
||||||
namespace Hashing {
|
namespace Hashing {
|
||||||
|
|
||||||
|
enum class Algorithm { Md4, Md5, Sha1, Sha256, Sha512, Murmur2, Unknown };
|
||||||
|
|
||||||
|
QString algorithmToString(Algorithm type);
|
||||||
|
Algorithm algorithmFromString(QString type);
|
||||||
|
QString hash(QIODevice* device, Algorithm type);
|
||||||
|
QString hash(QString fileName, Algorithm type);
|
||||||
|
QString hash(QByteArray data, Algorithm type);
|
||||||
|
|
||||||
class Hasher : public Task {
|
class Hasher : public Task {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
using Ptr = shared_qobject_ptr<Hasher>;
|
using Ptr = shared_qobject_ptr<Hasher>;
|
||||||
|
|
||||||
Hasher(QString file_path) : m_path(std::move(file_path)) {}
|
Hasher(QString file_path, Algorithm alg) : m_path(file_path), m_alg(alg) {}
|
||||||
|
Hasher(QString file_path, QString alg) : Hasher(file_path, algorithmFromString(alg)) {}
|
||||||
|
|
||||||
/* We can't really abort this task, but we can say we aborted and finish our thing quickly :) */
|
bool abort() override;
|
||||||
bool abort() override { return true; }
|
|
||||||
|
|
||||||
void executeTask() override = 0;
|
void executeTask() override;
|
||||||
|
|
||||||
QString getResult() const { return m_hash; };
|
QString getResult() const { return m_result; };
|
||||||
QString getPath() const { return m_path; };
|
QString getPath() const { return m_path; };
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resultsReady(QString hash);
|
void resultsReady(QString hash);
|
||||||
|
|
||||||
protected:
|
|
||||||
QString m_hash;
|
|
||||||
QString m_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FlameHasher : public Hasher {
|
|
||||||
public:
|
|
||||||
FlameHasher(QString file_path) : Hasher(file_path) { setObjectName(QString("FlameHasher: %1").arg(file_path)); }
|
|
||||||
|
|
||||||
void executeTask() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModrinthHasher : public Hasher {
|
|
||||||
public:
|
|
||||||
ModrinthHasher(QString file_path) : Hasher(file_path) { setObjectName(QString("ModrinthHasher: %1").arg(file_path)); }
|
|
||||||
|
|
||||||
void executeTask() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlockedModHasher : public Hasher {
|
|
||||||
public:
|
|
||||||
BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider);
|
|
||||||
|
|
||||||
void executeTask() override;
|
|
||||||
|
|
||||||
QStringList getHashTypes();
|
|
||||||
bool useHashType(QString type);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModPlatform::ResourceProvider provider;
|
QString m_result;
|
||||||
QString hash_type;
|
QString m_path;
|
||||||
|
Algorithm m_alg;
|
||||||
|
|
||||||
|
QFuture<QString> m_future;
|
||||||
|
QFutureWatcher<QString> m_watcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider);
|
Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider);
|
||||||
Hasher::Ptr createFlameHasher(QString file_path);
|
Hasher::Ptr createHasher(QString file_path, QString type);
|
||||||
Hasher::Ptr createModrinthHasher(QString file_path);
|
|
||||||
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider);
|
|
||||||
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider, QString type);
|
|
||||||
|
|
||||||
} // namespace Hashing
|
} // namespace Hashing
|
||||||
|
@ -120,3 +120,41 @@ QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
|
|||||||
{ 4, "newest", QObject::tr("Sort by Newest") },
|
{ 4, "newest", QObject::tr("Sort by Newest") },
|
||||||
{ 5, "updated", QObject::tr("Sort by Last Updated") } };
|
{ 5, "updated", QObject::tr("Sort by Last Updated") } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr ModrinthAPI::getModCategories(std::shared_ptr<QByteArray> response)
|
||||||
|
{
|
||||||
|
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCategories"), APPLICATION->network());
|
||||||
|
netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(BuildConfig.MODRINTH_PROD_URL + "/tag/category"), response));
|
||||||
|
QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Modrinth failed to get categories:" << msg; });
|
||||||
|
return netJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ModPlatform::Category> ModrinthAPI::loadModCategories(std::shared_ptr<QByteArray> response)
|
||||||
|
{
|
||||||
|
QList<ModPlatform::Category> categories;
|
||||||
|
QJsonParseError parse_error{};
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||||
|
if (parse_error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from categories at " << parse_error.offset
|
||||||
|
<< " reason: " << parse_error.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto arr = Json::requireArray(doc);
|
||||||
|
|
||||||
|
for (auto val : arr) {
|
||||||
|
auto cat = Json::requireObject(val);
|
||||||
|
auto name = Json::requireString(cat, "name");
|
||||||
|
if (Json::ensureString(cat, "project_type", "") == "mod")
|
||||||
|
categories.push_back({ name, name });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Json::JsonException& e) {
|
||||||
|
qCritical() << "Failed to parse response from a version request.";
|
||||||
|
qCritical() << e.what();
|
||||||
|
qDebug() << doc;
|
||||||
|
}
|
||||||
|
return categories;
|
||||||
|
};
|
@ -30,6 +30,9 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
|
|
||||||
|
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
@ -41,7 +44,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
for (auto loader :
|
for (auto loader :
|
||||||
{ ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) {
|
{ ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) {
|
||||||
if (types & loader) {
|
if (types & loader) {
|
||||||
l << getModLoaderString(loader);
|
l << getModLoaderAsString(loader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
@ -56,6 +59,27 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
return l.join(',');
|
return l.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto getCategoriesFilters(QStringList categories) -> const QString
|
||||||
|
{
|
||||||
|
QStringList l;
|
||||||
|
for (auto cat : categories) {
|
||||||
|
l << QString("\"categories:%1\"").arg(cat);
|
||||||
|
}
|
||||||
|
return l.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto getSideFilters(QString side) -> const QString
|
||||||
|
{
|
||||||
|
if (side.isEmpty() || side == "both") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (side == "client")
|
||||||
|
return QString("\"client_side:required\",\"client_side:optional\"");
|
||||||
|
if (side == "server")
|
||||||
|
return QString("\"server_side:required\",\"server_side:optional\"");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type)
|
[[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type)
|
||||||
{
|
{
|
||||||
@ -73,6 +97,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QString createFacets(SearchArgs const& args) const
|
[[nodiscard]] QString createFacets(SearchArgs const& args) const
|
||||||
{
|
{
|
||||||
QStringList facets_list;
|
QStringList facets_list;
|
||||||
@ -81,6 +106,14 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
||||||
if (args.versions.has_value())
|
if (args.versions.has_value())
|
||||||
facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
|
facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
|
||||||
|
if (args.side.has_value()) {
|
||||||
|
auto side = getSideFilters(args.side.value());
|
||||||
|
if (!side.isEmpty())
|
||||||
|
facets_list.append(QString("[%1]").arg(side));
|
||||||
|
}
|
||||||
|
if (args.categoryIds.has_value() && !args.categoryIds->empty())
|
||||||
|
facets_list.append(QString("[%1]").arg(getCategoriesFilters(args.categoryIds.value())));
|
||||||
|
|
||||||
facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type)));
|
facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type)));
|
||||||
|
|
||||||
return QString("[%1]").arg(facets_list.join(','));
|
return QString("[%1]").arg(facets_list.join(','));
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
|
||||||
static ModrinthAPI api;
|
static ModrinthAPI api;
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
bool ModrinthCheckUpdate::abort()
|
bool ModrinthCheckUpdate::abort()
|
||||||
{
|
{
|
||||||
@ -36,7 +35,7 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
|
|
||||||
// Create all hashes
|
// Create all hashes
|
||||||
QStringList hashes;
|
QStringList hashes;
|
||||||
auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
auto best_hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||||
|
|
||||||
ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||||
for (auto* mod : m_mods) {
|
for (auto* mod : m_mods) {
|
||||||
@ -51,7 +50,7 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
// need to generate a new hash if the current one is innadequate
|
// need to generate a new hash if the current one is innadequate
|
||||||
// (though it will rarely happen, if at all)
|
// (though it will rarely happen, if at all)
|
||||||
if (mod->metadata()->hash_format != best_hash_type) {
|
if (mod->metadata()->hash_format != best_hash_type) {
|
||||||
auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath());
|
auto hash_task = Hashing::createHasher(mod->fileinfo().absoluteFilePath(), ModPlatform::ResourceProvider::MODRINTH);
|
||||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, mod](QString hash) {
|
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, mod](QString hash) {
|
||||||
hashes.append(hash);
|
hashes.append(hash);
|
||||||
mappings.insert(hash, mod);
|
mappings.insert(hash, mod);
|
||||||
@ -112,7 +111,7 @@ void ModrinthCheckUpdate::executeTask()
|
|||||||
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
|
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
|
||||||
for (auto flag : flags) {
|
for (auto flag : flags) {
|
||||||
if (m_loaders.value().testFlag(flag)) {
|
if (m_loaders.value().testFlag(flag)) {
|
||||||
loader_filter = ModPlatform::getModLoaderString(flag);
|
loader_filter = ModPlatform::getModLoaderAsString(flag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "minecraft/mod/MetadataHandler.h"
|
#include "minecraft/mod/MetadataHandler.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
#include "modplatform/helpers/HashUtils.h"
|
||||||
|
|
||||||
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
|
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
|
||||||
const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" });
|
const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" });
|
||||||
@ -102,8 +103,6 @@ void ModrinthPackExportTask::collectHashes()
|
|||||||
}))
|
}))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QCryptographicHash sha512(QCryptographicHash::Algorithm::Sha512);
|
|
||||||
|
|
||||||
QFile openFile(file.absoluteFilePath());
|
QFile openFile(file.absoluteFilePath());
|
||||||
if (!openFile.open(QFile::ReadOnly)) {
|
if (!openFile.open(QFile::ReadOnly)) {
|
||||||
qWarning() << "Could not open" << file << "for hashing";
|
qWarning() << "Could not open" << file << "for hashing";
|
||||||
@ -115,7 +114,7 @@ void ModrinthPackExportTask::collectHashes()
|
|||||||
qWarning() << "Could not read" << file;
|
qWarning() << "Could not read" << file;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sha512.addData(data);
|
auto sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512);
|
||||||
|
|
||||||
auto allMods = mcInstance->loaderModList()->allMods();
|
auto allMods = mcInstance->loaderModList()->allMods();
|
||||||
if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; });
|
if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; });
|
||||||
@ -127,11 +126,9 @@ void ModrinthPackExportTask::collectHashes()
|
|||||||
if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) {
|
if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) {
|
||||||
qDebug() << "Resolving" << relative << "from index";
|
qDebug() << "Resolving" << relative << "from index";
|
||||||
|
|
||||||
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
|
auto sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1);
|
||||||
sha1.addData(data);
|
|
||||||
|
|
||||||
ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size(),
|
ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), openFile.size(), mod->metadata()->side };
|
||||||
mod->metadata()->side };
|
|
||||||
resolvedFiles[relative] = resolvedFile;
|
resolvedFiles[relative] = resolvedFile;
|
||||||
|
|
||||||
// nice! we've managed to resolve based on local metadata!
|
// nice! we've managed to resolve based on local metadata!
|
||||||
@ -142,7 +139,7 @@ void ModrinthPackExportTask::collectHashes()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Enqueueing" << relative << "for Modrinth query";
|
qDebug() << "Enqueueing" << relative << "for Modrinth query";
|
||||||
pendingHashes[relative] = sha512.result().toHex();
|
pendingHashes[relative] = sha512;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAbortable(true);
|
setAbortable(true);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -26,7 +27,6 @@
|
|||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
static ModrinthAPI api;
|
static ModrinthAPI api;
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
bool shouldDownloadOnSide(QString side)
|
bool shouldDownloadOnSide(QString side)
|
||||||
{
|
{
|
||||||
@ -115,16 +115,11 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
|||||||
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst)
|
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst)
|
||||||
{
|
{
|
||||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
|
||||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
|
||||||
auto loaders = profile->getSupportedModLoaders();
|
|
||||||
|
|
||||||
for (auto versionIter : arr) {
|
for (auto versionIter : arr) {
|
||||||
auto obj = versionIter.toObject();
|
auto obj = versionIter.toObject();
|
||||||
auto file = loadIndexedPackVersion(obj);
|
auto file = loadIndexedPackVersion(obj);
|
||||||
|
|
||||||
if (file.fileId.isValid() &&
|
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
|
||||||
unsortedVersions.append(file);
|
unsortedVersions.append(file);
|
||||||
}
|
}
|
||||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||||
@ -155,15 +150,15 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
for (auto loader : loaders) {
|
for (auto loader : loaders) {
|
||||||
if (loader == "neoforge")
|
if (loader == "neoforge")
|
||||||
file.loaders |= ModPlatform::NeoForge;
|
file.loaders |= ModPlatform::NeoForge;
|
||||||
if (loader == "forge")
|
else if (loader == "forge")
|
||||||
file.loaders |= ModPlatform::Forge;
|
file.loaders |= ModPlatform::Forge;
|
||||||
if (loader == "cauldron")
|
else if (loader == "cauldron")
|
||||||
file.loaders |= ModPlatform::Cauldron;
|
file.loaders |= ModPlatform::Cauldron;
|
||||||
if (loader == "liteloader")
|
else if (loader == "liteloader")
|
||||||
file.loaders |= ModPlatform::LiteLoader;
|
file.loaders |= ModPlatform::LiteLoader;
|
||||||
if (loader == "fabric")
|
else if (loader == "fabric")
|
||||||
file.loaders |= ModPlatform::Fabric;
|
file.loaders |= ModPlatform::Fabric;
|
||||||
if (loader == "quilt")
|
else if (loader == "quilt")
|
||||||
file.loaders |= ModPlatform::Quilt;
|
file.loaders |= ModPlatform::Quilt;
|
||||||
}
|
}
|
||||||
file.version = Json::requireString(obj, "name");
|
file.version = Json::requireString(obj, "name");
|
||||||
@ -235,7 +230,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
|||||||
file.hash = Json::requireString(hash_list, preferred_hash_type);
|
file.hash = Json::requireString(hash_list, preferred_hash_type);
|
||||||
file.hash_type = preferred_hash_type;
|
file.hash_type = preferred_hash_type;
|
||||||
} else {
|
} else {
|
||||||
auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH);
|
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH);
|
||||||
for (auto& hash_type : hash_types) {
|
for (auto& hash_type : hash_types) {
|
||||||
if (hash_list.contains(hash_type)) {
|
if (hash_list.contains(hash_type)) {
|
||||||
file.hash = Json::requireString(hash_list, hash_type);
|
file.hash = Json::requireString(hash_list, hash_type);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -66,8 +67,6 @@ static inline auto indexFileName(QString const& mod_slug) -> QString
|
|||||||
return QString("%1.pw.toml").arg(mod_slug);
|
return QString("%1.pw.toml").arg(mod_slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
// Helper functions for extracting data from the TOML file
|
// Helper functions for extracting data from the TOML file
|
||||||
auto stringEntry(toml::table table, QString entry_name) -> QString
|
auto stringEntry(toml::table table, QString entry_name) -> QString
|
||||||
{
|
{
|
||||||
@ -113,7 +112,11 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP
|
|||||||
mod.provider = mod_pack.provider;
|
mod.provider = mod_pack.provider;
|
||||||
mod.file_id = mod_version.fileId;
|
mod.file_id = mod_version.fileId;
|
||||||
mod.project_id = mod_pack.addonId;
|
mod.project_id = mod_pack.addonId;
|
||||||
mod.side = stringToSide(mod_pack.side);
|
mod.side = stringToSide(mod_version.side.isEmpty() ? mod_pack.side : mod_version.side);
|
||||||
|
mod.loaders = mod_version.loaders;
|
||||||
|
mod.mcVersions = mod_version.mcVersion;
|
||||||
|
mod.mcVersions.sort();
|
||||||
|
mod.releaseType = mod_version.version_type;
|
||||||
|
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@ -181,6 +184,18 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toml::array loaders;
|
||||||
|
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, ModPlatform::Fabric,
|
||||||
|
ModPlatform::Quilt }) {
|
||||||
|
if (mod.loaders & loader) {
|
||||||
|
loaders.push_back(getModLoaderAsString(loader).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toml::array mcVersions;
|
||||||
|
for (auto version : mod.mcVersions) {
|
||||||
|
mcVersions.push_back(version.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
if (!index_file.open(QIODevice::ReadWrite)) {
|
if (!index_file.open(QIODevice::ReadWrite)) {
|
||||||
qCritical() << QString("Could not open file %1!").arg(normalized_fname);
|
qCritical() << QString("Could not open file %1!").arg(normalized_fname);
|
||||||
return;
|
return;
|
||||||
@ -192,6 +207,9 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
|||||||
auto tbl = toml::table{ { "name", mod.name.toStdString() },
|
auto tbl = toml::table{ { "name", mod.name.toStdString() },
|
||||||
{ "filename", mod.filename.toStdString() },
|
{ "filename", mod.filename.toStdString() },
|
||||||
{ "side", sideToString(mod.side).toStdString() },
|
{ "side", sideToString(mod.side).toStdString() },
|
||||||
|
{ "loaders", loaders },
|
||||||
|
{ "mcVersions", mcVersions },
|
||||||
|
{ "releaseType", mod.releaseType.toString().toStdString() },
|
||||||
{ "download",
|
{ "download",
|
||||||
toml::table{
|
toml::table{
|
||||||
{ "mode", mod.mode.toStdString() },
|
{ "mode", mod.mode.toStdString() },
|
||||||
@ -199,7 +217,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
|||||||
{ "hash-format", mod.hash_format.toStdString() },
|
{ "hash-format", mod.hash_format.toStdString() },
|
||||||
{ "hash", mod.hash.toStdString() },
|
{ "hash", mod.hash.toStdString() },
|
||||||
} },
|
} },
|
||||||
{ "update", toml::table{ { ProviderCaps.name(mod.provider), update } } } };
|
{ "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update } } } };
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << tbl;
|
ss << tbl;
|
||||||
in_stream << QString::fromStdString(ss.str());
|
in_stream << QString::fromStdString(ss.str());
|
||||||
@ -276,6 +294,25 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
|||||||
mod.name = stringEntry(table, "name");
|
mod.name = stringEntry(table, "name");
|
||||||
mod.filename = stringEntry(table, "filename");
|
mod.filename = stringEntry(table, "filename");
|
||||||
mod.side = stringToSide(stringEntry(table, "side"));
|
mod.side = stringToSide(stringEntry(table, "side"));
|
||||||
|
mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "releaseType"));
|
||||||
|
if (auto loaders = table["loaders"]; loaders && loaders.is_array()) {
|
||||||
|
for (auto&& loader : *loaders.as_array()) {
|
||||||
|
if (loader.is_string()) {
|
||||||
|
mod.loaders |= ModPlatform::getModLoaderFromString(QString::fromStdString(loader.as_string()->value_or("")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto versions = table["mcVersions"]; versions && versions.is_array()) {
|
||||||
|
for (auto&& version : *versions.as_array()) {
|
||||||
|
if (version.is_string()) {
|
||||||
|
auto ver = QString::fromStdString(version.as_string()->value_or(""));
|
||||||
|
if (!ver.isEmpty()) {
|
||||||
|
mod.mcVersions << ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod.mcVersions.sort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // [download] info
|
{ // [download] info
|
||||||
@ -301,11 +338,11 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
toml::table* mod_provider_table = nullptr;
|
toml::table* mod_provider_table = nullptr;
|
||||||
if ((mod_provider_table = update_table[ProviderCaps.name(Provider::FLAME)].as_table())) {
|
if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::name(Provider::FLAME)].as_table())) {
|
||||||
mod.provider = Provider::FLAME;
|
mod.provider = Provider::FLAME;
|
||||||
mod.file_id = intEntry(*mod_provider_table, "file-id");
|
mod.file_id = intEntry(*mod_provider_table, "file-id");
|
||||||
mod.project_id = intEntry(*mod_provider_table, "project-id");
|
mod.project_id = intEntry(*mod_provider_table, "project-id");
|
||||||
} else if ((mod_provider_table = update_table[ProviderCaps.name(Provider::MODRINTH)].as_table())) {
|
} else if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::name(Provider::MODRINTH)].as_table())) {
|
||||||
mod.provider = Provider::MODRINTH;
|
mod.provider = Provider::MODRINTH;
|
||||||
mod.mod_id() = stringEntry(*mod_provider_table, "mod-id");
|
mod.mod_id() = stringEntry(*mod_provider_table, "mod-id");
|
||||||
mod.version() = stringEntry(*mod_provider_table, "version");
|
mod.version() = stringEntry(*mod_provider_table, "version");
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -41,6 +42,9 @@ class V1 {
|
|||||||
QString name{};
|
QString name{};
|
||||||
QString filename{};
|
QString filename{};
|
||||||
Side side{ Side::UniversalSide };
|
Side side{ Side::UniversalSide };
|
||||||
|
ModPlatform::ModLoaderTypes loaders;
|
||||||
|
QStringList mcVersions;
|
||||||
|
ModPlatform::IndexedVersionType releaseType;
|
||||||
|
|
||||||
// [download]
|
// [download]
|
||||||
QString mode{};
|
QString mode{};
|
||||||
|
@ -868,30 +868,6 @@ void MainWindow::on_actionCopyInstance_triggered()
|
|||||||
runModalTask(task.get());
|
runModalTask(task.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::finalizeInstance(InstancePtr inst)
|
|
||||||
{
|
|
||||||
view->updateGeometries();
|
|
||||||
setSelectedInstanceById(inst->id());
|
|
||||||
if (APPLICATION->accounts()->anyAccountIsValid()) {
|
|
||||||
ProgressDialog loadDialog(this);
|
|
||||||
auto update = inst->createUpdateTask(Net::Mode::Online);
|
|
||||||
connect(update.get(), &Task::failed, [this](QString reason) {
|
|
||||||
QString error = QString("Instance load failed: %1").arg(reason);
|
|
||||||
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
|
||||||
});
|
|
||||||
if (update) {
|
|
||||||
loadDialog.setSkipButton(true, tr("Abort"));
|
|
||||||
loadDialog.execWithTask(update.get());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CustomMessageBox::selectable(this, tr("Error"),
|
|
||||||
tr("The launcher cannot download Minecraft or update instances unless you have at least "
|
|
||||||
"one account added.\nPlease add a Microsoft account."),
|
|
||||||
QMessageBox::Warning)
|
|
||||||
->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info)
|
void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info)
|
||||||
{
|
{
|
||||||
QString groupName;
|
QString groupName;
|
||||||
|
@ -229,7 +229,6 @@ class MainWindow : public QMainWindow {
|
|||||||
|
|
||||||
void runModalTask(Task* task);
|
void runModalTask(Task* task);
|
||||||
void instanceFromInstanceTask(InstanceTask* task);
|
void instanceFromInstanceTask(InstanceTask* task);
|
||||||
void finalizeInstance(InstancePtr inst);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow* ui;
|
Ui::MainWindow* ui;
|
||||||
|
@ -266,7 +266,7 @@ void BlockedModsDialog::addHashTask(QString path)
|
|||||||
/// @param path the path to the local file being hashed
|
/// @param path the path to the local file being hashed
|
||||||
void BlockedModsDialog::buildHashTask(QString path)
|
void BlockedModsDialog::buildHashTask(QString path)
|
||||||
{
|
{
|
||||||
auto hash_task = Hashing::createBlockedModHasher(path, ModPlatform::ResourceProvider::FLAME, m_hash_type);
|
auto hash_task = Hashing::createHasher(path, m_hash_type);
|
||||||
|
|
||||||
qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path;
|
qDebug() << "[Blocked Mods Dialog] Creating Hash task for path: " << path;
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
ChooseProviderDialog::ChooseProviderDialog(QWidget* parent, bool single_choice, bool allow_skipping)
|
ChooseProviderDialog::ChooseProviderDialog(QWidget* parent, bool single_choice, bool allow_skipping)
|
||||||
: QDialog(parent), ui(new Ui::ChooseProviderDialog)
|
: QDialog(parent), ui(new Ui::ChooseProviderDialog)
|
||||||
{
|
{
|
||||||
@ -78,7 +76,7 @@ void ChooseProviderDialog::addProviders()
|
|||||||
QRadioButton* btn;
|
QRadioButton* btn;
|
||||||
|
|
||||||
for (auto& provider : { ModPlatform::ResourceProvider::MODRINTH, ModPlatform::ResourceProvider::FLAME }) {
|
for (auto& provider : { ModPlatform::ResourceProvider::MODRINTH, ModPlatform::ResourceProvider::FLAME }) {
|
||||||
btn = new QRadioButton(ProviderCaps.readableName(provider), this);
|
btn = new QRadioButton(ModPlatform::ProviderCapabilities::readableName(provider), this);
|
||||||
m_providers.addButton(btn, btn_index++);
|
m_providers.addButton(btn, btn_index++);
|
||||||
ui->providersLayout->addWidget(btn);
|
ui->providersLayout->addWidget(btn);
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
static std::list<Version> mcVersions(BaseInstance* inst)
|
static std::list<Version> mcVersions(BaseInstance* inst)
|
||||||
{
|
{
|
||||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
|
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
|
||||||
@ -427,7 +425,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
|||||||
item_top->setExpanded(true);
|
item_top->setExpanded(true);
|
||||||
|
|
||||||
auto provider_item = new QTreeWidgetItem(item_top);
|
auto provider_item = new QTreeWidgetItem(item_top);
|
||||||
provider_item->setText(0, tr("Provider: %1").arg(ProviderCaps.readableName(info.provider)));
|
provider_item->setText(0, tr("Provider: %1").arg(ModPlatform::ProviderCapabilities::readableName(info.provider)));
|
||||||
|
|
||||||
auto old_version_item = new QTreeWidgetItem(item_top);
|
auto old_version_item = new QTreeWidgetItem(item_top);
|
||||||
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version));
|
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version));
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "ResourceDownloadTask.h"
|
#include "ResourceDownloadTask.h"
|
||||||
|
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
#include "minecraft/mod/ResourcePackFolderModel.h"
|
#include "minecraft/mod/ResourcePackFolderModel.h"
|
||||||
#include "minecraft/mod/ShaderPackFolderModel.h"
|
#include "minecraft/mod/ShaderPackFolderModel.h"
|
||||||
@ -125,8 +126,6 @@ void ResourceDownloadDialog::connectButtons()
|
|||||||
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
||||||
|
|
||||||
void ResourceDownloadDialog::confirm()
|
void ResourceDownloadDialog::confirm()
|
||||||
{
|
{
|
||||||
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
||||||
@ -168,7 +167,7 @@ void ResourceDownloadDialog::confirm()
|
|||||||
for (auto& task : selected) {
|
for (auto& task : selected) {
|
||||||
auto extraInfo = dependencyExtraInfo.value(task->getPack()->addonId.toString());
|
auto extraInfo = dependencyExtraInfo.value(task->getPack()->addonId.toString());
|
||||||
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
||||||
ProviderCaps.name(task->getProvider()), extraInfo.required_by,
|
ModPlatform::ProviderCapabilities::name(task->getProvider()), extraInfo.required_by,
|
||||||
task->getVersion().version_type.toString(), !extraInfo.maybe_installed });
|
task->getVersion().version_type.toString(), !extraInfo.maybe_installed });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,15 +25,21 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
|
|||||||
Q_ASSERT(m_filter);
|
Q_ASSERT(m_filter);
|
||||||
|
|
||||||
std::optional<std::list<Version>> versions{};
|
std::optional<std::list<Version>> versions{};
|
||||||
|
std::optional<QStringList> categories{};
|
||||||
|
auto loaders = profile->getSupportedModLoaders();
|
||||||
|
|
||||||
{ // Version filter
|
// Version filter
|
||||||
if (!m_filter->versions.empty())
|
if (!m_filter->versions.empty())
|
||||||
versions = m_filter->versions;
|
versions = m_filter->versions;
|
||||||
}
|
if (m_filter->loaders)
|
||||||
|
loaders = m_filter->loaders;
|
||||||
|
if (!m_filter->categoryIds.empty())
|
||||||
|
categories = m_filter->categoryIds;
|
||||||
|
auto side = m_filter->side;
|
||||||
|
|
||||||
auto sort = getCurrentSortingMethodByIndex();
|
auto sort = getCurrentSortingMethodByIndex();
|
||||||
|
|
||||||
return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions };
|
return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side, categories };
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry)
|
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry)
|
||||||
@ -45,10 +51,13 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en
|
|||||||
Q_ASSERT(m_filter);
|
Q_ASSERT(m_filter);
|
||||||
|
|
||||||
std::optional<std::list<Version>> versions{};
|
std::optional<std::list<Version>> versions{};
|
||||||
|
auto loaders = profile->getSupportedModLoaders();
|
||||||
if (!m_filter->versions.empty())
|
if (!m_filter->versions.empty())
|
||||||
versions = m_filter->versions;
|
versions = m_filter->versions;
|
||||||
|
if (m_filter->loaders)
|
||||||
|
loaders = m_filter->loaders;
|
||||||
|
|
||||||
return { pack, versions, profile->getSupportedModLoaders() };
|
return { pack, versions, loaders };
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry)
|
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry)
|
||||||
@ -79,4 +88,43 @@ bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkSide(QString filter, QString value)
|
||||||
|
{
|
||||||
|
return filter.isEmpty() || value.isEmpty() || filter == "both" || value == "both" || filter == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkMcVersions(std::list<Version> filter, QStringList value)
|
||||||
|
{
|
||||||
|
bool valid = false;
|
||||||
|
for (auto mcVersion : filter) {
|
||||||
|
if (value.contains(mcVersion.toString())) {
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter.empty() || valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModModel::checkFilters(ModPlatform::IndexedPack::Ptr pack)
|
||||||
|
{
|
||||||
|
if (!m_filter)
|
||||||
|
return true;
|
||||||
|
return !(m_filter->hideInstalled && isPackInstalled(pack)) && checkSide(m_filter->side, pack->side);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModModel::checkVersionFilters(const ModPlatform::IndexedVersion& v)
|
||||||
|
{
|
||||||
|
if (!m_filter)
|
||||||
|
return true;
|
||||||
|
auto loaders = static_cast<MinecraftInstance&>(m_base_instance).getPackProfile()->getSupportedModLoaders();
|
||||||
|
if (m_filter->loaders)
|
||||||
|
loaders = m_filter->loaders;
|
||||||
|
return (!optedOut(v) && // is opted out(aka curseforge download link)
|
||||||
|
(!loaders.has_value() || !v.loaders || loaders.value() & v.loaders) && // loaders
|
||||||
|
checkSide(m_filter->side, v.side) && // side
|
||||||
|
(m_filter->releases.empty() || // releases
|
||||||
|
std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.version_type) != m_filter->releases.cend()) &&
|
||||||
|
checkMcVersions(m_filter->versions, v.mcVersion)); // mcVersions
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ResourceDownload
|
} // namespace ResourceDownload
|
||||||
|
@ -45,6 +45,9 @@ class ModModel : public ResourceModel {
|
|||||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
||||||
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override;
|
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override;
|
||||||
|
|
||||||
|
virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) override;
|
||||||
|
virtual bool checkVersionFilters(const ModPlatform::IndexedVersion&) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BaseInstance& m_base_instance;
|
BaseInstance& m_base_instance;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -57,7 +58,6 @@ namespace ResourceDownload {
|
|||||||
|
|
||||||
ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance)
|
ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance)
|
||||||
{
|
{
|
||||||
connect(m_ui->searchButton, &QPushButton::clicked, this, &ModPage::triggerSearch);
|
|
||||||
connect(m_ui->resourceFilterButton, &QPushButton::clicked, this, &ModPage::filterMods);
|
connect(m_ui->resourceFilterButton, &QPushButton::clicked, this, &ModPage::filterMods);
|
||||||
connect(m_ui->packView, &QListView::doubleClicked, this, &ModPage::onResourceSelected);
|
connect(m_ui->packView, &QListView::doubleClicked, this, &ModPage::onResourceSelected);
|
||||||
}
|
}
|
||||||
@ -67,17 +67,18 @@ void ModPage::setFilterWidget(unique_qobject_ptr<ModFilterWidget>& widget)
|
|||||||
if (m_filter_widget)
|
if (m_filter_widget)
|
||||||
disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr);
|
disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
auto old = m_ui->splitter->replaceWidget(0, widget.get());
|
||||||
|
// because we replaced the widget we also need to delete it
|
||||||
|
if (old) {
|
||||||
|
delete old;
|
||||||
|
}
|
||||||
|
|
||||||
m_filter_widget.swap(widget);
|
m_filter_widget.swap(widget);
|
||||||
|
|
||||||
m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount());
|
|
||||||
|
|
||||||
m_filter_widget->setInstance(&static_cast<MinecraftInstance&>(m_base_instance));
|
|
||||||
m_filter = m_filter_widget->getFilter();
|
m_filter = m_filter_widget->getFilter();
|
||||||
|
|
||||||
connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this,
|
connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ModPage::triggerSearch);
|
||||||
[&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); });
|
prepareProviderCategories();
|
||||||
connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this,
|
|
||||||
[&] { m_ui->searchButton->setStyleSheet("text-decoration: none"); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******** Callbacks to events in the UI (set up in the derived classes) ********/
|
/******** Callbacks to events in the UI (set up in the derived classes) ********/
|
||||||
@ -89,6 +90,7 @@ void ModPage::filterMods()
|
|||||||
|
|
||||||
void ModPage::triggerSearch()
|
void ModPage::triggerSearch()
|
||||||
{
|
{
|
||||||
|
auto changed = m_filter_widget->changed();
|
||||||
m_filter = m_filter_widget->getFilter();
|
m_filter = m_filter_widget->getFilter();
|
||||||
m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||||
m_ui->packView->clearSelection();
|
m_ui->packView->clearSelection();
|
||||||
@ -96,7 +98,7 @@ void ModPage::triggerSearch()
|
|||||||
m_ui->versionSelectionBox->clear();
|
m_ui->versionSelectionBox->clear();
|
||||||
updateSelectionButton();
|
updateSelectionButton();
|
||||||
|
|
||||||
static_cast<ModModel*>(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), m_filter_widget->changed());
|
static_cast<ModModel*>(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), changed);
|
||||||
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,40 +113,6 @@ QMap<QString, QString> ModPage::urlHandlers() const
|
|||||||
|
|
||||||
/******** Make changes to the UI ********/
|
/******** Make changes to the UI ********/
|
||||||
|
|
||||||
void ModPage::updateVersionList()
|
|
||||||
{
|
|
||||||
m_ui->versionSelectionBox->clear();
|
|
||||||
auto packProfile = (dynamic_cast<MinecraftInstance&>(m_base_instance)).getPackProfile();
|
|
||||||
|
|
||||||
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
|
|
||||||
|
|
||||||
auto current_pack = getCurrentPack();
|
|
||||||
if (!current_pack)
|
|
||||||
return;
|
|
||||||
for (int i = 0; i < current_pack->versions.size(); i++) {
|
|
||||||
auto version = current_pack->versions[i];
|
|
||||||
bool valid = false;
|
|
||||||
for (auto& mcVer : m_filter->versions) {
|
|
||||||
if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) {
|
|
||||||
valid = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out
|
|
||||||
if ((valid || m_filter->versions.empty()) && !optedOut(version)) {
|
|
||||||
auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : "";
|
|
||||||
m_ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_ui->versionSelectionBox->count() == 0) {
|
|
||||||
m_ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1));
|
|
||||||
m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :("));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSelectionButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
|
void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
|
||||||
ModPlatform::IndexedVersion& version,
|
ModPlatform::IndexedVersion& version,
|
||||||
const std::shared_ptr<ResourceFolderModel> base_model)
|
const std::shared_ptr<ResourceFolderModel> base_model)
|
||||||
|
@ -31,8 +31,7 @@ class ModPage : public ResourcePage {
|
|||||||
auto page = new T(dialog, instance);
|
auto page = new T(dialog, instance);
|
||||||
auto model = static_cast<ModModel*>(page->getModel());
|
auto model = static_cast<ModModel*>(page->getModel());
|
||||||
|
|
||||||
auto filter_widget =
|
auto filter_widget = page->createFilterWidget();
|
||||||
ModFilterWidget::create(static_cast<MinecraftInstance&>(instance).getPackProfile()->getComponentVersion("net.minecraft"), page);
|
|
||||||
page->setFilterWidget(filter_widget);
|
page->setFilterWidget(filter_widget);
|
||||||
model->setFilter(page->getFilter());
|
model->setFilter(page->getFilter());
|
||||||
|
|
||||||
@ -51,20 +50,17 @@ class ModPage : public ResourcePage {
|
|||||||
|
|
||||||
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr<ResourceFolderModel>) override;
|
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, std::shared_ptr<ResourceFolderModel>) override;
|
||||||
|
|
||||||
virtual auto validateVersion(ModPlatform::IndexedVersion& ver,
|
virtual unique_qobject_ptr<ModFilterWidget> createFilterWidget() = 0;
|
||||||
QString mineVer,
|
|
||||||
std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const -> bool = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] bool supportsFiltering() const override { return true; };
|
[[nodiscard]] bool supportsFiltering() const override { return true; };
|
||||||
auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }
|
auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }
|
||||||
void setFilterWidget(unique_qobject_ptr<ModFilterWidget>&);
|
void setFilterWidget(unique_qobject_ptr<ModFilterWidget>&);
|
||||||
|
|
||||||
public slots:
|
|
||||||
void updateVersionList() override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ModPage(ModDownloadDialog* dialog, BaseInstance& instance);
|
ModPage(ModDownloadDialog* dialog, BaseInstance& instance);
|
||||||
|
|
||||||
|
virtual void prepareProviderCategories(){};
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void filterMods();
|
virtual void filterMods();
|
||||||
void triggerSearch() override;
|
void triggerSearch() override;
|
||||||
|
@ -410,12 +410,17 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc)
|
|||||||
m_search_state = SearchState::CanFetchMore;
|
m_search_state = SearchState::CanFetchMore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<ModPlatform::IndexedPack::Ptr> filteredNewList;
|
||||||
|
for (auto p : newList)
|
||||||
|
if (checkFilters(p))
|
||||||
|
filteredNewList << p;
|
||||||
|
|
||||||
// When you have a Qt build with assertions turned on, proceeding here will abort the application
|
// When you have a Qt build with assertions turned on, proceeding here will abort the application
|
||||||
if (newList.size() == 0)
|
if (filteredNewList.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + newList.size() - 1);
|
beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + filteredNewList.size() - 1);
|
||||||
m_packs.append(newList);
|
m_packs.append(filteredNewList);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,4 +563,8 @@ void ResourceModel::removePack(const QString& rem)
|
|||||||
ver.is_currently_selected = false;
|
ver.is_currently_selected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ResourceModel::checkVersionFilters(const ModPlatform::IndexedVersion& v)
|
||||||
|
{
|
||||||
|
return (!optedOut(v));
|
||||||
|
}
|
||||||
} // namespace ResourceDownload
|
} // namespace ResourceDownload
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
|
||||||
#include "ResourceDownloadTask.h"
|
#include "ResourceDownloadTask.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
#include "modplatform/ResourceAPI.h"
|
#include "modplatform/ResourceAPI.h"
|
||||||
|
|
||||||
#include "tasks/ConcurrentTask.h"
|
#include "tasks/ConcurrentTask.h"
|
||||||
@ -55,6 +56,16 @@ class ResourceModel : public QAbstractListModel {
|
|||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); }
|
[[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); }
|
||||||
|
|
||||||
|
/** Whether the version is opted out or not. Currently only makes sense in CF. */
|
||||||
|
virtual bool optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(ver);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) { return true; }
|
||||||
|
virtual bool checkVersionFilters(const ModPlatform::IndexedVersion&);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void fetchMore(const QModelIndex& parent) override;
|
void fetchMore(const QModelIndex& parent) override;
|
||||||
// NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12
|
// NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12
|
||||||
|
@ -15,7 +15,6 @@ namespace ResourceDownload {
|
|||||||
|
|
||||||
ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance)
|
ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance)
|
||||||
{
|
{
|
||||||
connect(m_ui->searchButton, &QPushButton::clicked, this, &ResourcePackResourcePage::triggerSearch);
|
|
||||||
connect(m_ui->packView, &QListView::doubleClicked, this, &ResourcePackResourcePage::onResourceSelected);
|
connect(m_ui->packView, &QListView::doubleClicked, this, &ResourcePackResourcePage::onResourceSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -67,11 +68,13 @@ ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_in
|
|||||||
|
|
||||||
connect(&m_search_timer, &QTimer::timeout, this, &ResourcePage::triggerSearch);
|
connect(&m_search_timer, &QTimer::timeout, this, &ResourcePage::triggerSearch);
|
||||||
|
|
||||||
|
// hide progress bar to prevent weird artifact
|
||||||
|
m_fetch_progress.hide();
|
||||||
m_fetch_progress.hideIfInactive(true);
|
m_fetch_progress.hideIfInactive(true);
|
||||||
m_fetch_progress.setFixedHeight(24);
|
m_fetch_progress.setFixedHeight(24);
|
||||||
m_fetch_progress.progressFormat("");
|
m_fetch_progress.progressFormat("");
|
||||||
|
|
||||||
m_ui->gridLayout_3->addWidget(&m_fetch_progress, 0, 0, 1, m_ui->gridLayout_3->columnCount());
|
m_ui->verticalLayout->insertWidget(1, &m_fetch_progress);
|
||||||
|
|
||||||
m_ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
m_ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||||
m_ui->packView->installEventFilter(this);
|
m_ui->packView->installEventFilter(this);
|
||||||
@ -93,8 +96,10 @@ void ResourcePage::retranslate()
|
|||||||
|
|
||||||
void ResourcePage::openedImpl()
|
void ResourcePage::openedImpl()
|
||||||
{
|
{
|
||||||
if (!supportsFiltering())
|
if (!supportsFiltering()) {
|
||||||
m_ui->resourceFilterButton->setVisible(false);
|
m_ui->resourceFilterButton->setVisible(false);
|
||||||
|
m_ui->filterWidget->hide();
|
||||||
|
}
|
||||||
|
|
||||||
//: String in the search bar of the mod downloading dialog
|
//: String in the search bar of the mod downloading dialog
|
||||||
m_ui->searchEdit->setPlaceholderText(tr("Search for %1...").arg(resourcesString()));
|
m_ui->searchEdit->setPlaceholderText(tr("Search for %1...").arg(resourcesString()));
|
||||||
@ -269,7 +274,7 @@ void ResourcePage::updateVersionList()
|
|||||||
if (current_pack)
|
if (current_pack)
|
||||||
for (int i = 0; i < current_pack->versions.size(); i++) {
|
for (int i = 0; i < current_pack->versions.size(); i++) {
|
||||||
auto& version = current_pack->versions[i];
|
auto& version = current_pack->versions[i];
|
||||||
if (optedOut(version))
|
if (!m_model->checkVersionFilters(version))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto release_type = current_pack->versions[i].version_type.isValid()
|
auto release_type = current_pack->versions[i].version_type.isValid()
|
||||||
|
@ -84,7 +84,7 @@ class ResourcePage : public QWidget, public BasePage {
|
|||||||
bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); }
|
bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); }
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void triggerSearch() {}
|
virtual void triggerSearch() = 0;
|
||||||
|
|
||||||
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
||||||
void onVersionSelectionChanged(QString data);
|
void onVersionSelectionChanged(QString data);
|
||||||
@ -96,13 +96,6 @@ class ResourcePage : public QWidget, public BasePage {
|
|||||||
virtual QMap<QString, QString> urlHandlers() const = 0;
|
virtual QMap<QString, QString> urlHandlers() const = 0;
|
||||||
virtual void openUrl(const QUrl&);
|
virtual void openUrl(const QUrl&);
|
||||||
|
|
||||||
/** Whether the version is opted out or not. Currently only makes sense in CF. */
|
|
||||||
virtual bool optedOut(ModPlatform::IndexedVersion& ver) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(ver);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BaseInstance& m_base_instance;
|
BaseInstance& m_base_instance;
|
||||||
|
|
||||||
|
@ -10,20 +10,30 @@
|
|||||||
<height>685</height>
|
<height>685</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item row="1" column="0" colspan="4">
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item row="1" column="2">
|
<item>
|
||||||
<widget class="ProjectDescriptionPage" name="packDescription">
|
<widget class="QPushButton" name="resourceFilterButton">
|
||||||
<property name="openExternalLinks">
|
<property name="text">
|
||||||
<bool>false</bool>
|
<string>Filter options</string>
|
||||||
</property>
|
|
||||||
<property name="openLinks">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item>
|
||||||
|
<widget class="QLineEdit" name="searchEdit"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="childrenCollapsible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="filterWidget" native="true"/>
|
||||||
<widget class="QListView" name="packView">
|
<widget class="QListView" name="packView">
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
@ -38,20 +48,17 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
<widget class="ProjectDescriptionPage" name="packDescription">
|
||||||
</layout>
|
<property name="openExternalLinks">
|
||||||
</item>
|
<bool>false</bool>
|
||||||
<item row="0" column="3">
|
</property>
|
||||||
<widget class="QPushButton" name="searchButton">
|
<property name="openLinks">
|
||||||
<property name="text">
|
<bool>false</bool>
|
||||||
<string>Search</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="QLineEdit" name="searchEdit"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="4">
|
|
||||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0">
|
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0">
|
||||||
<item row="0" column="2">
|
<item row="0" column="2">
|
||||||
<widget class="QComboBox" name="versionSelectionBox"/>
|
<widget class="QComboBox" name="versionSelectionBox"/>
|
||||||
@ -74,20 +81,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QPushButton" name="resourceFilterButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Filter options</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
@ -98,8 +91,6 @@
|
|||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>searchEdit</tabstop>
|
|
||||||
<tabstop>searchButton</tabstop>
|
|
||||||
<tabstop>packView</tabstop>
|
<tabstop>packView</tabstop>
|
||||||
<tabstop>packDescription</tabstop>
|
<tabstop>packDescription</tabstop>
|
||||||
<tabstop>sortByBox</tabstop>
|
<tabstop>sortByBox</tabstop>
|
||||||
|
@ -16,7 +16,6 @@ namespace ResourceDownload {
|
|||||||
|
|
||||||
ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance)
|
ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance)
|
||||||
{
|
{
|
||||||
connect(m_ui->searchButton, &QPushButton::clicked, this, &ShaderPackResourcePage::triggerSearch);
|
|
||||||
connect(m_ui->packView, &QListView::doubleClicked, this, &ShaderPackResourcePage::onResourceSelected);
|
connect(m_ui->packView, &QListView::doubleClicked, this, &ShaderPackResourcePage::onResourceSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ class TexturePackResourcePage : public ResourcePackResourcePage {
|
|||||||
protected:
|
protected:
|
||||||
TexturePackResourcePage(TexturePackDownloadDialog* dialog, BaseInstance& instance) : ResourcePackResourcePage(dialog, instance)
|
TexturePackResourcePage(TexturePackDownloadDialog* dialog, BaseInstance& instance) : ResourcePackResourcePage(dialog, instance)
|
||||||
{
|
{
|
||||||
connect(m_ui->searchButton, &QPushButton::clicked, this, &TexturePackResourcePage::triggerSearch);
|
|
||||||
connect(m_ui->packView, &QListView::doubleClicked, this, &TexturePackResourcePage::onResourceSelected);
|
connect(m_ui->packView, &QListView::doubleClicked, this, &TexturePackResourcePage::onResourceSelected);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,11 +6,17 @@
|
|||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
#include "modplatform/flame/FlameModIndex.h"
|
#include "modplatform/flame/FlameModIndex.h"
|
||||||
|
|
||||||
namespace ResourceDownload {
|
namespace ResourceDownload {
|
||||||
|
|
||||||
|
static bool isOptedOut(const ModPlatform::IndexedVersion& ver)
|
||||||
|
{
|
||||||
|
return ver.downloadUrl.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {}
|
FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {}
|
||||||
|
|
||||||
void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||||
@ -34,6 +40,11 @@ auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJs
|
|||||||
return FlameMod::loadDependencyVersions(m, arr, &m_base_instance);
|
return FlameMod::loadDependencyVersions(m, arr, &m_base_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlameModModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||||
|
{
|
||||||
|
return isOptedOut(ver);
|
||||||
|
}
|
||||||
|
|
||||||
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||||
{
|
{
|
||||||
return Json::ensureArray(obj.object(), "data");
|
return Json::ensureArray(obj.object(), "data");
|
||||||
@ -57,6 +68,11 @@ void FlameResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m
|
|||||||
FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);
|
FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlameResourcePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||||
|
{
|
||||||
|
return isOptedOut(ver);
|
||||||
|
}
|
||||||
|
|
||||||
auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||||
{
|
{
|
||||||
return Json::ensureArray(obj.object(), "data");
|
return Json::ensureArray(obj.object(), "data");
|
||||||
@ -116,6 +132,11 @@ ResourceAPI::VersionSearchArgs FlameTexturePackModel::createVersionsArguments(QM
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlameTexturePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||||
|
{
|
||||||
|
return isOptedOut(ver);
|
||||||
|
}
|
||||||
|
|
||||||
auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||||
{
|
{
|
||||||
return Json::ensureArray(obj.object(), "data");
|
return Json::ensureArray(obj.object(), "data");
|
||||||
@ -139,6 +160,11 @@ void FlameShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m,
|
|||||||
FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);
|
FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlameShaderPackModel::optedOut(const ModPlatform::IndexedVersion& ver) const
|
||||||
|
{
|
||||||
|
return isOptedOut(ver);
|
||||||
|
}
|
||||||
|
|
||||||
auto FlameShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
auto FlameShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||||
{
|
{
|
||||||
return Json::ensureArray(obj.object(), "data");
|
return Json::ensureArray(obj.object(), "data");
|
||||||
|
@ -17,6 +17,8 @@ class FlameModModel : public ModModel {
|
|||||||
FlameModModel(BaseInstance&);
|
FlameModModel(BaseInstance&);
|
||||||
~FlameModModel() override = default;
|
~FlameModModel() override = default;
|
||||||
|
|
||||||
|
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||||
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||||
@ -36,6 +38,8 @@ class FlameResourcePackModel : public ResourcePackResourceModel {
|
|||||||
FlameResourcePackModel(const BaseInstance&);
|
FlameResourcePackModel(const BaseInstance&);
|
||||||
~FlameResourcePackModel() override = default;
|
~FlameResourcePackModel() override = default;
|
||||||
|
|
||||||
|
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||||
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||||
@ -54,6 +58,8 @@ class FlameTexturePackModel : public TexturePackResourceModel {
|
|||||||
FlameTexturePackModel(const BaseInstance&);
|
FlameTexturePackModel(const BaseInstance&);
|
||||||
~FlameTexturePackModel() override = default;
|
~FlameTexturePackModel() override = default;
|
||||||
|
|
||||||
|
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||||
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||||
@ -75,6 +81,8 @@ class FlameShaderPackModel : public ShaderPackResourceModel {
|
|||||||
FlameShaderPackModel(const BaseInstance&);
|
FlameShaderPackModel(const BaseInstance&);
|
||||||
~FlameShaderPackModel() override = default;
|
~FlameShaderPackModel() override = default;
|
||||||
|
|
||||||
|
bool optedOut(const ModPlatform::IndexedVersion& ver) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
[[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; }
|
||||||
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
[[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); }
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -37,6 +38,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FlameResourcePages.h"
|
#include "FlameResourcePages.h"
|
||||||
|
#include <QList>
|
||||||
|
#include <memory>
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
#include "ui_ResourcePage.h"
|
#include "ui_ResourcePage.h"
|
||||||
|
|
||||||
#include "FlameResourceModels.h"
|
#include "FlameResourceModels.h"
|
||||||
@ -44,11 +49,6 @@
|
|||||||
|
|
||||||
namespace ResourceDownload {
|
namespace ResourceDownload {
|
||||||
|
|
||||||
static bool isOptedOut(ModPlatform::IndexedVersion const& ver)
|
|
||||||
{
|
|
||||||
return ver.downloadUrl.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance)
|
FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance)
|
||||||
{
|
{
|
||||||
m_model = new FlameModModel(instance);
|
m_model = new FlameModModel(instance);
|
||||||
@ -66,19 +66,6 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) :
|
|||||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver,
|
|
||||||
QString mineVer,
|
|
||||||
std::optional<ModPlatform::ModLoaderTypes> loaders) const -> bool
|
|
||||||
{
|
|
||||||
return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() &&
|
|
||||||
(!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
|
||||||
{
|
|
||||||
return isOptedOut(ver);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameModPage::openUrl(const QUrl& url)
|
void FlameModPage::openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
if (url.scheme().isEmpty()) {
|
if (url.scheme().isEmpty()) {
|
||||||
@ -113,11 +100,6 @@ FlameResourcePackPage::FlameResourcePackPage(ResourcePackDownloadDialog* dialog,
|
|||||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlameResourcePackPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
|
||||||
{
|
|
||||||
return isOptedOut(ver);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameResourcePackPage::openUrl(const QUrl& url)
|
void FlameResourcePackPage::openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
if (url.scheme().isEmpty()) {
|
if (url.scheme().isEmpty()) {
|
||||||
@ -152,11 +134,6 @@ FlameTexturePackPage::FlameTexturePackPage(TexturePackDownloadDialog* dialog, Ba
|
|||||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlameTexturePackPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
|
||||||
{
|
|
||||||
return isOptedOut(ver);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameTexturePackPage::openUrl(const QUrl& url)
|
void FlameTexturePackPage::openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
if (url.scheme().isEmpty()) {
|
if (url.scheme().isEmpty()) {
|
||||||
@ -191,11 +168,6 @@ FlameShaderPackPage::FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseI
|
|||||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlameShaderPackPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
|
||||||
{
|
|
||||||
return isOptedOut(ver);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FlameShaderPackPage::openUrl(const QUrl& url)
|
void FlameShaderPackPage::openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
if (url.scheme().isEmpty()) {
|
if (url.scheme().isEmpty()) {
|
||||||
@ -232,4 +204,19 @@ auto FlameShaderPackPage::shouldDisplay() const -> bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unique_qobject_ptr<ModFilterWidget> FlameModPage::createFilterWidget()
|
||||||
|
{
|
||||||
|
return ModFilterWidget::create(&static_cast<MinecraftInstance&>(m_base_instance), false, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlameModPage::prepareProviderCategories()
|
||||||
|
{
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
auto task = FlameAPI::getModCategories(response);
|
||||||
|
QObject::connect(task.get(), &Task::succeeded, [this, response]() {
|
||||||
|
auto categories = FlameAPI::loadModCategories(response);
|
||||||
|
m_filter_widget->setCategories(categories);
|
||||||
|
});
|
||||||
|
task->start();
|
||||||
|
};
|
||||||
} // namespace ResourceDownload
|
} // namespace ResourceDownload
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -94,12 +95,11 @@ class FlameModPage : public ModPage {
|
|||||||
|
|
||||||
[[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; }
|
[[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; }
|
||||||
|
|
||||||
bool validateVersion(ModPlatform::IndexedVersion& ver,
|
|
||||||
QString mineVer,
|
|
||||||
std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const override;
|
|
||||||
bool optedOut(ModPlatform::IndexedVersion& ver) const override;
|
|
||||||
|
|
||||||
void openUrl(const QUrl& url) override;
|
void openUrl(const QUrl& url) override;
|
||||||
|
unique_qobject_ptr<ModFilterWidget> createFilterWidget() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void prepareProviderCategories() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlameResourcePackPage : public ResourcePackResourcePage {
|
class FlameResourcePackPage : public ResourcePackResourcePage {
|
||||||
@ -124,8 +124,6 @@ class FlameResourcePackPage : public ResourcePackResourcePage {
|
|||||||
|
|
||||||
[[nodiscard]] inline auto helpPage() const -> QString override { return ""; }
|
[[nodiscard]] inline auto helpPage() const -> QString override { return ""; }
|
||||||
|
|
||||||
bool optedOut(ModPlatform::IndexedVersion& ver) const override;
|
|
||||||
|
|
||||||
void openUrl(const QUrl& url) override;
|
void openUrl(const QUrl& url) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,8 +149,6 @@ class FlameTexturePackPage : public TexturePackResourcePage {
|
|||||||
|
|
||||||
[[nodiscard]] inline auto helpPage() const -> QString override { return ""; }
|
[[nodiscard]] inline auto helpPage() const -> QString override { return ""; }
|
||||||
|
|
||||||
bool optedOut(ModPlatform::IndexedVersion& ver) const override;
|
|
||||||
|
|
||||||
void openUrl(const QUrl& url) override;
|
void openUrl(const QUrl& url) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,8 +174,6 @@ class FlameShaderPackPage : public ShaderPackResourcePage {
|
|||||||
|
|
||||||
[[nodiscard]] inline auto helpPage() const -> QString override { return ""; }
|
[[nodiscard]] inline auto helpPage() const -> QString override { return ""; }
|
||||||
|
|
||||||
bool optedOut(ModPlatform::IndexedVersion& ver) const override;
|
|
||||||
|
|
||||||
void openUrl(const QUrl& url) override;
|
void openUrl(const QUrl& url) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -63,13 +64,6 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan
|
|||||||
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
m_ui->packDescription->setMetaEntry(metaEntryBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver,
|
|
||||||
QString mineVer,
|
|
||||||
std::optional<ModPlatform::ModLoaderTypes> loaders) const -> bool
|
|
||||||
{
|
|
||||||
return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance)
|
ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance)
|
||||||
: ResourcePackResourcePage(dialog, instance)
|
: ResourcePackResourcePage(dialog, instance)
|
||||||
{
|
{
|
||||||
@ -144,4 +138,19 @@ auto ModrinthShaderPackPage::shouldDisplay() const -> bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unique_qobject_ptr<ModFilterWidget> ModrinthModPage::createFilterWidget()
|
||||||
|
{
|
||||||
|
return ModFilterWidget::create(&static_cast<MinecraftInstance&>(m_base_instance), true, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModrinthModPage::prepareProviderCategories()
|
||||||
|
{
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
auto task = ModrinthAPI::getModCategories(response);
|
||||||
|
QObject::connect(task.get(), &Task::succeeded, [this, response]() {
|
||||||
|
auto categories = ModrinthAPI::loadModCategories(response);
|
||||||
|
m_filter_widget->setCategories(categories);
|
||||||
|
});
|
||||||
|
task->start();
|
||||||
|
};
|
||||||
} // namespace ResourceDownload
|
} // namespace ResourceDownload
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
/*
|
/*
|
||||||
* Prism Launcher - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -93,8 +94,10 @@ class ModrinthModPage : public ModPage {
|
|||||||
|
|
||||||
[[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; }
|
[[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; }
|
||||||
|
|
||||||
auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const
|
unique_qobject_ptr<ModFilterWidget> createFilterWidget() override;
|
||||||
-> bool override;
|
|
||||||
|
protected:
|
||||||
|
virtual void prepareProviderCategories() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModrinthResourcePackPage : public ResourcePackResourcePage {
|
class ModrinthResourcePackPage : public ResourcePackResourcePage {
|
||||||
|
206
launcher/ui/widgets/CheckComboBox.cpp
Normal file
206
launcher/ui/widgets/CheckComboBox.cpp
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CheckComboBox.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemView>
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QIdentityProxyModel>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QStylePainter>
|
||||||
|
|
||||||
|
class CheckComboModel : public QIdentityProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CheckComboModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {}
|
||||||
|
|
||||||
|
virtual Qt::ItemFlags flags(const QModelIndex& index) const { return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable; }
|
||||||
|
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
|
||||||
|
{
|
||||||
|
if (role == Qt::CheckStateRole) {
|
||||||
|
auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString();
|
||||||
|
return checked.contains(txt) ? Qt::Checked : Qt::Unchecked;
|
||||||
|
}
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return QIdentityProxyModel::data(index, Qt::DisplayRole);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole)
|
||||||
|
{
|
||||||
|
if (role == Qt::CheckStateRole) {
|
||||||
|
auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString();
|
||||||
|
if (checked.contains(txt)) {
|
||||||
|
checked.removeOne(txt);
|
||||||
|
} else {
|
||||||
|
checked.push_back(txt);
|
||||||
|
}
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
emit checkStateChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return QIdentityProxyModel::setData(index, value, role);
|
||||||
|
}
|
||||||
|
QStringList getChecked() { return checked; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void checkStateChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList checked;
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ")
|
||||||
|
{
|
||||||
|
view()->installEventFilter(this);
|
||||||
|
view()->window()->installEventFilter(this);
|
||||||
|
view()->viewport()->installEventFilter(this);
|
||||||
|
this->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::setSourceModel(QAbstractItemModel* new_model)
|
||||||
|
{
|
||||||
|
auto proxy = new CheckComboModel(this);
|
||||||
|
proxy->setSourceModel(new_model);
|
||||||
|
model()->disconnect(this);
|
||||||
|
QComboBox::setModel(proxy);
|
||||||
|
connect(this, QOverload<int>::of(&QComboBox::activated), this, &CheckComboBox::toggleCheckState);
|
||||||
|
connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::emitCheckedItemsChanged);
|
||||||
|
connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::emitCheckedItemsChanged);
|
||||||
|
connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::emitCheckedItemsChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::hidePopup()
|
||||||
|
{
|
||||||
|
if (!containerMousePress)
|
||||||
|
QComboBox::hidePopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::emitCheckedItemsChanged()
|
||||||
|
{
|
||||||
|
emit checkedItemsChanged(checkedItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CheckComboBox::defaultText() const
|
||||||
|
{
|
||||||
|
return m_default_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::setDefaultText(const QString& text)
|
||||||
|
{
|
||||||
|
m_default_text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CheckComboBox::separator() const
|
||||||
|
{
|
||||||
|
return m_separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::setSeparator(const QString& separator)
|
||||||
|
{
|
||||||
|
m_separator = separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event)
|
||||||
|
{
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::KeyPress:
|
||||||
|
case QEvent::KeyRelease: {
|
||||||
|
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
|
if (receiver == this && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) {
|
||||||
|
showPopup();
|
||||||
|
return true;
|
||||||
|
} else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Escape) {
|
||||||
|
QComboBox::hidePopup();
|
||||||
|
return (keyEvent->key() != Qt::Key_Escape);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::MouseButtonPress: {
|
||||||
|
auto ev = static_cast<QMouseEvent*>(event);
|
||||||
|
containerMousePress = ev && view()->indexAt(ev->pos()).isValid();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::Wheel:
|
||||||
|
return receiver == this;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::toggleCheckState(int index)
|
||||||
|
{
|
||||||
|
QVariant value = itemData(index, Qt::CheckStateRole);
|
||||||
|
if (value.isValid()) {
|
||||||
|
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
|
||||||
|
setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
emitCheckedItemsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::CheckState CheckComboBox::itemCheckState(int index) const
|
||||||
|
{
|
||||||
|
return static_cast<Qt::CheckState>(itemData(index, Qt::CheckStateRole).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::setItemCheckState(int index, Qt::CheckState state)
|
||||||
|
{
|
||||||
|
setItemData(index, state, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList CheckComboBox::checkedItems() const
|
||||||
|
{
|
||||||
|
if (model())
|
||||||
|
return dynamic_cast<CheckComboModel*>(model())->getChecked();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::setCheckedItems(const QStringList& items)
|
||||||
|
{
|
||||||
|
foreach (auto text, items) {
|
||||||
|
auto index = findText(text);
|
||||||
|
setItemCheckState(index, index != -1 ? Qt::Checked : Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckComboBox::paintEvent(QPaintEvent*)
|
||||||
|
{
|
||||||
|
QStylePainter painter(this);
|
||||||
|
painter.setPen(palette().color(QPalette::Text));
|
||||||
|
|
||||||
|
// draw the combobox frame, focusrect and selected etc.
|
||||||
|
QStyleOptionComboBox opt;
|
||||||
|
initStyleOption(&opt);
|
||||||
|
QStringList items = checkedItems();
|
||||||
|
if (items.isEmpty())
|
||||||
|
opt.currentText = defaultText();
|
||||||
|
else
|
||||||
|
opt.currentText = items.join(separator());
|
||||||
|
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
|
||||||
|
|
||||||
|
// draw the icon and text
|
||||||
|
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "CheckComboBox.moc"
|
64
launcher/ui/widgets/CheckComboBox.h
Normal file
64
launcher/ui/widgets/CheckComboBox.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class CheckComboBox : public QComboBox {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CheckComboBox(QWidget* parent = nullptr);
|
||||||
|
virtual ~CheckComboBox() = default;
|
||||||
|
|
||||||
|
void hidePopup() override;
|
||||||
|
|
||||||
|
QString defaultText() const;
|
||||||
|
void setDefaultText(const QString& text);
|
||||||
|
|
||||||
|
Qt::CheckState itemCheckState(int index) const;
|
||||||
|
void setItemCheckState(int index, Qt::CheckState state);
|
||||||
|
|
||||||
|
QString separator() const;
|
||||||
|
void setSeparator(const QString& separator);
|
||||||
|
|
||||||
|
QStringList checkedItems() const;
|
||||||
|
|
||||||
|
void setSourceModel(QAbstractItemModel* model);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setCheckedItems(const QStringList& items);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void checkedItemsChanged(const QStringList& items);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void emitCheckedItemsChanged();
|
||||||
|
bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||||
|
void toggleCheckState(int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_default_text;
|
||||||
|
QString m_separator;
|
||||||
|
bool containerMousePress;
|
||||||
|
};
|
@ -1,13 +1,139 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ModFilterWidget.h"
|
#include "ModFilterWidget.h"
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <list>
|
||||||
|
#include "BaseVersionList.h"
|
||||||
|
#include "Version.h"
|
||||||
|
#include "meta/Index.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "ui/widgets/CheckComboBox.h"
|
||||||
#include "ui_ModFilterWidget.h"
|
#include "ui_ModFilterWidget.h"
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
|
|
||||||
unique_qobject_ptr<ModFilterWidget> ModFilterWidget::create(Version default_version, QWidget* parent)
|
unique_qobject_ptr<ModFilterWidget> ModFilterWidget::create(MinecraftInstance* instance, bool extended, QWidget* parent)
|
||||||
{
|
{
|
||||||
auto filter_widget = new ModFilterWidget(default_version, parent);
|
return unique_qobject_ptr<ModFilterWidget>(new ModFilterWidget(instance, extended, parent));
|
||||||
|
}
|
||||||
|
|
||||||
if (!filter_widget->versionList()->isLoaded()) {
|
class VersionBasicModel : public QIdentityProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VersionBasicModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {}
|
||||||
|
|
||||||
|
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWidget* parent)
|
||||||
|
: QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter())
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
m_versions_proxy = new VersionProxyModel(this);
|
||||||
|
m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release"));
|
||||||
|
|
||||||
|
auto proxy = new VersionBasicModel(this);
|
||||||
|
proxy->setSourceModel(m_versions_proxy);
|
||||||
|
|
||||||
|
if (extended) {
|
||||||
|
ui->versions->setSourceModel(proxy);
|
||||||
|
ui->versions->setSeparator(", ");
|
||||||
|
ui->version->hide();
|
||||||
|
} else {
|
||||||
|
ui->version->setModel(proxy);
|
||||||
|
ui->versions->hide();
|
||||||
|
ui->showAllVersions->hide();
|
||||||
|
ui->environmentGroup->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->versions->setStyleSheet("combobox-popup: 0;");
|
||||||
|
ui->version->setStyleSheet("combobox-popup: 0;");
|
||||||
|
connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged);
|
||||||
|
connect(ui->versions, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged);
|
||||||
|
connect(ui->versions, &CheckComboBox::checkedItemsChanged, this, [this] { onVersionFilterChanged(0); });
|
||||||
|
connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged);
|
||||||
|
|
||||||
|
connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
|
||||||
|
connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||||
|
|
||||||
|
if (extended) {
|
||||||
|
connect(ui->clientSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged);
|
||||||
|
connect(ui->serverSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->hideInstalled, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged);
|
||||||
|
|
||||||
|
setHidden(true);
|
||||||
|
loadVersionList();
|
||||||
|
prepareBasicFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ModFilterWidget::getFilter() -> std::shared_ptr<Filter>
|
||||||
|
{
|
||||||
|
m_filter_changed = false;
|
||||||
|
return m_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModFilterWidget::~ModFilterWidget()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModFilterWidget::loadVersionList()
|
||||||
|
{
|
||||||
|
m_version_list = APPLICATION->metadataIndex()->get("net.minecraft");
|
||||||
|
if (!m_version_list->isLoaded()) {
|
||||||
QEventLoop load_version_list_loop;
|
QEventLoop load_version_list_loop;
|
||||||
|
|
||||||
QTimer time_limit_for_list_load;
|
QTimer time_limit_for_list_load;
|
||||||
@ -16,10 +142,12 @@ unique_qobject_ptr<ModFilterWidget> ModFilterWidget::create(Version default_vers
|
|||||||
time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit);
|
time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit);
|
||||||
time_limit_for_list_load.start(4000);
|
time_limit_for_list_load.start(4000);
|
||||||
|
|
||||||
auto task = filter_widget->versionList()->getLoadTask();
|
auto task = m_version_list->getLoadTask();
|
||||||
|
|
||||||
connect(task.get(), &Task::failed,
|
connect(task.get(), &Task::failed, [this] {
|
||||||
[filter_widget] { filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); });
|
ui->versions->setEnabled(false);
|
||||||
|
ui->showAllVersions->setEnabled(false);
|
||||||
|
});
|
||||||
connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit);
|
connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit);
|
||||||
|
|
||||||
if (!task->isRunning())
|
if (!task->isRunning())
|
||||||
@ -29,128 +157,132 @@ unique_qobject_ptr<ModFilterWidget> ModFilterWidget::create(Version default_vers
|
|||||||
if (time_limit_for_list_load.isActive())
|
if (time_limit_for_list_load.isActive())
|
||||||
time_limit_for_list_load.stop();
|
time_limit_for_list_load.stop();
|
||||||
}
|
}
|
||||||
|
m_versions_proxy->setSourceModel(m_version_list.get());
|
||||||
return unique_qobject_ptr<ModFilterWidget>(filter_widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget)
|
void ModFilterWidget::prepareBasicFilter()
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
m_filter->hideInstalled = false;
|
||||||
|
m_filter->side = ""; // or "both"
|
||||||
m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict);
|
auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value();
|
||||||
ui->strictVersionButton->click();
|
ui->neoForge->setChecked(loaders & ModPlatform::NeoForge);
|
||||||
m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major);
|
ui->forge->setChecked(loaders & ModPlatform::Forge);
|
||||||
m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All);
|
ui->fabric->setChecked(loaders & ModPlatform::Fabric);
|
||||||
// m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between);
|
ui->quilt->setChecked(loaders & ModPlatform::Quilt);
|
||||||
|
m_filter->loaders = loaders;
|
||||||
connect(&m_mcVersion_buttons, SIGNAL(idClicked(int)), this, SLOT(onVersionFilterChanged(int)));
|
auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft");
|
||||||
|
m_filter->versions.emplace_front(def);
|
||||||
m_filter->versions.push_front(def);
|
ui->versions->setCheckedItems({ def });
|
||||||
|
ui->version->setCurrentIndex(ui->version->findText(def));
|
||||||
m_version_list = APPLICATION->metadataIndex()->get("net.minecraft");
|
|
||||||
setHidden(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModFilterWidget::setInstance(MinecraftInstance* instance)
|
void ModFilterWidget::onShowAllVersionsChanged()
|
||||||
{
|
{
|
||||||
m_instance = instance;
|
if (ui->showAllVersions->isChecked())
|
||||||
|
m_versions_proxy->clearFilters();
|
||||||
ui->strictVersionButton->setText(tr("Strict match (= %1)").arg(mcVersionStr()));
|
|
||||||
|
|
||||||
// we can't do this for snapshots sadly
|
|
||||||
if (mcVersionStr().contains('.')) {
|
|
||||||
auto mcVersionSplit = mcVersionStr().split(".");
|
|
||||||
ui->majorVersionButton->setText(tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1]));
|
|
||||||
} else {
|
|
||||||
ui->majorVersionButton->setText(tr("Major version match (unsupported)"));
|
|
||||||
disableVersionButton(Major);
|
|
||||||
}
|
|
||||||
ui->allVersionsButton->setText(tr("Any version"));
|
|
||||||
// ui->betweenVersionsButton->setText(
|
|
||||||
// tr("Between two versions"));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ModFilterWidget::getFilter() -> std::shared_ptr<Filter>
|
|
||||||
{
|
|
||||||
m_last_version_id = m_version_id;
|
|
||||||
emit filterUnchanged();
|
|
||||||
return m_filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason)
|
|
||||||
{
|
|
||||||
QAbstractButton* btn = nullptr;
|
|
||||||
|
|
||||||
switch (id) {
|
|
||||||
case (VersionButtonID::Strict):
|
|
||||||
btn = ui->strictVersionButton;
|
|
||||||
break;
|
|
||||||
case (VersionButtonID::Major):
|
|
||||||
btn = ui->majorVersionButton;
|
|
||||||
break;
|
|
||||||
case (VersionButtonID::All):
|
|
||||||
btn = ui->allVersionsButton;
|
|
||||||
break;
|
|
||||||
case (VersionButtonID::Between):
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btn) {
|
|
||||||
btn->setEnabled(false);
|
|
||||||
if (!reason.isEmpty())
|
|
||||||
btn->setText(btn->text() + QString(" (%1)").arg(reason));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModFilterWidget::onVersionFilterChanged(int id)
|
|
||||||
{
|
|
||||||
// ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between);
|
|
||||||
// ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between);
|
|
||||||
|
|
||||||
int index = 1;
|
|
||||||
|
|
||||||
auto cast_id = (VersionButtonID)id;
|
|
||||||
if (cast_id != m_version_id) {
|
|
||||||
m_version_id = cast_id;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_filter->versions.clear();
|
|
||||||
|
|
||||||
switch (cast_id) {
|
|
||||||
case (VersionButtonID::Strict):
|
|
||||||
m_filter->versions.push_front(mcVersion());
|
|
||||||
break;
|
|
||||||
case (VersionButtonID::Major): {
|
|
||||||
auto versionSplit = mcVersionStr().split(".");
|
|
||||||
|
|
||||||
auto major_version = QString("%1.%2").arg(versionSplit[0], versionSplit[1]);
|
|
||||||
QString version_str = major_version;
|
|
||||||
|
|
||||||
while (m_version_list->hasVersion(version_str)) {
|
|
||||||
m_filter->versions.emplace_back(version_str);
|
|
||||||
version_str = QString("%1.%2").arg(major_version, QString::number(index++));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case (VersionButtonID::All):
|
|
||||||
// Empty list to avoid enumerating all versions :P
|
|
||||||
break;
|
|
||||||
case (VersionButtonID::Between):
|
|
||||||
// TODO
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed())
|
|
||||||
emit filterChanged();
|
|
||||||
else
|
else
|
||||||
emit filterUnchanged();
|
m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ModFilterWidget::~ModFilterWidget()
|
void ModFilterWidget::onVersionFilterChanged(int)
|
||||||
{
|
{
|
||||||
delete ui;
|
auto versions = ui->versions->checkedItems();
|
||||||
|
versions.sort();
|
||||||
|
std::list<Version> current_list;
|
||||||
|
|
||||||
|
for (const QString& version : versions)
|
||||||
|
current_list.emplace_back(version);
|
||||||
|
|
||||||
|
m_filter_changed = m_filter->versions.size() != current_list.size() ||
|
||||||
|
!std::equal(m_filter->versions.begin(), m_filter->versions.end(), current_list.begin(), current_list.end());
|
||||||
|
m_filter->versions = current_list;
|
||||||
|
if (m_filter_changed)
|
||||||
|
emit filterChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModFilterWidget::onLoadersFilterChanged()
|
||||||
|
{
|
||||||
|
ModPlatform::ModLoaderTypes loaders;
|
||||||
|
if (ui->neoForge->isChecked())
|
||||||
|
loaders |= ModPlatform::NeoForge;
|
||||||
|
if (ui->forge->isChecked())
|
||||||
|
loaders |= ModPlatform::Forge;
|
||||||
|
if (ui->fabric->isChecked())
|
||||||
|
loaders |= ModPlatform::Fabric;
|
||||||
|
if (ui->quilt->isChecked())
|
||||||
|
loaders |= ModPlatform::Quilt;
|
||||||
|
m_filter_changed = loaders != m_filter->loaders;
|
||||||
|
m_filter->loaders = loaders;
|
||||||
|
if (m_filter_changed)
|
||||||
|
emit filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModFilterWidget::onSideFilterChanged()
|
||||||
|
{
|
||||||
|
QString side;
|
||||||
|
|
||||||
|
if (ui->clientSide->isChecked() != ui->serverSide->isChecked()) {
|
||||||
|
if (ui->clientSide->isChecked())
|
||||||
|
side = "client";
|
||||||
|
else
|
||||||
|
side = "server";
|
||||||
|
} else {
|
||||||
|
// both are checked or none are checked; in either case no filtering will happen
|
||||||
|
side = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_filter_changed = side != m_filter->side;
|
||||||
|
m_filter->side = side;
|
||||||
|
if (m_filter_changed)
|
||||||
|
emit filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModFilterWidget::onHideInstalledFilterChanged()
|
||||||
|
{
|
||||||
|
auto hide = ui->hideInstalled->isChecked();
|
||||||
|
m_filter_changed = hide != m_filter->hideInstalled;
|
||||||
|
m_filter->hideInstalled = hide;
|
||||||
|
if (m_filter_changed)
|
||||||
|
emit filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModFilterWidget::onVersionFilterTextChanged(const QString& version)
|
||||||
|
{
|
||||||
|
m_filter->versions.clear();
|
||||||
|
m_filter->versions.emplace_back(version);
|
||||||
|
m_filter_changed = true;
|
||||||
|
emit filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModFilterWidget::setCategories(const QList<ModPlatform::Category>& categories)
|
||||||
|
{
|
||||||
|
m_categories = categories;
|
||||||
|
|
||||||
|
delete ui->categoryGroup->layout();
|
||||||
|
auto layout = new QVBoxLayout(ui->categoryGroup);
|
||||||
|
|
||||||
|
for (const auto& category : categories) {
|
||||||
|
auto name = category.name;
|
||||||
|
name.replace("-", " ");
|
||||||
|
name.replace("&", "&&");
|
||||||
|
auto checkbox = new QCheckBox(name);
|
||||||
|
auto font = checkbox->font();
|
||||||
|
font.setCapitalization(QFont::Capitalize);
|
||||||
|
checkbox->setFont(font);
|
||||||
|
|
||||||
|
layout->addWidget(checkbox);
|
||||||
|
|
||||||
|
const QString id = category.id;
|
||||||
|
connect(checkbox, &QCheckBox::toggled, this, [this, id](bool checked) {
|
||||||
|
if (checked)
|
||||||
|
m_filter->categoryIds.append(id);
|
||||||
|
else
|
||||||
|
m_filter->categoryIds.removeOne(id);
|
||||||
|
|
||||||
|
m_filter_changed = true;
|
||||||
|
emit filterChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "ModFilterWidget.moc"
|
@ -1,15 +1,52 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
|
#include <QList>
|
||||||
|
#include <QListWidgetItem>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
|
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
|
|
||||||
#include "meta/Index.h"
|
#include "VersionProxyModel.h"
|
||||||
#include "meta/VersionList.h"
|
#include "meta/VersionList.h"
|
||||||
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include "minecraft/MinecraftInstance.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
class MinecraftInstance;
|
class MinecraftInstance;
|
||||||
|
|
||||||
@ -20,59 +57,57 @@ class ModFilterWidget;
|
|||||||
class ModFilterWidget : public QTabWidget {
|
class ModFilterWidget : public QTabWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum VersionButtonID { Strict = 0, Major = 1, All = 2, Between = 3 };
|
|
||||||
|
|
||||||
struct Filter {
|
struct Filter {
|
||||||
std::list<Version> versions;
|
std::list<Version> versions;
|
||||||
|
std::list<ModPlatform::IndexedVersionType> releases;
|
||||||
|
ModPlatform::ModLoaderTypes loaders;
|
||||||
|
QString side;
|
||||||
|
bool hideInstalled;
|
||||||
|
QStringList categoryIds;
|
||||||
|
|
||||||
bool operator==(const Filter& other) const { return versions == other.versions; }
|
bool operator==(const Filter& other) const
|
||||||
|
{
|
||||||
|
return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions &&
|
||||||
|
releases == other.releases && categoryIds == other.categoryIds;
|
||||||
|
}
|
||||||
bool operator!=(const Filter& other) const { return !(*this == other); }
|
bool operator!=(const Filter& other) const { return !(*this == other); }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<Filter> m_filter;
|
static unique_qobject_ptr<ModFilterWidget> create(MinecraftInstance* instance, bool extended, QWidget* parent = nullptr);
|
||||||
|
virtual ~ModFilterWidget();
|
||||||
public:
|
|
||||||
static unique_qobject_ptr<ModFilterWidget> create(Version default_version, QWidget* parent = nullptr);
|
|
||||||
~ModFilterWidget();
|
|
||||||
|
|
||||||
void setInstance(MinecraftInstance* instance);
|
|
||||||
|
|
||||||
/// By default all buttons are enabled
|
|
||||||
void disableVersionButton(VersionButtonID, QString reason = {});
|
|
||||||
|
|
||||||
auto getFilter() -> std::shared_ptr<Filter>;
|
auto getFilter() -> std::shared_ptr<Filter>;
|
||||||
auto changed() const -> bool { return m_last_version_id != m_version_id; }
|
auto changed() const -> bool { return m_filter_changed; }
|
||||||
|
|
||||||
Meta::VersionList::Ptr versionList() { return m_version_list; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ModFilterWidget(Version def, QWidget* parent = nullptr);
|
|
||||||
|
|
||||||
inline auto mcVersionStr() const -> QString
|
|
||||||
{
|
|
||||||
return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : "";
|
|
||||||
}
|
|
||||||
inline auto mcVersion() const -> Version { return { mcVersionStr() }; }
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onVersionFilterChanged(int id);
|
|
||||||
|
|
||||||
public:
|
|
||||||
signals:
|
signals:
|
||||||
void filterChanged();
|
void filterChanged();
|
||||||
void filterUnchanged();
|
|
||||||
|
public slots:
|
||||||
|
void setCategories(const QList<ModPlatform::Category>&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void loadVersionList();
|
||||||
|
void prepareBasicFilter();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onVersionFilterChanged(int);
|
||||||
|
void onVersionFilterTextChanged(const QString& version);
|
||||||
|
void onLoadersFilterChanged();
|
||||||
|
void onSideFilterChanged();
|
||||||
|
void onHideInstalledFilterChanged();
|
||||||
|
void onShowAllVersionsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ModFilterWidget* ui;
|
Ui::ModFilterWidget* ui;
|
||||||
|
|
||||||
MinecraftInstance* m_instance = nullptr;
|
MinecraftInstance* m_instance = nullptr;
|
||||||
|
std::shared_ptr<Filter> m_filter;
|
||||||
/* Version stuff */
|
bool m_filter_changed = false;
|
||||||
QButtonGroup m_mcVersion_buttons;
|
|
||||||
|
|
||||||
Meta::VersionList::Ptr m_version_list;
|
Meta::VersionList::Ptr m_version_list;
|
||||||
|
VersionProxyModel* m_versions_proxy = nullptr;
|
||||||
|
|
||||||
/* Used to tell if the filter was changed since the last getFilter() call */
|
QList<ModPlatform::Category> m_categories;
|
||||||
VersionButtonID m_last_version_id = VersionButtonID::Strict;
|
|
||||||
VersionButtonID m_version_id = VersionButtonID::Strict;
|
|
||||||
};
|
};
|
||||||
|
@ -1,54 +1,219 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>ModFilterWidget</class>
|
<class>ModFilterWidget</class>
|
||||||
<widget class="QTabWidget" name="ModFilterWidget">
|
<widget class="QWidget" name="ModFilterWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>310</width>
|
||||||
<height>300</height>
|
<height>600</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="VersionPage">
|
<property name="minimumSize">
|
||||||
<attribute name="title">
|
<size>
|
||||||
<string>Minecraft versions</string>
|
<width>275</width>
|
||||||
</attribute>
|
<height>0</height>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
</size>
|
||||||
<item row="0" column="0">
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<property name="maximumSize">
|
||||||
<item row="2" column="0">
|
<size>
|
||||||
<widget class="QRadioButton" name="allVersionsButton">
|
<width>310</width>
|
||||||
<property name="text">
|
<height>16777215</height>
|
||||||
<string notr="true">allVersions</string>
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>275</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>308</width>
|
||||||
|
<height>598</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="categoryGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>Categories</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="QRadioButton" name="strictVersionButton">
|
<widget class="QGroupBox" name="loaderGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>Loaders</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="neoForge">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string notr="true">strictVersion</string>
|
<string>NeoForge</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item>
|
||||||
<widget class="QRadioButton" name="majorVersionButton">
|
<widget class="QCheckBox" name="forge">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string notr="true">majorVersion</string>
|
<string>Forge</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="fabric">
|
||||||
|
<property name="text">
|
||||||
|
<string>Fabric</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="quilt">
|
||||||
|
<property name="text">
|
||||||
|
<string>Quilt</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="minecraftVersionGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>Versions</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="showAllVersions">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show all versions</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="CheckComboBox" name="versions"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="version"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="environmentGroup">
|
||||||
|
<property name="title">
|
||||||
|
<string>Environments</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="clientSide">
|
||||||
|
<property name="text">
|
||||||
|
<string>Client</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="serverSide">
|
||||||
|
<property name="text">
|
||||||
|
<string>Server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="hideInstalled">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hide installed items</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>CheckComboBox</class>
|
||||||
|
<extends>QComboBox</extends>
|
||||||
|
<header>ui/widgets/CheckComboBox.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
|
|
||||||
#include "MurmurHash2.h"
|
#include "MurmurHash2.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
namespace Murmur2 {
|
||||||
|
|
||||||
// 'm' and 'r' are mixing constants generated offline.
|
// 'm' and 'r' are mixing constants generated offline.
|
||||||
// They're not really 'magic', they just happen to work well.
|
// They're not really 'magic', they just happen to work well.
|
||||||
const uint32_t m = 0x5bd1e995;
|
const uint32_t m = 0x5bd1e995;
|
||||||
const int r = 24;
|
const int r = 24;
|
||||||
|
|
||||||
uint32_t MurmurHash2(std::ifstream&& file_stream, std::size_t buffer_size, std::function<bool(char)> filter_out)
|
uint32_t hash(Reader* file_stream, std::size_t buffer_size, std::function<bool(char)> filter_out)
|
||||||
{
|
{
|
||||||
auto* buffer = new char[buffer_size];
|
auto* buffer = new char[buffer_size];
|
||||||
char data[4];
|
char data[4];
|
||||||
@ -26,24 +26,21 @@ uint32_t MurmurHash2(std::ifstream&& file_stream, std::size_t buffer_size, std::
|
|||||||
// We need the size without the filtered out characters before actually calculating the hash,
|
// We need the size without the filtered out characters before actually calculating the hash,
|
||||||
// to setup the initial value for the hash.
|
// to setup the initial value for the hash.
|
||||||
do {
|
do {
|
||||||
file_stream.read(buffer, buffer_size);
|
read = file_stream->read(buffer, buffer_size);
|
||||||
read = file_stream.gcount();
|
|
||||||
for (int i = 0; i < read; i++) {
|
for (int i = 0; i < read; i++) {
|
||||||
if (!filter_out(buffer[i]))
|
if (!filter_out(buffer[i]))
|
||||||
size += 1;
|
size += 1;
|
||||||
}
|
}
|
||||||
} while (!file_stream.eof());
|
} while (!file_stream->eof());
|
||||||
|
|
||||||
file_stream.clear();
|
file_stream->goToBeginning();
|
||||||
file_stream.seekg(0, file_stream.beg);
|
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
// This forces a seed of 1.
|
// This forces a seed of 1.
|
||||||
IncrementalHashInfo info{ (uint32_t)1 ^ size, (uint32_t)size };
|
IncrementalHashInfo info{ (uint32_t)1 ^ size, (uint32_t)size };
|
||||||
do {
|
do {
|
||||||
file_stream.read(buffer, buffer_size);
|
read = file_stream->read(buffer, buffer_size);
|
||||||
read = file_stream.gcount();
|
|
||||||
for (int i = 0; i < read; i++) {
|
for (int i = 0; i < read; i++) {
|
||||||
char c = buffer[i];
|
char c = buffer[i];
|
||||||
|
|
||||||
@ -57,14 +54,13 @@ uint32_t MurmurHash2(std::ifstream&& file_stream, std::size_t buffer_size, std::
|
|||||||
if (index == 0)
|
if (index == 0)
|
||||||
FourBytes_MurmurHash2(reinterpret_cast<unsigned char*>(&data), info);
|
FourBytes_MurmurHash2(reinterpret_cast<unsigned char*>(&data), info);
|
||||||
}
|
}
|
||||||
} while (!file_stream.eof());
|
} while (!file_stream->eof());
|
||||||
|
|
||||||
// Do one last bit shuffle in the hash
|
// Do one last bit shuffle in the hash
|
||||||
FourBytes_MurmurHash2(reinterpret_cast<unsigned char*>(&data), info);
|
FourBytes_MurmurHash2(reinterpret_cast<unsigned char*>(&data), info);
|
||||||
|
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
|
||||||
file_stream.close();
|
|
||||||
return info.h;
|
return info.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,4 +105,4 @@ void FourBytes_MurmurHash2(const unsigned char* data, IncrementalHashInfo& prev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
} // namespace Murmur2
|
@ -9,17 +9,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
namespace Murmur2 {
|
||||||
|
|
||||||
#define KiB 1024
|
#define KiB 1024
|
||||||
#define MiB 1024 * KiB
|
#define MiB 1024 * KiB
|
||||||
|
|
||||||
uint32_t MurmurHash2(
|
class Reader {
|
||||||
std::ifstream&& file_stream,
|
public:
|
||||||
|
virtual ~Reader() = default;
|
||||||
|
virtual int read(char* s, int n) = 0;
|
||||||
|
virtual bool eof() = 0;
|
||||||
|
virtual void goToBeginning() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t hash(
|
||||||
|
Reader* file_stream,
|
||||||
std::size_t buffer_size = 4 * MiB,
|
std::size_t buffer_size = 4 * MiB,
|
||||||
std::function<bool(char)> filter_out = [](char) { return false; });
|
std::function<bool(char)> filter_out = [](char) { return false; });
|
||||||
|
|
||||||
@ -29,5 +35,4 @@ struct IncrementalHashInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void FourBytes_MurmurHash2(const unsigned char* data, IncrementalHashInfo& prev);
|
void FourBytes_MurmurHash2(const unsigned char* data, IncrementalHashInfo& prev);
|
||||||
|
} // namespace Murmur2
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
@ -13,8 +13,6 @@
|
|||||||
in {
|
in {
|
||||||
inherit
|
inherit
|
||||||
(ourPackages)
|
(ourPackages)
|
||||||
prismlauncher-qt5-unwrapped
|
|
||||||
prismlauncher-qt5
|
|
||||||
prismlauncher-unwrapped
|
prismlauncher-unwrapped
|
||||||
prismlauncher
|
prismlauncher
|
||||||
;
|
;
|
||||||
@ -25,21 +23,12 @@
|
|||||||
flake = {
|
flake = {
|
||||||
overlays.default = final: prev: let
|
overlays.default = final: prev: let
|
||||||
version = builtins.substring 0 8 self.lastModifiedDate or "dirty";
|
version = builtins.substring 0 8 self.lastModifiedDate or "dirty";
|
||||||
|
in {
|
||||||
# common args for prismlauncher evaluations
|
prismlauncher-unwrapped = prev.qt6Packages.callPackage ./pkg {
|
||||||
unwrappedArgs = {
|
|
||||||
inherit (inputs) libnbtplusplus;
|
inherit (inputs) libnbtplusplus;
|
||||||
inherit ((final.darwin or prev.darwin).apple_sdk.frameworks) Cocoa;
|
inherit ((final.darwin or prev.darwin).apple_sdk.frameworks) Cocoa;
|
||||||
inherit version;
|
inherit version;
|
||||||
};
|
};
|
||||||
in {
|
|
||||||
prismlauncher-qt5-unwrapped = prev.libsForQt5.callPackage ./pkg unwrappedArgs;
|
|
||||||
|
|
||||||
prismlauncher-qt5 = prev.libsForQt5.callPackage ./pkg/wrapper.nix {
|
|
||||||
prismlauncher-unwrapped = final.prismlauncher-qt5-unwrapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
prismlauncher-unwrapped = prev.qt6Packages.callPackage ./pkg unwrappedArgs;
|
|
||||||
|
|
||||||
prismlauncher = prev.qt6Packages.callPackage ./pkg/wrapper.nix {
|
prismlauncher = prev.qt6Packages.callPackage ./pkg/wrapper.nix {
|
||||||
inherit (final) prismlauncher-unwrapped;
|
inherit (final) prismlauncher-unwrapped;
|
||||||
|
Loading…
Reference in New Issue
Block a user