Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into fix_duplicate_mod
This commit is contained in:
commit
ddf1ea014c
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -79,7 +79,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.0"
|
||||
qt_version: "6.7.1"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: windows-2022
|
||||
@ -90,7 +90,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: "win64_msvc2019_arm64"
|
||||
qt_version: "6.7.0"
|
||||
qt_version: "6.7.1"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: macos-12
|
||||
@ -99,7 +99,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ""
|
||||
qt_version: "6.7.0"
|
||||
qt_version: "6.7.1"
|
||||
qt_modules: "qt5compat qtimageformats qtnetworkauth"
|
||||
|
||||
- os: macos-12
|
||||
|
2
.github/workflows/update-flake.yml
vendored
2
.github/workflows/update-flake.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v21
|
||||
- uses: DeterminateSystems/update-flake-lock@v22
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
|
12
flake.lock
generated
12
flake.lock
generated
@ -75,16 +75,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1717774105,
|
||||
"narHash": "sha256-HV97wqUQv9wvptiHCb3Y0/YH0lJ60uZ8FYfEOIzYEqI=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1718276985,
|
||||
"narHash": "sha256-u1fA0DYQYdeG+5kDm1bOoGcHtX0rtC7qs2YA2N1X++I=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d226935fd75012939397c83f6c385e4d6d832288",
|
||||
"rev": "3f84a279f1a6290ce154c5531378acc827836fbb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
};
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-parts = {
|
||||
url = "github:hercules-ci/flake-parts";
|
||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
|
@ -847,24 +847,17 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{
|
||||
m_metacache.reset(new HttpMetaCache("metacache"));
|
||||
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
|
||||
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
|
||||
m_metacache->addBase("versions", QDir("versions").absolutePath());
|
||||
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
||||
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
|
||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||
m_metacache->addBase("general", QDir("cache").absolutePath());
|
||||
m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
|
||||
m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
|
||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
|
||||
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
|
||||
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
|
||||
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
|
||||
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
||||
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
m_metacache->addBase("meta", QDir("meta").absolutePath());
|
||||
m_metacache->Load();
|
||||
qDebug() << "<> Cache initialized.";
|
||||
@ -945,8 +938,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
[[fallthrough]];
|
||||
default: {
|
||||
qDebug() << "Exiting because update lockfile is present";
|
||||
QMetaObject::invokeMethod(
|
||||
this, []() { exit(1); }, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -978,8 +970,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
[[fallthrough]];
|
||||
default: {
|
||||
qDebug() << "Exiting because update lockfile is present";
|
||||
QMetaObject::invokeMethod(
|
||||
this, []() { exit(1); }, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -991,7 +982,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
"\n"
|
||||
"You are now running %1 .\n"
|
||||
"Check the Prism Launcher updater log at: \n"
|
||||
"%1\n"
|
||||
"%2\n"
|
||||
"for details.")
|
||||
.arg(BuildConfig.printableVersionString())
|
||||
.arg(update_log_path);
|
||||
@ -1673,8 +1664,7 @@ QString Application::getJarPath(QString jarFile)
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME),
|
||||
#endif
|
||||
FS::PathCombine(m_rootPath, "jars"),
|
||||
FS::PathCombine(applicationDirPath(), "jars"),
|
||||
FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"),
|
||||
FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging
|
||||
};
|
||||
for (QString p : potentialPaths) {
|
||||
|
@ -29,7 +29,7 @@ class BaseVersion;
|
||||
class BaseInstaller {
|
||||
public:
|
||||
BaseInstaller();
|
||||
virtual ~BaseInstaller(){};
|
||||
virtual ~BaseInstaller() {};
|
||||
bool isApplied(MinecraftInstance* on);
|
||||
|
||||
virtual bool add(MinecraftInstance* to);
|
||||
|
@ -269,13 +269,18 @@ void BaseInstance::setRunning(bool running)
|
||||
|
||||
m_isRunning = running;
|
||||
|
||||
if (!m_settings->get("RecordGameTime").toBool()) {
|
||||
emit runningStatusChanged(running);
|
||||
emit runningStatusChanged(running);
|
||||
}
|
||||
|
||||
void BaseInstance::setMinecraftRunning(bool running)
|
||||
{
|
||||
if (!settings()->get("RecordGameTime").toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (running) {
|
||||
m_timeStarted = QDateTime::currentDateTime();
|
||||
setLastLaunch(m_timeStarted.toMSecsSinceEpoch());
|
||||
} else {
|
||||
QDateTime timeEnded = QDateTime::currentDateTime();
|
||||
|
||||
@ -285,8 +290,6 @@ void BaseInstance::setRunning(bool running)
|
||||
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
emit runningStatusChanged(running);
|
||||
}
|
||||
|
||||
int64_t BaseInstance::totalTimePlayed() const
|
||||
|
@ -104,6 +104,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
|
||||
/// be unique.
|
||||
virtual QString id() const;
|
||||
|
||||
void setMinecraftRunning(bool running);
|
||||
void setRunning(bool running);
|
||||
bool isRunning() const;
|
||||
int64_t totalTimePlayed() const;
|
||||
|
@ -163,6 +163,8 @@ set(LAUNCH_SOURCES
|
||||
launch/steps/Update.h
|
||||
launch/steps/QuitAfterGameStop.cpp
|
||||
launch/steps/QuitAfterGameStop.h
|
||||
launch/steps/PrintServers.cpp
|
||||
launch/steps/PrintServers.h
|
||||
launch/LaunchStep.cpp
|
||||
launch/LaunchStep.h
|
||||
launch/LaunchTask.cpp
|
||||
@ -1034,6 +1036,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/skins/SkinManageDialog.h
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/CheckComboBox.cpp
|
||||
ui/widgets/CheckComboBox.h
|
||||
ui/widgets/Common.cpp
|
||||
ui/widgets/Common.h
|
||||
ui/widgets/CustomCommands.cpp
|
||||
|
@ -815,20 +815,11 @@ QString NormalizePath(QString path)
|
||||
}
|
||||
}
|
||||
|
||||
QString removeDuplicates(QString a)
|
||||
{
|
||||
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_WIN_CHARS = "<>:\"|?*\r\n";
|
||||
static const QString BAD_NTFS_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)
|
||||
{
|
||||
@ -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
|
||||
switch (statFS(path).fsType) {
|
||||
case FilesystemType::FAT:
|
||||
invalidChars += BAD_FAT_CHARS;
|
||||
break;
|
||||
case FilesystemType::FAT: // similar to NTFS
|
||||
/* fallthrough */
|
||||
case FilesystemType::NTFS:
|
||||
/* fallthrough */
|
||||
case FilesystemType::REFS: // similar to NTFS(should be available only on windows)
|
||||
|
@ -22,7 +22,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||
public:
|
||||
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
||||
|
||||
virtual ~InstancePageProvider(){};
|
||||
virtual ~InstancePageProvider() {};
|
||||
virtual QList<BasePage*> getPages() override
|
||||
{
|
||||
QList<BasePage*> values;
|
||||
|
@ -24,7 +24,7 @@ class TestCheck : public QObject {
|
||||
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
||||
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
||||
{}
|
||||
virtual ~TestCheck(){};
|
||||
virtual ~TestCheck() {};
|
||||
|
||||
void run();
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "LaunchController.h"
|
||||
#include "Application.h"
|
||||
#include "launch/steps/PrintServers.h"
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
|
||||
@ -193,7 +194,8 @@ void LaunchController::login()
|
||||
bool tryagain = true;
|
||||
unsigned int tries = 0;
|
||||
|
||||
if (m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) {
|
||||
if ((m_accountToUse->accountType() != AccountType::Offline && m_accountToUse->accountState() == AccountState::Offline) ||
|
||||
m_accountToUse->shouldRefresh()) {
|
||||
// Force account refresh on the account used to launch the instance updating the AccountState
|
||||
// only on first try and if it is not meant to be offline
|
||||
auto accounts = APPLICATION->accounts();
|
||||
@ -345,25 +347,8 @@ void LaunchController::launchInstance()
|
||||
|
||||
// Prepend Server Status
|
||||
QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
||||
QString resolved_servers = "";
|
||||
QHostInfo host_info;
|
||||
|
||||
for (QString server : servers) {
|
||||
host_info = QHostInfo::fromName(server);
|
||||
resolved_servers = resolved_servers + server + " resolves to:\n [";
|
||||
if (!host_info.addresses().isEmpty()) {
|
||||
for (QHostAddress address : host_info.addresses()) {
|
||||
resolved_servers = resolved_servers + address.toString();
|
||||
if (!host_info.addresses().endsWith(address)) {
|
||||
resolved_servers = resolved_servers + ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolved_servers = resolved_servers + "N/A";
|
||||
}
|
||||
resolved_servers = resolved_servers + "]\n\n";
|
||||
}
|
||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||
m_launcher->prependStep(makeShared<PrintServers>(m_launcher.get(), servers));
|
||||
} else {
|
||||
online_mode = m_demo ? "demo" : "offline";
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class LaunchController : public Task {
|
||||
void executeTask() override;
|
||||
|
||||
LaunchController(QObject* parent = nullptr);
|
||||
virtual ~LaunchController(){};
|
||||
virtual ~LaunchController() {};
|
||||
|
||||
void setInstance(InstancePtr instance) { m_instance = instance; }
|
||||
|
||||
|
@ -176,7 +176,7 @@ class ExportToZipTask : public Task {
|
||||
QString destinationPrefix = "",
|
||||
bool followSymlinks = false,
|
||||
bool utf8Enabled = false)
|
||||
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks, utf8Enabled){};
|
||||
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks, utf8Enabled) {};
|
||||
|
||||
virtual ~ExportToZipTask() = default;
|
||||
|
||||
@ -213,7 +213,7 @@ class ExtractZipTask : public Task {
|
||||
{}
|
||||
virtual ~ExtractZipTask() = default;
|
||||
|
||||
typedef std::optional<QString> ZipResult;
|
||||
using ZipResult = std::optional<QString>;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
@ -46,7 +46,7 @@ class NullInstance : public BaseInstance {
|
||||
{
|
||||
setVersionBroken(true);
|
||||
}
|
||||
virtual ~NullInstance(){};
|
||||
virtual ~NullInstance() {};
|
||||
void saveNow() override {}
|
||||
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
|
||||
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
|
||||
|
@ -14,7 +14,7 @@ class VersionProxyModel : public QAbstractProxyModel {
|
||||
|
||||
public:
|
||||
VersionProxyModel(QObject* parent = 0);
|
||||
virtual ~VersionProxyModel(){};
|
||||
virtual ~VersionProxyModel() {};
|
||||
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
@ -52,7 +52,7 @@ class IconList : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit IconList(const QStringList& builtinPaths, QString path, QObject* parent = 0);
|
||||
virtual ~IconList(){};
|
||||
virtual ~IconList() {};
|
||||
|
||||
QIcon getIcon(const QString& key) const;
|
||||
int getIconIndex(const QString& key) const;
|
||||
|
@ -26,8 +26,8 @@ using JavaCheckerJobPtr = shared_qobject_ptr<JavaCheckerJob>;
|
||||
class JavaCheckerJob : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name){};
|
||||
virtual ~JavaCheckerJob(){};
|
||||
explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {};
|
||||
virtual ~JavaCheckerJob() {};
|
||||
|
||||
bool addJavaCheckerAction(JavaCheckerPtr base)
|
||||
{
|
||||
|
@ -79,11 +79,9 @@ QProcessEnvironment CleanEnviroment()
|
||||
|
||||
QStringList stripped = {
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
"LD_LIBRARY_PATH",
|
||||
"LD_PRELOAD",
|
||||
"LD_LIBRARY_PATH", "LD_PRELOAD",
|
||||
#endif
|
||||
"QT_PLUGIN_PATH",
|
||||
"QT_FONTPATH"
|
||||
"QT_PLUGIN_PATH", "QT_FONTPATH"
|
||||
};
|
||||
for (auto key : rawenv.keys()) {
|
||||
auto value = rawenv.value(key);
|
||||
@ -376,7 +374,19 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
auto home = qEnvironmentVariable("HOME");
|
||||
|
||||
// javas downloaded by sdkman
|
||||
javas.append(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||
QDir sdkmanDir(FS::PathCombine(home, ".sdkman/candidates/java"));
|
||||
QStringList sdkmanJavas = sdkmanDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (const QString& java, sdkmanJavas) {
|
||||
javas.append(sdkmanDir.absolutePath() + "/" + java + "/bin/java");
|
||||
}
|
||||
|
||||
// java in user library folder (like from intellij downloads)
|
||||
QDir userLibraryJVMDir(FS::PathCombine(home, "Library/Java/JavaVirtualMachines/"));
|
||||
QStringList userLibraryJVMJavas = userLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
foreach (const QString& java, userLibraryJVMJavas) {
|
||||
javas.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java");
|
||||
javas.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java");
|
||||
}
|
||||
|
||||
javas.append(getMinecraftJavaBundle());
|
||||
javas = addJavasFromEnv(javas);
|
||||
@ -415,6 +425,10 @@ QList<QString> JavaUtils::FindJavaPaths()
|
||||
scanJavaDirs("/usr/lib/jvm");
|
||||
scanJavaDirs("/usr/lib64/jvm");
|
||||
scanJavaDirs("/usr/lib32/jvm");
|
||||
// Gentoo's locations for openjdk and openjdk-bin respectively
|
||||
scanJavaDir("/usr/lib64");
|
||||
scanJavaDir("/usr/lib");
|
||||
scanJavaDir("/opt");
|
||||
// javas stored in Prism Launcher's folder
|
||||
scanJavaDirs("java");
|
||||
// manually installed JDKs in /opt
|
||||
|
@ -25,7 +25,7 @@ class LaunchStep : public Task {
|
||||
Q_OBJECT
|
||||
public: /* methods */
|
||||
explicit LaunchStep(LaunchTask* parent) : Task(nullptr), m_parent(parent) { bind(parent); };
|
||||
virtual ~LaunchStep(){};
|
||||
virtual ~LaunchStep() {};
|
||||
|
||||
private: /* methods */
|
||||
void bind(LaunchTask* parent);
|
||||
@ -37,9 +37,9 @@ class LaunchStep : public Task {
|
||||
void progressReportingRequest();
|
||||
|
||||
public slots:
|
||||
virtual void proceed(){};
|
||||
virtual void proceed() {};
|
||||
// called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
|
||||
virtual void finalize(){};
|
||||
virtual void finalize() {};
|
||||
|
||||
protected: /* data */
|
||||
LaunchTask* m_parent;
|
||||
|
@ -55,7 +55,7 @@ class LaunchTask : public Task {
|
||||
|
||||
public: /* methods */
|
||||
static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
|
||||
virtual ~LaunchTask(){};
|
||||
virtual ~LaunchTask() {};
|
||||
|
||||
void appendStep(shared_qobject_ptr<LaunchStep> step);
|
||||
void prependStep(shared_qobject_ptr<LaunchStep> step);
|
||||
|
@ -22,8 +22,8 @@
|
||||
class CheckJava : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CheckJava(LaunchTask* parent) : LaunchStep(parent){};
|
||||
virtual ~CheckJava(){};
|
||||
explicit CheckJava(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~CheckJava() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
@ -25,7 +25,7 @@ class LookupServerAddress : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LookupServerAddress(LaunchTask* parent);
|
||||
virtual ~LookupServerAddress(){};
|
||||
virtual ~LookupServerAddress() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
|
@ -22,7 +22,7 @@ class PostLaunchCommand : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PostLaunchCommand(LaunchTask* parent);
|
||||
virtual ~PostLaunchCommand(){};
|
||||
virtual ~PostLaunchCommand() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
|
@ -22,7 +22,7 @@ class PreLaunchCommand : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PreLaunchCommand(LaunchTask* parent);
|
||||
virtual ~PreLaunchCommand(){};
|
||||
virtual ~PreLaunchCommand() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
|
65
launcher/launch/steps/PrintServers.cpp
Normal file
65
launcher/launch/steps/PrintServers.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2024 Leia uwu <leia@tutamail.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 "PrintServers.h"
|
||||
#include "QHostInfo"
|
||||
|
||||
PrintServers::PrintServers(LaunchTask* parent, const QStringList& servers) : LaunchStep(parent)
|
||||
{
|
||||
m_servers = servers;
|
||||
}
|
||||
|
||||
void PrintServers::executeTask()
|
||||
{
|
||||
for (QString server : m_servers) {
|
||||
QHostInfo::lookupHost(server, this, &PrintServers::resolveServer);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintServers::resolveServer(const QHostInfo& host_info)
|
||||
{
|
||||
QString server = host_info.hostName();
|
||||
QString addresses = server + " resolves to:\n [";
|
||||
|
||||
if (!host_info.addresses().isEmpty()) {
|
||||
for (QHostAddress address : host_info.addresses()) {
|
||||
addresses += address.toString();
|
||||
if (!host_info.addresses().endsWith(address)) {
|
||||
addresses += ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addresses += "N/A";
|
||||
}
|
||||
addresses += "]\n\n";
|
||||
|
||||
m_server_to_address.insert(server, addresses);
|
||||
|
||||
// print server info in order once all servers are resolved
|
||||
if (m_server_to_address.size() >= m_servers.size()) {
|
||||
for (QString serv : m_servers) {
|
||||
emit logLine(m_server_to_address.value(serv), MessageLevel::Launcher);
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
bool PrintServers::canAbort() const
|
||||
{
|
||||
return true;
|
||||
}
|
37
launcher/launch/steps/PrintServers.h
Normal file
37
launcher/launch/steps/PrintServers.h
Normal file
@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2024 Leia uwu <leia@tutamail.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 <LoggedProcess.h>
|
||||
#include <java/JavaChecker.h>
|
||||
#include <launch/LaunchStep.h>
|
||||
#include <QHostInfo>
|
||||
|
||||
class PrintServers : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PrintServers(LaunchTask* parent, const QStringList& servers);
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const;
|
||||
|
||||
private:
|
||||
void resolveServer(const QHostInfo& host_info);
|
||||
QMap<QString, QString> m_server_to_address;
|
||||
QStringList m_servers;
|
||||
};
|
@ -23,8 +23,8 @@
|
||||
class QuitAfterGameStop : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QuitAfterGameStop(LaunchTask* parent) : LaunchStep(parent){};
|
||||
virtual ~QuitAfterGameStop(){};
|
||||
explicit QuitAfterGameStop(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~QuitAfterGameStop() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
@ -28,7 +28,7 @@ class TextPrint : public LaunchStep {
|
||||
public:
|
||||
explicit TextPrint(LaunchTask* parent, const QStringList& lines, MessageLevel::Enum level);
|
||||
explicit TextPrint(LaunchTask* parent, const QString& line, MessageLevel::Enum level);
|
||||
virtual ~TextPrint(){};
|
||||
virtual ~TextPrint() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const;
|
||||
|
@ -25,8 +25,8 @@
|
||||
class Update : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Update(LaunchTask* parent, Net::Mode mode) : LaunchStep(parent), m_mode(mode){};
|
||||
virtual ~Update(){};
|
||||
explicit Update(LaunchTask* parent, Net::Mode mode) : LaunchStep(parent), m_mode(mode) {};
|
||||
virtual ~Update() {};
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
|
@ -26,8 +26,8 @@
|
||||
|
||||
class ParsingValidator : public Net::Validator {
|
||||
public: /* con/des */
|
||||
ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity){};
|
||||
virtual ~ParsingValidator(){};
|
||||
ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity) {};
|
||||
virtual ~ParsingValidator() {};
|
||||
|
||||
public: /* methods */
|
||||
bool init(QNetworkRequest&) override { return true; }
|
||||
|
@ -56,7 +56,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
|
||||
virtual ~MinecraftInstance(){};
|
||||
virtual ~MinecraftInstance() {};
|
||||
virtual void saveNow() override;
|
||||
|
||||
void loadSpecificSettings() override;
|
||||
|
@ -31,7 +31,7 @@ class MinecraftLoadAndCheck : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftLoadAndCheck(MinecraftInstance* inst, QObject* parent = 0);
|
||||
virtual ~MinecraftLoadAndCheck(){};
|
||||
virtual ~MinecraftLoadAndCheck() {};
|
||||
void executeTask() override;
|
||||
|
||||
private slots:
|
||||
|
@ -33,7 +33,7 @@ class MinecraftUpdate : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftUpdate(MinecraftInstance* inst, QObject* parent = 0);
|
||||
virtual ~MinecraftUpdate(){};
|
||||
virtual ~MinecraftUpdate() {};
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
|
@ -27,7 +27,7 @@ class AuthStep : public QObject {
|
||||
public:
|
||||
using Ptr = shared_qobject_ptr<AuthStep>;
|
||||
|
||||
explicit AuthStep(AccountData* data) : QObject(nullptr), m_data(data){};
|
||||
explicit AuthStep(AccountData* data) : QObject(nullptr), m_data(data) {};
|
||||
virtual ~AuthStep() noexcept = default;
|
||||
|
||||
virtual QString describe() = 0;
|
||||
|
@ -22,7 +22,7 @@ class ClaimAccount : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ClaimAccount(LaunchTask* parent, AuthSessionPtr session);
|
||||
virtual ~ClaimAccount(){};
|
||||
virtual ~ClaimAccount() {};
|
||||
|
||||
void executeTask() override;
|
||||
void finalize() override;
|
||||
|
@ -24,7 +24,7 @@ class CreateGameFolders : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CreateGameFolders(LaunchTask* parent);
|
||||
virtual ~CreateGameFolders(){};
|
||||
virtual ~CreateGameFolders() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
@ -21,8 +21,8 @@
|
||||
class ExtractNatives : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ExtractNatives(LaunchTask* parent) : LaunchStep(parent){};
|
||||
virtual ~ExtractNatives(){};
|
||||
explicit ExtractNatives(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~ExtractNatives() {};
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override { return false; }
|
||||
|
@ -178,6 +178,7 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state)
|
||||
APPLICATION->showMainWindow();
|
||||
|
||||
m_parent->setPid(-1);
|
||||
m_parent->instance()->setMinecraftRunning(false);
|
||||
// if the exit code wasn't 0, report this as a crash
|
||||
auto exitCode = m_process.exitCode();
|
||||
if (exitCode != 0) {
|
||||
@ -193,7 +194,6 @@ void LauncherPartLaunch::on_state(LoggedProcess::State state)
|
||||
case LoggedProcess::Running:
|
||||
emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher);
|
||||
m_parent->setPid(m_process.processId());
|
||||
m_parent->instance()->setLastLaunch();
|
||||
// send the launch script to the launcher part
|
||||
m_process.write(m_launchScript.toUtf8());
|
||||
|
||||
@ -213,6 +213,7 @@ void LauncherPartLaunch::setWorkingDirectory(const QString& wd)
|
||||
void LauncherPartLaunch::proceed()
|
||||
{
|
||||
if (mayProceed) {
|
||||
m_parent->instance()->setMinecraftRunning(true);
|
||||
QString launchString("launch\n");
|
||||
m_process.write(launchString.toUtf8());
|
||||
mayProceed = false;
|
||||
|
@ -25,7 +25,7 @@ class LauncherPartLaunch : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LauncherPartLaunch(LaunchTask* parent);
|
||||
virtual ~LauncherPartLaunch(){};
|
||||
virtual ~LauncherPartLaunch() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool abort();
|
||||
|
@ -21,8 +21,8 @@
|
||||
class ModMinecraftJar : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModMinecraftJar(LaunchTask* parent) : LaunchStep(parent){};
|
||||
virtual ~ModMinecraftJar(){};
|
||||
explicit ModMinecraftJar(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~ModMinecraftJar() {};
|
||||
|
||||
virtual void executeTask() override;
|
||||
virtual bool canAbort() const override { return false; }
|
||||
|
@ -25,8 +25,8 @@ class PrintInstanceInfo : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
: LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin){};
|
||||
virtual ~PrintInstanceInfo(){};
|
||||
: LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {};
|
||||
virtual ~PrintInstanceInfo() {};
|
||||
|
||||
virtual void executeTask();
|
||||
virtual bool canAbort() const { return false; }
|
||||
|
@ -21,8 +21,8 @@
|
||||
class ReconstructAssets : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ReconstructAssets(LaunchTask* parent) : LaunchStep(parent){};
|
||||
virtual ~ReconstructAssets(){};
|
||||
explicit ReconstructAssets(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~ReconstructAssets() {};
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override { return false; }
|
||||
|
@ -21,8 +21,8 @@
|
||||
class ScanModFolders : public LaunchStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScanModFolders(LaunchTask* parent) : LaunchStep(parent){};
|
||||
virtual ~ScanModFolders(){};
|
||||
explicit ScanModFolders(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
virtual ~ScanModFolders() {};
|
||||
|
||||
virtual void executeTask() override;
|
||||
virtual bool canAbort() const override { return false; }
|
||||
|
@ -42,7 +42,7 @@ class VerifyJavaInstall : public LaunchStep {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VerifyJavaInstall(LaunchTask* parent) : LaunchStep(parent){};
|
||||
explicit VerifyJavaInstall(LaunchTask* parent) : LaunchStep(parent) {};
|
||||
~VerifyJavaInstall() override = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
@ -28,15 +28,52 @@
|
||||
#include "Version.h"
|
||||
|
||||
// Values taken from:
|
||||
// https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#%22pack_format%22
|
||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {
|
||||
{ 4, { Version("1.13"), Version("1.14.4") } }, { 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") } },
|
||||
{ 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } },
|
||||
{ 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") } },
|
||||
};
|
||||
// https://minecraft.wiki/w/Pack_format#List_of_data_pack_formats
|
||||
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { { 4, { Version("1.13"), Version("1.14.4") } },
|
||||
{ 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") } },
|
||||
{ 8, { Version("1.18"), Version("1.18.1") } },
|
||||
{ 9, { Version("1.18.2"), Version("1.18.2") } },
|
||||
{ 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)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* 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, 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
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
* 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
|
||||
* 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/Resource.h"
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
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: {
|
||||
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;
|
||||
}
|
||||
@ -222,7 +248,35 @@ void Mod::finishResolvingWithDetails(ModDetails&& details)
|
||||
auto Mod::provider() const -> std::optional<QString>
|
||||
{
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -47,6 +48,7 @@
|
||||
|
||||
#include "ModDetails.h"
|
||||
#include "Resource.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class Mod : public Resource {
|
||||
Q_OBJECT
|
||||
@ -70,6 +72,10 @@ class Mod : public Resource {
|
||||
auto licenses() const -> const QList<ModLicense>&;
|
||||
auto issueTracker() 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*/
|
||||
QString iconPath() const { return m_local_details.icon_file; }
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -47,12 +48,11 @@
|
||||
#include <QThreadPool>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "StringUtils.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "minecraft/mod/Resource.h"
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
@ -64,14 +64,18 @@
|
||||
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)
|
||||
{
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size" });
|
||||
m_column_names_translated =
|
||||
QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size") });
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION,
|
||||
SortType::DATE, SortType::PROVIDER, SortType::SIZE };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size", "Side", "Loaders",
|
||||
"Minecraft Versions", "Release Type" });
|
||||
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,
|
||||
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,
|
||||
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
|
||||
@ -109,6 +113,26 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
|
||||
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:
|
||||
return m_resources[row]->sizeStr();
|
||||
default:
|
||||
@ -165,6 +189,10 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio
|
||||
case DateColumn:
|
||||
case ProviderColumn:
|
||||
case ImageColumn:
|
||||
case SideColumn:
|
||||
case LoadersColumn:
|
||||
case McVersionsColumn:
|
||||
case ReleaseTypeColumn:
|
||||
case SizeColumn:
|
||||
return columnNames().at(section);
|
||||
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).");
|
||||
case ProviderColumn:
|
||||
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:
|
||||
return tr("The size of the mod.");
|
||||
default:
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -61,7 +62,20 @@ class QFileSystemWatcher;
|
||||
class ModFolderModel : public ResourceFolderModel {
|
||||
Q_OBJECT
|
||||
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 };
|
||||
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
|
||||
|
||||
#include <QDateTime>
|
||||
@ -15,7 +50,7 @@ enum class ResourceType {
|
||||
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 };
|
||||
|
||||
|
@ -279,8 +279,7 @@ void ResourceFolderModel::resolveResource(Resource* res)
|
||||
|
||||
connect(
|
||||
task.get(), &Task::succeeded, this, [=] { onParseSucceeded(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
||||
connect(
|
||||
task.get(), &Task::failed, this, [=] { onParseFailed(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
||||
connect(task.get(), &Task::failed, this, [=] { onParseFailed(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection);
|
||||
connect(
|
||||
task.get(), &Task::finished, this,
|
||||
[=] {
|
||||
@ -536,6 +535,10 @@ void ResourceFolderModel::saveColumns(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 setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(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,
|
||||
QHeaderView::Interactive };
|
||||
QList<bool> m_columnsHideable = { false, false, true, true };
|
||||
QList<bool> m_columnsHiddenByDefault = { false, false, false, false };
|
||||
|
||||
QDir m_dir;
|
||||
BaseInstance* m_instance;
|
||||
|
@ -11,15 +11,24 @@
|
||||
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
||||
|
||||
// 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 = {
|
||||
{ 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") } },
|
||||
{ 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") } }, { 8, { Version("1.18"), Version("1.18.2") } },
|
||||
{ 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") } },
|
||||
{ 14, { Version("1.20"), Version("1.20") } }
|
||||
{ 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") } },
|
||||
{ 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") } }, { 8, { Version("1.18"), Version("1.18.2") } },
|
||||
{ 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") } },
|
||||
{ 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)
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include <QStyle>
|
||||
|
||||
#include "Application.h"
|
||||
#include "StringUtils.h"
|
||||
#include "Version.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,
|
||||
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
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
|
||||
|
@ -37,7 +37,6 @@
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include "TexturePackFolderModel.h"
|
||||
|
||||
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <memory>
|
||||
#include "Json.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
@ -44,6 +45,14 @@ static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
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,
|
||||
BaseInstance* instance,
|
||||
ModFolderModel* folder,
|
||||
@ -68,9 +77,10 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
|
||||
void GetModDependenciesTask::prepare()
|
||||
{
|
||||
for (auto sel : m_selected) {
|
||||
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
||||
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
||||
}
|
||||
if (checkDependencies(sel, m_version, m_loaderType))
|
||||
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
||||
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ class FMLLibrariesTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FMLLibrariesTask(MinecraftInstance* inst);
|
||||
virtual ~FMLLibrariesTask(){};
|
||||
virtual ~FMLLibrariesTask() {};
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -7,7 +7,7 @@ class FoldersTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FoldersTask(MinecraftInstance* inst);
|
||||
virtual ~FoldersTask(){};
|
||||
virtual ~FoldersTask() {};
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -7,7 +7,7 @@ class LibrariesTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LibrariesTask(MinecraftInstance* inst);
|
||||
virtual ~LibrariesTask(){};
|
||||
virtual ~LibrariesTask() {};
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
@ -17,7 +17,7 @@ class CheckUpdateTask : public Task {
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){};
|
||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder) {};
|
||||
|
||||
struct UpdatableMod {
|
||||
QString name;
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
static ModrinthAPI modrinth_api;
|
||||
static FlameAPI flame_api;
|
||||
|
||||
@ -162,10 +160,10 @@ void EnsureMetadataTask::executeTask()
|
||||
});
|
||||
|
||||
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())
|
||||
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;
|
||||
version_task->start();
|
||||
@ -215,7 +213,7 @@ void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove)
|
||||
|
||||
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 ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* 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);
|
||||
}
|
||||
|
||||
auto ProviderCapabilities::name(ResourceProvider p) -> const char*
|
||||
const char* ProviderCapabilities::name(ResourceProvider p)
|
||||
{
|
||||
switch (p) {
|
||||
case ResourceProvider::MODRINTH:
|
||||
@ -68,7 +69,8 @@ auto ProviderCapabilities::name(ResourceProvider p) -> const char*
|
||||
}
|
||||
return {};
|
||||
}
|
||||
auto ProviderCapabilities::readableName(ResourceProvider p) -> QString
|
||||
|
||||
QString ProviderCapabilities::readableName(ResourceProvider p)
|
||||
{
|
||||
switch (p) {
|
||||
case ResourceProvider::MODRINTH:
|
||||
@ -78,7 +80,8 @@ auto ProviderCapabilities::readableName(ResourceProvider p) -> QString
|
||||
}
|
||||
return {};
|
||||
}
|
||||
auto ProviderCapabilities::hashType(ResourceProvider p) -> QStringList
|
||||
|
||||
QStringList ProviderCapabilities::hashType(ResourceProvider p)
|
||||
{
|
||||
switch (p) {
|
||||
case ResourceProvider::MODRINTH:
|
||||
@ -90,34 +93,13 @@ auto ProviderCapabilities::hashType(ResourceProvider p) -> QStringList
|
||||
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)
|
||||
{
|
||||
return ((provider == ModPlatform::ResourceProvider::FLAME) ? "https://www.curseforge.com/projects/" : "https://modrinth.com/mod/") +
|
||||
projectID.toString();
|
||||
}
|
||||
|
||||
auto getModLoaderString(ModLoaderType type) -> const QString
|
||||
auto getModLoaderAsString(ModLoaderType type) -> const QString
|
||||
{
|
||||
switch (type) {
|
||||
case NeoForge:
|
||||
@ -138,4 +120,21 @@ auto getModLoaderString(ModLoaderType type) -> const QString
|
||||
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
|
||||
|
@ -40,13 +40,11 @@ enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
|
||||
|
||||
enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN };
|
||||
|
||||
class ProviderCapabilities {
|
||||
public:
|
||||
auto name(ResourceProvider) -> const char*;
|
||||
auto readableName(ResourceProvider) -> QString;
|
||||
auto hashType(ResourceProvider) -> QStringList;
|
||||
auto hash(ResourceProvider, QIODevice*, QString type = "") -> QString;
|
||||
};
|
||||
namespace ProviderCapabilities {
|
||||
const char* name(ResourceProvider);
|
||||
QString readableName(ResourceProvider);
|
||||
QStringList hashType(ResourceProvider);
|
||||
}; // namespace ProviderCapabilities
|
||||
|
||||
struct ModpackAuthor {
|
||||
QString name;
|
||||
@ -109,6 +107,7 @@ struct IndexedVersion {
|
||||
bool is_preferred = true;
|
||||
QString changelog;
|
||||
QList<Dependency> dependencies;
|
||||
QString side; // this is for flame API
|
||||
|
||||
// For internal use, not provided by APIs
|
||||
bool is_currently_selected = false;
|
||||
@ -183,7 +182,8 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
||||
|
||||
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
|
||||
{
|
||||
@ -191,6 +191,11 @@ constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept
|
||||
return x && !(x & (x - 1));
|
||||
}
|
||||
|
||||
struct Category {
|
||||
QString name;
|
||||
QString id;
|
||||
};
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
||||
|
@ -4,6 +4,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* 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<ModPlatform::ModLoaderTypes> loaders;
|
||||
std::optional<std::list<Version> > versions;
|
||||
std::optional<QString> side;
|
||||
std::optional<QStringList> categoryIds;
|
||||
};
|
||||
struct SearchCallbacks {
|
||||
std::function<void(QJsonDocument&)> on_succeed;
|
||||
|
@ -9,7 +9,7 @@ class FileResolvingTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileResolvingTask(const shared_qobject_ptr<QNetworkAccessManager>& network, Flame::Manifest& toProcess);
|
||||
virtual ~FileResolvingTask(){};
|
||||
virtual ~FileResolvingTask() {};
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
|
@ -3,10 +3,12 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "FlameAPI.h"
|
||||
#include <memory>
|
||||
#include "FlameModIndex.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ApiUpload.h"
|
||||
#include "net/NetJob.h"
|
||||
@ -220,3 +222,42 @@ QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
|
||||
{ 7, "Category", QObject::tr("Sort by Category") },
|
||||
{ 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
|
||||
|
||||
#include <QList>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#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 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;
|
||||
|
||||
static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
|
||||
@ -96,6 +100,9 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
get_arguments.append("sortOrder=desc");
|
||||
if (args.loaders.has_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);
|
||||
|
||||
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
|
||||
static FlameAPI api;
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
@ -21,6 +20,9 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
QJsonObject logo = Json::ensureObject(obj, "logo");
|
||||
pack.logoName = Json::ensureString(logo, "title");
|
||||
pack.logoUrl = Json::ensureString(logo, "thumbnailUrl");
|
||||
if (pack.logoUrl.isEmpty()) {
|
||||
pack.logoUrl = Json::ensureString(logo, "url");
|
||||
}
|
||||
|
||||
auto authors = Json::ensureArray(obj, "authors");
|
||||
for (auto authorIter : authors) {
|
||||
@ -80,10 +82,6 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
const BaseInstance* inst)
|
||||
{
|
||||
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) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
@ -91,8 +89,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
if (!file.addonId.isValid())
|
||||
file.addonId = pack.addonId;
|
||||
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
|
||||
@ -118,19 +115,25 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
||||
|
||||
if (str.contains('.'))
|
||||
file.mcVersion.append(str);
|
||||
auto loader = str.toLower();
|
||||
if (loader == "neoforge")
|
||||
|
||||
if (auto loader = str.toLower(); loader == "neoforge")
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
if (loader == "forge")
|
||||
else if (loader == "forge")
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
if (loader == "cauldron")
|
||||
else if (loader == "cauldron")
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
if (loader == "liteloader")
|
||||
else if (loader == "liteloader")
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
if (loader == "fabric")
|
||||
else if (loader == "fabric")
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
if (loader == "quilt")
|
||||
else if (loader == "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");
|
||||
@ -160,7 +163,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
||||
auto hash_list = Json::ensureArray(obj, "hashes");
|
||||
for (auto h : hash_list) {
|
||||
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"));
|
||||
if (hash_types.contains(hash_algo)) {
|
||||
file.hash = Json::requireString(hash_entry, "value");
|
||||
|
@ -116,7 +116,7 @@ void FlamePackExportTask::collectHashes()
|
||||
|
||||
if (relative.startsWith("resourcepacks/") &&
|
||||
(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) {
|
||||
if (m_state == Task::State::Running) {
|
||||
pendingHashes.insert(hash, { relative, file.absoluteFilePath(), relative.endsWith(".zip") });
|
||||
@ -140,7 +140,7 @@ void FlamePackExportTask::collectHashes()
|
||||
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) {
|
||||
if (m_state == Task::State::Running) {
|
||||
pendingHashes.insert(hash, { mod->name(), mod->fileinfo().absoluteFilePath(), mod->enabled(), true });
|
||||
|
@ -1,24 +1,22 @@
|
||||
#include "HashUtils.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "StringUtils.h"
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
#include <MurmurHash2.h>
|
||||
|
||||
namespace Hashing {
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider)
|
||||
{
|
||||
switch (provider) {
|
||||
case ModPlatform::ResourceProvider::MODRINTH:
|
||||
return createModrinthHasher(file_path);
|
||||
return makeShared<Hasher>(file_path,
|
||||
ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first());
|
||||
case ModPlatform::ResourceProvider::FLAME:
|
||||
return createFlameHasher(file_path);
|
||||
return makeShared<Hasher>(file_path, Algorithm::Murmur2);
|
||||
default:
|
||||
qCritical() << "[Hashing]"
|
||||
<< "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);
|
||||
hasher->useHashType(type);
|
||||
return hasher;
|
||||
}
|
||||
|
||||
void ModrinthHasher::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;
|
||||
if (!device->isOpen() && !device->open(QFile::ReadOnly))
|
||||
return "";
|
||||
QCryptographicHash::Algorithm alg = QCryptographicHash::Sha1;
|
||||
switch (type) {
|
||||
case Algorithm::Md4:
|
||||
alg = QCryptographicHash::Algorithm::Md4;
|
||||
break;
|
||||
case Algorithm::Md5:
|
||||
alg = QCryptographicHash::Algorithm::Md5;
|
||||
break;
|
||||
case Algorithm::Sha1:
|
||||
alg = QCryptographicHash::Algorithm::Sha1;
|
||||
break;
|
||||
case Algorithm::Sha256:
|
||||
alg = QCryptographicHash::Algorithm::Sha256;
|
||||
break;
|
||||
case Algorithm::Sha512:
|
||||
alg = QCryptographicHash::Algorithm::Sha512;
|
||||
break;
|
||||
case Algorithm::Murmur2: { // CF-specific
|
||||
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 "";
|
||||
}
|
||||
|
||||
auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||
m_hash = ProviderCaps.hash(ModPlatform::ResourceProvider::MODRINTH, &file, hash_type);
|
||||
QCryptographicHash hash(alg);
|
||||
if (!hash.addData(device))
|
||||
qCritical() << "Failed to read JAR to create hash!";
|
||||
|
||||
file.close();
|
||||
|
||||
if (m_hash.isEmpty()) {
|
||||
emitFailed("Empty hash!");
|
||||
} else {
|
||||
emitSucceeded();
|
||||
emit resultsReady(m_hash);
|
||||
}
|
||||
Q_ASSERT(hash.result().length() == hash.hashLength(alg));
|
||||
auto result = hash.result().toHex();
|
||||
device->close();
|
||||
return result;
|
||||
}
|
||||
|
||||
void FlameHasher::executeTask()
|
||||
QString hash(QString fileName, Algorithm type)
|
||||
{
|
||||
// CF-specific
|
||||
auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); };
|
||||
|
||||
std::ifstream file_stream(StringUtils::toStdString(m_path).c_str(), std::ifstream::binary);
|
||||
// TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread.
|
||||
// How do we make this non-blocking then?
|
||||
m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out));
|
||||
|
||||
if (m_hash.isEmpty()) {
|
||||
emitFailed("Empty hash!");
|
||||
} else {
|
||||
emitSucceeded();
|
||||
emit resultsReady(m_hash);
|
||||
}
|
||||
QFile file(fileName);
|
||||
return hash(&file, type);
|
||||
}
|
||||
|
||||
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) : Hasher(file_path), provider(provider)
|
||||
QString hash(QByteArray data, Algorithm type)
|
||||
{
|
||||
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
|
||||
hash_type = ProviderCaps.hashType(provider).first();
|
||||
QBuffer buff(&data);
|
||||
return hash(&buff, type);
|
||||
}
|
||||
|
||||
void BlockedModHasher::executeTask()
|
||||
void Hasher::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);
|
||||
}
|
||||
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!");
|
||||
} else {
|
||||
emitSucceeded();
|
||||
emit resultsReady(m_result);
|
||||
}
|
||||
});
|
||||
m_watcher.setFuture(m_future);
|
||||
}
|
||||
|
||||
QStringList BlockedModHasher::getHashTypes()
|
||||
bool Hasher::abort()
|
||||
{
|
||||
return ProviderCaps.hashType(provider);
|
||||
}
|
||||
|
||||
bool BlockedModHasher::useHashType(QString type)
|
||||
{
|
||||
auto types = ProviderCaps.hashType(provider);
|
||||
if (types.contains(type)) {
|
||||
hash_type = type;
|
||||
if (m_future.isRunning()) {
|
||||
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.
|
||||
return true;
|
||||
}
|
||||
qDebug() << "Bad hash type " << type << " for provider";
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Hashing
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QString>
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
@ -7,61 +10,42 @@
|
||||
|
||||
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 {
|
||||
Q_OBJECT
|
||||
public:
|
||||
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 { return true; }
|
||||
bool abort() override;
|
||||
|
||||
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; };
|
||||
|
||||
signals:
|
||||
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:
|
||||
ModPlatform::ResourceProvider provider;
|
||||
QString hash_type;
|
||||
QString m_result;
|
||||
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 createFlameHasher(QString file_path);
|
||||
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);
|
||||
Hasher::Ptr createHasher(QString file_path, QString type);
|
||||
|
||||
} // namespace Hashing
|
||||
|
@ -13,7 +13,7 @@ class PackFetchTask : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PackFetchTask(shared_qobject_ptr<QNetworkAccessManager> network) : QObject(nullptr), m_network(network){};
|
||||
PackFetchTask(shared_qobject_ptr<QNetworkAccessManager> network) : QObject(nullptr), m_network(network) {};
|
||||
virtual ~PackFetchTask() = default;
|
||||
|
||||
void fetch();
|
||||
|
@ -120,3 +120,41 @@ QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
|
||||
{ 4, "newest", QObject::tr("Sort by Newest") },
|
||||
{ 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;
|
||||
|
||||
static Task::Ptr getModCategories(std::shared_ptr<QByteArray> response);
|
||||
static QList<ModPlatform::Category> loadModCategories(std::shared_ptr<QByteArray> response);
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||
|
||||
@ -41,7 +44,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
for (auto loader :
|
||||
{ ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) {
|
||||
if (types & loader) {
|
||||
l << getModLoaderString(loader);
|
||||
l << getModLoaderAsString(loader);
|
||||
}
|
||||
}
|
||||
return l;
|
||||
@ -56,6 +59,27 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
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:
|
||||
[[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type)
|
||||
{
|
||||
@ -73,6 +97,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] QString createFacets(SearchArgs const& args) const
|
||||
{
|
||||
QStringList facets_list;
|
||||
@ -81,6 +106,14 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
||||
if (args.versions.has_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)));
|
||||
|
||||
return QString("[%1]").arg(facets_list.join(','));
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
|
||||
static ModrinthAPI api;
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
bool ModrinthCheckUpdate::abort()
|
||||
{
|
||||
@ -36,7 +35,7 @@ void ModrinthCheckUpdate::executeTask()
|
||||
|
||||
// Create all 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());
|
||||
for (auto* mod : m_mods) {
|
||||
@ -51,7 +50,7 @@ void ModrinthCheckUpdate::executeTask()
|
||||
// need to generate a new hash if the current one is innadequate
|
||||
// (though it will rarely happen, if at all)
|
||||
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) {
|
||||
hashes.append(hash);
|
||||
mappings.insert(hash, mod);
|
||||
@ -112,7 +111,7 @@ void ModrinthCheckUpdate::executeTask()
|
||||
ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
|
||||
for (auto flag : flags) {
|
||||
if (m_loaders.value().testFlag(flag)) {
|
||||
loader_filter = ModPlatform::getModLoaderString(flag);
|
||||
loader_filter = ModPlatform::getModLoaderAsString(flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
|
||||
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
|
||||
const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" });
|
||||
@ -102,8 +103,6 @@ void ModrinthPackExportTask::collectHashes()
|
||||
}))
|
||||
continue;
|
||||
|
||||
QCryptographicHash sha512(QCryptographicHash::Algorithm::Sha512);
|
||||
|
||||
QFile openFile(file.absoluteFilePath());
|
||||
if (!openFile.open(QFile::ReadOnly)) {
|
||||
qWarning() << "Could not open" << file << "for hashing";
|
||||
@ -115,7 +114,7 @@ void ModrinthPackExportTask::collectHashes()
|
||||
qWarning() << "Could not read" << file;
|
||||
continue;
|
||||
}
|
||||
sha512.addData(data);
|
||||
auto sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512);
|
||||
|
||||
auto allMods = mcInstance->loaderModList()->allMods();
|
||||
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())) {
|
||||
qDebug() << "Resolving" << relative << "from index";
|
||||
|
||||
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
|
||||
sha1.addData(data);
|
||||
auto sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1);
|
||||
|
||||
ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size(),
|
||||
mod->metadata()->side };
|
||||
ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), openFile.size(), mod->metadata()->side };
|
||||
resolvedFiles[relative] = resolvedFile;
|
||||
|
||||
// nice! we've managed to resolve based on local metadata!
|
||||
@ -142,7 +139,7 @@ void ModrinthPackExportTask::collectHashes()
|
||||
}
|
||||
|
||||
qDebug() << "Enqueueing" << relative << "for Modrinth query";
|
||||
pendingHashes[relative] = sha512.result().toHex();
|
||||
pendingHashes[relative] = sha512;
|
||||
}
|
||||
|
||||
setAbortable(true);
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,7 +27,6 @@
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
static ModrinthAPI api;
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
auto obj = versionIter.toObject();
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
|
||||
if (file.fileId.isValid() &&
|
||||
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
@ -136,8 +131,9 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArra
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_type, QString preferred_file_name)
|
||||
-> ModPlatform::IndexedVersion
|
||||
auto Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
||||
QString preferred_hash_type,
|
||||
QString preferred_file_name) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
ModPlatform::IndexedVersion file;
|
||||
|
||||
@ -155,15 +151,15 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
||||
for (auto loader : loaders) {
|
||||
if (loader == "neoforge")
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
if (loader == "forge")
|
||||
else if (loader == "forge")
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
if (loader == "cauldron")
|
||||
else if (loader == "cauldron")
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
if (loader == "liteloader")
|
||||
else if (loader == "liteloader")
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
if (loader == "fabric")
|
||||
else if (loader == "fabric")
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
if (loader == "quilt")
|
||||
else if (loader == "quilt")
|
||||
file.loaders |= ModPlatform::Quilt;
|
||||
}
|
||||
file.version = Json::requireString(obj, "name");
|
||||
@ -235,7 +231,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
||||
file.hash = Json::requireString(hash_list, preferred_hash_type);
|
||||
file.hash_type = preferred_hash_type;
|
||||
} else {
|
||||
auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH);
|
||||
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH);
|
||||
for (auto& hash_type : hash_types) {
|
||||
if (hash_list.contains(hash_type)) {
|
||||
file.hash = Json::requireString(hash_list, hash_type);
|
||||
@ -251,8 +247,9 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst)
|
||||
-> ModPlatform::IndexedVersion
|
||||
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m,
|
||||
QJsonArray& arr,
|
||||
const BaseInstance* inst) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* 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);
|
||||
}
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
// Helper functions for extracting data from the TOML file
|
||||
auto stringEntry(toml::table table, QString entry_name) -> QString
|
||||
{
|
||||
@ -91,8 +90,9 @@ auto intEntry(toml::table table, QString entry_name) -> int
|
||||
return node.value_or(0);
|
||||
}
|
||||
|
||||
auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version)
|
||||
-> Mod
|
||||
auto V1::createModFormat([[maybe_unused]] QDir& index_dir,
|
||||
ModPlatform::IndexedPack& mod_pack,
|
||||
ModPlatform::IndexedVersion& mod_version) -> Mod
|
||||
{
|
||||
Mod mod;
|
||||
|
||||
@ -113,7 +113,11 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP
|
||||
mod.provider = mod_pack.provider;
|
||||
mod.file_id = mod_version.fileId;
|
||||
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;
|
||||
}
|
||||
@ -181,6 +185,18 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
||||
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)) {
|
||||
qCritical() << QString("Could not open file %1!").arg(normalized_fname);
|
||||
return;
|
||||
@ -192,6 +208,9 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
||||
auto tbl = toml::table{ { "name", mod.name.toStdString() },
|
||||
{ "filename", mod.filename.toStdString() },
|
||||
{ "side", sideToString(mod.side).toStdString() },
|
||||
{ "loaders", loaders },
|
||||
{ "mcVersions", mcVersions },
|
||||
{ "releaseType", mod.releaseType.toString().toStdString() },
|
||||
{ "download",
|
||||
toml::table{
|
||||
{ "mode", mod.mode.toStdString() },
|
||||
@ -199,7 +218,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
||||
{ "hash-format", mod.hash_format.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;
|
||||
ss << tbl;
|
||||
in_stream << QString::fromStdString(ss.str());
|
||||
@ -276,6 +295,25 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
||||
mod.name = stringEntry(table, "name");
|
||||
mod.filename = stringEntry(table, "filename");
|
||||
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
|
||||
@ -301,11 +339,11 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
||||
}
|
||||
|
||||
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.file_id = intEntry(*mod_provider_table, "file-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.mod_id() = stringEntry(*mod_provider_table, "mod-id");
|
||||
mod.version() = stringEntry(*mod_provider_table, "version");
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -41,6 +42,9 @@ class V1 {
|
||||
QString name{};
|
||||
QString filename{};
|
||||
Side side{ Side::UniversalSide };
|
||||
ModPlatform::ModLoaderTypes loaders;
|
||||
QStringList mcVersions;
|
||||
ModPlatform::IndexedVersionType releaseType;
|
||||
|
||||
// [download]
|
||||
QString mode{};
|
||||
|
@ -45,7 +45,7 @@ namespace Net {
|
||||
*/
|
||||
class ByteArraySink : public Sink {
|
||||
public:
|
||||
ByteArraySink(std::shared_ptr<QByteArray> output) : m_output(output){};
|
||||
ByteArraySink(std::shared_ptr<QByteArray> output) : m_output(output) {};
|
||||
|
||||
virtual ~ByteArraySink() = default;
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace Net {
|
||||
class ChecksumValidator : public Validator {
|
||||
public:
|
||||
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
|
||||
: m_checksum(algorithm), m_expected(expected){};
|
||||
: m_checksum(algorithm), m_expected(expected) {};
|
||||
virtual ~ChecksumValidator() = default;
|
||||
|
||||
public:
|
||||
|
@ -42,7 +42,7 @@
|
||||
namespace Net {
|
||||
class FileSink : public Sink {
|
||||
public:
|
||||
FileSink(QString filename) : m_filename(filename){};
|
||||
FileSink(QString filename) : m_filename(filename) {};
|
||||
virtual ~FileSink() = default;
|
||||
|
||||
public:
|
||||
|
@ -175,6 +175,8 @@ void HttpMetaCache::evictAll()
|
||||
if (!evictEntry(entry))
|
||||
qCWarning(taskHttpMetaCacheLogC) << "Unexpected missing cache entry" << entry->m_basePath;
|
||||
}
|
||||
map.entry_list.clear();
|
||||
FS::deletePath(map.base_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace Net {
|
||||
|
||||
class StaticHeaderProxy : public HeaderProxy {
|
||||
public:
|
||||
StaticHeaderProxy(QList<HeaderPair> hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){};
|
||||
StaticHeaderProxy(QList<HeaderPair> hdrs = {}) : HeaderProxy(), m_hdrs(hdrs) {};
|
||||
virtual ~StaticHeaderProxy() = default;
|
||||
|
||||
public:
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
class FSTreeMatcher : public IPathMatcher {
|
||||
public:
|
||||
virtual ~FSTreeMatcher(){};
|
||||
virtual ~FSTreeMatcher() {};
|
||||
FSTreeMatcher(SeparatorPrefixTree<'/'>& tree) : m_fsTree(tree) {}
|
||||
|
||||
bool matches(const QString& string) const override { return m_fsTree.covers(string); }
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
class MultiMatcher : public IPathMatcher {
|
||||
public:
|
||||
virtual ~MultiMatcher(){};
|
||||
virtual ~MultiMatcher() {};
|
||||
MultiMatcher() {}
|
||||
MultiMatcher& add(Ptr add)
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
class SimplePrefixMatcher : public IPathMatcher {
|
||||
public:
|
||||
virtual ~SimplePrefixMatcher(){};
|
||||
virtual ~SimplePrefixMatcher() {};
|
||||
SimplePrefixMatcher(const QString& prefix)
|
||||
{
|
||||
m_prefix = prefix;
|
||||
|
@ -49,7 +49,7 @@ class ImgurAlbumCreation : public Net::NetRequest {
|
||||
|
||||
class Sink : public Net::Sink {
|
||||
public:
|
||||
Sink(std::shared_ptr<Result> res) : m_result(res){};
|
||||
Sink(std::shared_ptr<Result> res) : m_result(res) {};
|
||||
virtual ~Sink() = default;
|
||||
|
||||
public:
|
||||
|
@ -43,7 +43,7 @@ class ImgurUpload : public Net::NetRequest {
|
||||
public:
|
||||
class Sink : public Net::Sink {
|
||||
public:
|
||||
Sink(ScreenShot::Ptr shot) : m_shot(shot){};
|
||||
Sink(ScreenShot::Ptr shot) : m_shot(shot) {};
|
||||
virtual ~Sink() = default;
|
||||
|
||||
public:
|
||||
|
@ -22,7 +22,7 @@
|
||||
class GenericProfilerFactory : public BaseProfilerFactory {
|
||||
public:
|
||||
QString name() const override { return "Generic"; }
|
||||
void registerSettings([[maybe_unused]] SettingsObjectPtr settings) override{};
|
||||
void registerSettings([[maybe_unused]] SettingsObjectPtr settings) override {};
|
||||
BaseExternalTool* createTool(InstancePtr instance, QObject* parent = 0) override;
|
||||
bool check([[maybe_unused]] QString* error) override { return true; };
|
||||
bool check([[maybe_unused]] const QString& path, [[maybe_unused]] QString* error) override { return true; };
|
||||
|
@ -1560,7 +1560,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
|
||||
QFileDialog fileDialog;
|
||||
// workaround to make sure the portal file dialog opens in the desktop directory
|
||||
fileDialog.setDirectoryUrl(desktopPath);
|
||||
desktopFilePath = fileDialog.getSaveFileName(this, tr("Create Shortcut"), desktopFilePath, tr("Desktop Entries (*.desktop)"));
|
||||
desktopFilePath = fileDialog.getSaveFileName(this, tr("Create Shortcut"), desktopFilePath, tr("Desktop Entries") + " (*.desktop)");
|
||||
if (desktopFilePath.isEmpty())
|
||||
return; // file dialog canceled by user
|
||||
appPath = "flatpak";
|
||||
|
@ -266,7 +266,7 @@ void BlockedModsDialog::addHashTask(QString path)
|
||||
/// @param path the path to the local file being hashed
|
||||
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;
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
ChooseProviderDialog::ChooseProviderDialog(QWidget* parent, bool single_choice, bool allow_skipping)
|
||||
: QDialog(parent), ui(new Ui::ChooseProviderDialog)
|
||||
{
|
||||
@ -78,7 +76,7 @@ void ChooseProviderDialog::addProviders()
|
||||
QRadioButton* btn;
|
||||
|
||||
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++);
|
||||
ui->providersLayout->addWidget(btn);
|
||||
}
|
||||
|
@ -129,14 +129,14 @@ void ExportPackDialog::done(int result)
|
||||
QString output;
|
||||
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".mrpack"),
|
||||
"Modrinth pack (*.mrpack *.zip)", nullptr);
|
||||
tr("Modrinth pack") + " (*.mrpack *.zip)", nullptr);
|
||||
if (output.isEmpty())
|
||||
return;
|
||||
if (!(output.endsWith(".zip") || output.endsWith(".mrpack")))
|
||||
output.append(".mrpack");
|
||||
} else {
|
||||
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(name), FS::PathCombine(QDir::homePath(), filename + ".zip"),
|
||||
"CurseForge pack (*.zip)", nullptr);
|
||||
tr("CurseForge pack") + " (*.zip)", nullptr);
|
||||
if (output.isEmpty())
|
||||
return;
|
||||
if (!output.endsWith(".zip"))
|
||||
|
@ -160,7 +160,7 @@ void ExportToModListDialog::done(int result)
|
||||
const QString filename = FS::RemoveInvalidFilenameChars(m_name);
|
||||
const QString output =
|
||||
QFileDialog::getSaveFileName(this, tr("Export %1").arg(m_name), FS::PathCombine(QDir::homePath(), filename + extension()),
|
||||
"File (*.txt *.html *.md *.json *.csv)", nullptr);
|
||||
tr("File") + " (*.txt *.html *.md *.json *.csv)", nullptr);
|
||||
|
||||
if (output.isEmpty())
|
||||
return;
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
static std::list<Version> mcVersions(BaseInstance* inst)
|
||||
{
|
||||
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
|
||||
@ -94,17 +92,15 @@ void ModUpdateDialog::checkCandidates()
|
||||
|
||||
if (!m_modrinth_to_update.empty()) {
|
||||
m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model));
|
||||
connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ mod, reason, recover_url });
|
||||
});
|
||||
connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this,
|
||||
[this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({ mod, reason, recover_url }); });
|
||||
check_task.addTask(m_modrinth_check_task);
|
||||
}
|
||||
|
||||
if (!m_flame_to_update.empty()) {
|
||||
m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model));
|
||||
connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ mod, reason, recover_url });
|
||||
});
|
||||
connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this,
|
||||
[this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({ mod, reason, recover_url }); });
|
||||
check_task.addTask(m_flame_check_task);
|
||||
}
|
||||
|
||||
@ -427,7 +423,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
||||
item_top->setExpanded(true);
|
||||
|
||||
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);
|
||||
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version));
|
||||
|
@ -200,7 +200,7 @@ void NewInstanceDialog::setSuggestedPack(const QString& name, InstanceTask* task
|
||||
importVersion.clear();
|
||||
|
||||
if (!task) {
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon("default"));
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
|
||||
importIcon = false;
|
||||
}
|
||||
|
||||
@ -216,7 +216,7 @@ void NewInstanceDialog::setSuggestedPack(const QString& name, QString version, I
|
||||
importVersion = std::move(version);
|
||||
|
||||
if (!task) {
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon("default"));
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
|
||||
importIcon = false;
|
||||
}
|
||||
|
||||
@ -236,6 +236,9 @@ void NewInstanceDialog::setSuggestedIconFromFile(const QString& path, const QStr
|
||||
|
||||
void NewInstanceDialog::setSuggestedIcon(const QString& key)
|
||||
{
|
||||
if (key == "default")
|
||||
return;
|
||||
|
||||
auto icon = APPLICATION->icons()->getIcon(key);
|
||||
importIcon = false;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user