Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2023-08-27 20:41:56 +03:00
commit 0680d2dceb
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
78 changed files with 588 additions and 640 deletions

1
.envrc
View File

@ -1 +1,2 @@
use flake use flake
watch_file nix/*.nix

View File

@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22 - uses: cachix/install-nix-action@v22
- uses: DeterminateSystems/update-flake-lock@v19 - uses: DeterminateSystems/update-flake-lock@v20
with: with:
commit-msg: "chore(nix): update lockfile" commit-msg: "chore(nix): update lockfile"
pr-title: "chore(nix): update lockfile" pr-title: "chore(nix): update lockfile"

View File

@ -91,11 +91,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1691853136, "lastModified": 1693060755,
"narHash": "sha256-wTzDsRV4HN8A2Sl0SVQY0q8ILs90CD43Ha//7gNZE+E=", "narHash": "sha256-KNsbfqewEziFJEpPR0qvVz4rx0x6QXxw1CcunRhlFdk=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "f0451844bbdf545f696f029d1448de4906c7f753", "rev": "c66ccfa00c643751da2fd9290e096ceaa30493fc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -138,11 +138,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1691747570, "lastModified": 1692274144,
"narHash": "sha256-J3fnIwJtHVQ0tK2JMBv4oAmII+1mCdXdpeCxtIsrL2A=", "narHash": "sha256-BxTQuRUANQ81u8DJznQyPmRsg63t4Yc+0kcyq6OLz8s=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "c5ac3aa3324bd8aebe8622a3fc92eeb3975d317a", "rev": "7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -9,7 +9,6 @@
* Copyright (C) 2022 Tayou <git@tayou.org> * Copyright (C) 2022 Tayou <git@tayou.org>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
* Copyright (C) 2023 seth <getchoo at tuta dot io>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -580,9 +579,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaCompatibility", false);
m_settings->registerSetting("IgnoreJavaWizard", false); m_settings->registerSetting("IgnoreJavaWizard", false);
// Mod loader settings
m_settings->registerSetting("DisableQuiltBeacon", false);
// Native library workarounds // Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("UseNativeOpenAL", false);
m_settings->registerSetting("CustomOpenALPath", ""); m_settings->registerSetting("CustomOpenALPath", "");
@ -598,6 +594,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_settings->registerSetting("ShowGameTime", true); m_settings->registerSetting("ShowGameTime", true);
m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("ShowGlobalGameTime", true);
m_settings->registerSetting("RecordGameTime", true); m_settings->registerSetting("RecordGameTime", true);
m_settings->registerSetting("ShowGameTimeWithoutDays", false);
// Minecraft mods // Minecraft mods
m_settings->registerSetting("ModMetadataDisabled", false); m_settings->registerSetting("ModMetadataDisabled", false);
@ -968,7 +965,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse; qDebug() << " Launching with account" << m_profileToUse;
} }
launch(inst, true, false, nullptr, serverToJoin, accountToUse); launch(inst, true, false, serverToJoin, accountToUse);
return; return;
} }
} }
@ -1067,7 +1064,7 @@ void Application::messageReceived(const QByteArray& message)
} }
} }
launch(instance, true, false, nullptr, serverObject, accountObject); launch(instance, true, false, serverObject, accountObject);
} else { } else {
qWarning() << "Received invalid message" << message; qWarning() << "Received invalid message" << message;
} }
@ -1108,7 +1105,6 @@ bool Application::openJsonEditor(const QString& filename)
bool Application::launch(InstancePtr instance, bool Application::launch(InstancePtr instance,
bool online, bool online,
bool demo, bool demo,
BaseProfilerFactory* profiler,
MinecraftServerTargetPtr serverToJoin, MinecraftServerTargetPtr serverToJoin,
MinecraftAccountPtr accountToUse) MinecraftAccountPtr accountToUse)
{ {
@ -1116,7 +1112,7 @@ bool Application::launch(InstancePtr instance,
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
} else if (instance->canLaunch()) { } else if (instance->canLaunch()) {
auto& extras = m_instanceExtras[instance->id()]; auto& extras = m_instanceExtras[instance->id()];
auto& window = extras.window; auto window = extras.window;
if (window) { if (window) {
if (!window->saveAll()) { if (!window->saveAll()) {
return false; return false;
@ -1127,7 +1123,7 @@ bool Application::launch(InstancePtr instance,
controller->setInstance(instance); controller->setInstance(instance);
controller->setOnline(online); controller->setOnline(online);
controller->setDemo(demo); controller->setDemo(demo);
controller->setProfiler(profiler); controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
controller->setServerToJoin(serverToJoin); controller->setServerToJoin(serverToJoin);
controller->setAccountToUse(accountToUse); controller->setAccountToUse(accountToUse);
if (window) { if (window) {

View File

@ -195,7 +195,6 @@ class Application : public QApplication {
bool launch(InstancePtr instance, bool launch(InstancePtr instance,
bool online = true, bool online = true,
bool demo = false, bool demo = false,
BaseProfilerFactory* profiler = nullptr,
MinecraftServerTargetPtr serverToJoin = nullptr, MinecraftServerTargetPtr serverToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr); MinecraftAccountPtr accountToUse = nullptr);
bool kill(InstancePtr instance); bool kill(InstancePtr instance);

View File

@ -3,6 +3,7 @@
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -100,6 +101,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("ManagedPackName", ""); m_settings->registerSetting("ManagedPackName", "");
m_settings->registerSetting("ManagedPackVersionID", ""); m_settings->registerSetting("ManagedPackVersionID", "");
m_settings->registerSetting("ManagedPackVersionName", ""); m_settings->registerSetting("ManagedPackVersionName", "");
m_settings->registerSetting("Profiler", "");
} }
QString BaseInstance::getPreLaunchCommand() QString BaseInstance::getPreLaunchCommand()

View File

@ -3,6 +3,7 @@
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -38,6 +39,7 @@
#include <cassert> #include <cassert>
#include <QDateTime> #include <QDateTime>
#include <QMenu>
#include <QObject> #include <QObject>
#include <QProcess> #include <QProcess>
#include <QSet> #include <QSet>
@ -246,6 +248,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
virtual bool canEdit() const = 0; virtual bool canEdit() const = 0;
virtual bool canExport() const = 0; virtual bool canExport() const = 0;
virtual void populateLaunchMenu(QMenu* menu) = 0;
bool reloadSettings(); bool reloadSettings();
/** /**
@ -282,6 +286,8 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
void runningStatusChanged(bool running); void runningStatusChanged(bool running);
void profilerChanged();
void statusChanged(Status from, Status to); void statusChanged(Status from, Status to);
protected slots: protected slots:

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -361,22 +362,21 @@ void LaunchController::readyForLaunch()
QString error; QString error;
if (!m_profiler->check(&error)) { if (!m_profiler->check(&error)) {
m_launcher->abort(); m_launcher->abort();
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
emitFailed("Profiler startup failed!"); emitFailed("Profiler startup failed!");
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
return; return;
} }
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) { connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
QMessageBox msg; QMessageBox msg(m_parentWidget);
msg.setText(tr("The game launch is delayed until you press the " msg.setText(tr("The game launch is delayed until you press the "
"button. This is the right time to setup the profiler, as the " "button. This is the right time to setup the profiler, as the "
"profiler server is running now.\n\n%1") "profiler server is running now.\n\n%1")
.arg(message)); .arg(message));
msg.setWindowTitle(tr("Waiting.")); msg.setWindowTitle(tr("Waiting."));
msg.setIcon(QMessageBox::Information); msg.setIcon(QMessageBox::Information);
msg.addButton(tr("Launch"), QMessageBox::AcceptRole); msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
msg.setModal(true);
msg.exec(); msg.exec();
m_launcher->proceed(); m_launcher->proceed();
}); });

View File

@ -16,19 +16,20 @@
*/ */
#include <MMCTime.h> #include <MMCTime.h>
#include <qobject.h>
#include <QDateTime> #include <QDateTime>
#include <QObject> #include <QObject>
#include <QTextStream> #include <QTextStream>
QString Time::prettifyDuration(int64_t duration) QString Time::prettifyDuration(int64_t duration, bool noDays)
{ {
int seconds = (int)(duration % 60); int seconds = (int)(duration % 60);
duration /= 60; duration /= 60;
int minutes = (int)(duration % 60); int minutes = (int)(duration % 60);
duration /= 60; duration /= 60;
int hours = (int)(duration % 24); int hours = (int)(noDays ? duration : (duration % 24));
int days = (int)(duration / 24); int days = (int)(noDays ? 0 : (duration / 24));
if ((hours == 0) && (days == 0)) { if ((hours == 0) && (days == 0)) {
return QObject::tr("%1min %2s").arg(minutes).arg(seconds); return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
} }

View File

@ -20,7 +20,7 @@
namespace Time { namespace Time {
QString prettifyDuration(int64_t duration); QString prettifyDuration(int64_t duration, bool noDays = false);
/** /**
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.

View File

@ -2,6 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -62,6 +63,7 @@ class NullInstance : public BaseInstance {
bool canExport() const override { return false; } bool canExport() const override { return false; }
bool canEdit() const override { return false; } bool canEdit() const override { return false; }
bool canLaunch() const override { return false; } bool canLaunch() const override { return false; }
void populateLaunchMenu(QMenu* menu) override {}
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
{ {
QStringList out; QStringList out;

View File

@ -103,14 +103,8 @@ class Version {
QString m_fullString; QString m_fullString;
[[nodiscard]] inline bool isAppendix() const [[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
{ [[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
return m_stringPart.startsWith('+');
}
[[nodiscard]] inline bool isPreRelease() const
{
return m_stringPart.startsWith('-') && m_stringPart.length() > 1;
}
inline bool operator==(const Section& other) const inline bool operator==(const Section& other) const
{ {
@ -156,14 +150,8 @@ class Version {
return m_fullString < other.m_fullString; return m_fullString < other.m_fullString;
} }
inline bool operator!=(const Section& other) const inline bool operator!=(const Section& other) const { return !(*this == other); }
{ inline bool operator>(const Section& other) const { return !(*this < other || *this == other); }
return !(*this == other);
}
inline bool operator>(const Section& other) const
{
return !(*this < other || *this == other);
}
}; };
private: private:

View File

@ -30,7 +30,7 @@ class LogModel : public QAbstractListModel {
enum Roles { LevelRole = Qt::UserRole }; enum Roles { LevelRole = Qt::UserRole };
private /* types */: private /* types */:
struct entry { struct entry {
MessageLevel::Enum level; MessageLevel::Enum level;
QString line; QString line;

View File

@ -2,14 +2,14 @@
#include "Component.h" #include "Component.h"
#include "ComponentUpdateTask_p.h" #include "ComponentUpdateTask_p.h"
#include "OneSixVersionFormat.h"
#include "PackProfile.h" #include "PackProfile.h"
#include "PackProfile_p.h" #include "PackProfile_p.h"
#include "Version.h" #include "Version.h"
#include "cassert" #include "cassert"
#include "meta/Index.h" #include "meta/Index.h"
#include "meta/Version.h" #include "meta/Version.h"
#include "meta/VersionList.h" #include "minecraft/OneSixVersionFormat.h"
#include "minecraft/ProfileUtils.h"
#include "net/Mode.h" #include "net/Mode.h"
#include "Application.h" #include "Application.h"

View File

@ -3,8 +3,7 @@
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (C) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* Copyright (c) 2023 seth <getchoo at tuta dot io>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -88,6 +87,10 @@
#include "minecraft/gameoptions/GameOptions.h" #include "minecraft/gameoptions/GameOptions.h"
#include "minecraft/update/FoldersTask.h" #include "minecraft/update/FoldersTask.h"
#include "tools/BaseProfiler.h"
#include <QActionGroup>
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
#include "MangoHud.h" #include "MangoHud.h"
#endif #endif
@ -181,10 +184,6 @@ void MinecraftInstance::loadSpecificSettings()
m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride);
m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride);
// Mod loader specific options
auto modLoaderSettings = m_settings->registerSetting("OverrideModLoaderSettings", false);
m_settings->registerOverride(global_settings->getSetting("DisableQuiltBeacon"), modLoaderSettings);
m_settings->set("InstanceType", "OneSix"); m_settings->set("InstanceType", "OneSix");
} }
@ -231,6 +230,50 @@ QSet<QString> MinecraftInstance::traits() const
return profile->getTraits(); return profile->getTraits();
} }
// FIXME: move UI code out of MinecraftInstance
void MinecraftInstance::populateLaunchMenu(QMenu* menu)
{
QAction* normalLaunch = menu->addAction(tr("&Launch"));
normalLaunch->setShortcut(QKeySequence::Open);
QAction* normalLaunchOffline = menu->addAction(tr("Launch &Offline"));
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
QAction* normalLaunchDemo = menu->addAction(tr("Launch &Demo"));
normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
normalLaunchDemo->setEnabled(supportsDemo());
connect(normalLaunch, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this()); });
connect(normalLaunchOffline, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, false); });
connect(normalLaunchDemo, &QAction::triggered, [this] { APPLICATION->launch(shared_from_this(), false, true); });
QString profilersTitle = tr("Profilers");
menu->addSeparator()->setText(profilersTitle);
auto profilers = new QActionGroup(menu);
profilers->setExclusive(true);
connect(profilers, &QActionGroup::triggered, [this](QAction* action) {
settings()->set("Profiler", action->data());
emit profilerChanged();
});
QAction* noProfilerAction = menu->addAction(tr("&No Profiler"));
noProfilerAction->setData("");
noProfilerAction->setCheckable(true);
noProfilerAction->setChecked(true);
profilers->addAction(noProfilerAction);
for (auto profiler = APPLICATION->profilers().begin(); profiler != APPLICATION->profilers().end(); profiler++) {
QAction* profilerAction = menu->addAction(profiler.value()->name());
profilers->addAction(profilerAction);
profilerAction->setData(profiler.key());
profilerAction->setCheckable(true);
profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key());
QString error;
profilerAction->setEnabled(profiler.value()->check(&error));
}
}
QString MinecraftInstance::gameRoot() const QString MinecraftInstance::gameRoot() const
{ {
QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft"));
@ -386,12 +429,6 @@ QStringList MinecraftInstance::extraArguments()
list.append("-javaagent:" + jar[0] + (agent->argument().isEmpty() ? "" : "=" + agent->argument())); list.append("-javaagent:" + jar[0] + (agent->argument().isEmpty() ? "" : "=" + agent->argument()));
} }
{
const auto loaders = version->getModLoaders();
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
list.append("-Dloader.disable_beacon=true");
}
{ {
QString openALPath; QString openALPath;
QString glfwPath; QString glfwPath;
@ -897,13 +934,16 @@ QString MinecraftInstance::getStatusbarDescription()
if (m_settings->get("ShowGameTime").toBool()) { if (m_settings->get("ShowGameTime").toBool()) {
if (lastTimePlayed() > 0) { if (lastTimePlayed() > 0) {
QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch()); QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch());
description.append(tr(", last played on %1 for %2") description.append(
.arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat)) tr(", last played on %1 for %2")
.arg(Time::prettifyDuration(lastTimePlayed()))); .arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat))
.arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
} }
if (totalTimePlayed() > 0) { if (totalTimePlayed() > 0) {
description.append(tr(", total played for %1").arg(Time::prettifyDuration(totalTimePlayed()))); description.append(
tr(", total played for %1")
.arg(Time::prettifyDuration(totalTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
} }
} }
if (hasCrashed()) { if (hasCrashed()) {

View File

@ -2,7 +2,7 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -70,6 +70,8 @@ class MinecraftInstance : public BaseInstance {
bool canExport() const override { return true; } bool canExport() const override { return true; }
void populateLaunchMenu(QMenu* menu) override;
////// Directories and files ////// ////// Directories and files //////
QString jarModsDir() const; QString jarModsDir() const;
QString resourcePacksDir() const; QString resourcePacksDir() const;

View File

@ -58,15 +58,14 @@
#include "ComponentUpdateTask.h" #include "ComponentUpdateTask.h"
#include "PackProfile.h" #include "PackProfile.h"
#include "PackProfile_p.h" #include "PackProfile_p.h"
#include "minecraft/mod/Mod.h"
#include "modplatform/ModIndex.h"
#include "Application.h" static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
#include "modplatform/ResourceAPI.h" { "net.minecraftforge", ModPlatform::Forge },
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.neoforged", ResourceAPI::NeoForge }, { "org.quiltmc.quilt-loader", ModPlatform::Quilt },
{ "net.minecraftforge", ResourceAPI::Forge }, { "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
{ "net.fabricmc.fabric-loader", ResourceAPI::Fabric },
{ "org.quiltmc.quilt-loader", ResourceAPI::Quilt },
{ "com.mumfrey.liteloader", ResourceAPI::LiteLoader } };
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel() PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
{ {
@ -990,12 +989,12 @@ void PackProfile::disableInteraction(bool disable)
} }
} }
std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders() std::optional<ModPlatform::ModLoaderTypes> PackProfile::getModLoaders()
{ {
ResourceAPI::ModLoaderTypes result; ModPlatform::ModLoaderTypes result;
bool has_any_loader = false; bool has_any_loader = false;
QMapIterator<QString, ResourceAPI::ModLoaderType> i(modloaderMapping); QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
@ -1009,3 +1008,18 @@ std::optional<ResourceAPI::ModLoaderTypes> PackProfile::getModLoaders()
return {}; return {};
return result; return result;
} }
std::optional<ModPlatform::ModLoaderTypes> PackProfile::getSupportedModLoaders()
{
auto loadersOpt = getModLoaders();
if (!loadersOpt.has_value())
return loadersOpt;
auto loaders = loadersOpt.value();
// TODO: remove this or add version condition once Quilt drops official Fabric support
if (loaders & ModPlatform::Quilt)
loaders |= ModPlatform::Fabric;
// TODO: remove this or add version condition once NeoForge drops official Forge support
if (loaders & ModPlatform::NeoForge)
loaders |= ModPlatform::Forge;
return loaders;
}

View File

@ -44,14 +44,11 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <memory> #include <memory>
#include <optional>
#include "BaseVersion.h"
#include "Component.h" #include "Component.h"
#include "LaunchProfile.h" #include "LaunchProfile.h"
#include "Library.h" #include "modplatform/ModIndex.h"
#include "MojangDownloadInfo.h"
#include "ProfileUtils.h"
#include "modplatform/ResourceAPI.h"
#include "net/Mode.h" #include "net/Mode.h"
class MinecraftInstance; class MinecraftInstance;
@ -146,7 +143,9 @@ class PackProfile : public QAbstractListModel {
// todo(merged): is this the best approach // todo(merged): is this the best approach
void appendComponent(ComponentPtr component); void appendComponent(ComponentPtr component);
std::optional<ResourceAPI::ModLoaderTypes> getModLoaders(); std::optional<ModPlatform::ModLoaderTypes> getModLoaders();
// this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge)
std::optional<ModPlatform::ModLoaderTypes> getSupportedModLoaders();
private: private:
void scheduleSave(); void scheduleSave();

View File

@ -415,7 +415,7 @@ Qt::ItemFlags AccountList::flags(const QModelIndex& index) const
bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role) bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role)
{ {
if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid()) { if (idx.row() < 0 || idx.row() >= rowCount(idx.parent()) || !idx.isValid()) {
return false; return false;
} }
@ -423,7 +423,8 @@ bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int rol
if (value == Qt::Checked) { if (value == Qt::Checked) {
MinecraftAccountPtr account = at(idx.row()); MinecraftAccountPtr account = at(idx.row());
setDefaultAccount(account); setDefaultAccount(account);
} } else if (m_defaultAccount == at(idx.row()))
setDefaultAccount(nullptr);
} }
emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1)); emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));

View File

@ -51,8 +51,13 @@
#include "Application.h" #include "Application.h"
#include "Json.h"
#include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
#include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/FlameModIndex.h"
ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir)
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
@ -309,3 +314,47 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1));
} }
static const FlameAPI flameAPI;
bool ModFolderModel::installMod(QString file_path, ModPlatform::IndexedVersion& vers)
{
if (vers.addonId.isValid()) {
ModPlatform::IndexedPack pack{
vers.addonId,
ModPlatform::ResourceProvider::FLAME,
};
QEventLoop loop;
auto response = std::make_shared<QByteArray>();
auto job = flameAPI.getProject(vers.addonId.toString(), response);
QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); });
QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit);
QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] {
QJsonParseError parse_error{};
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
if (parse_error.error != QJsonParseError::NoError) {
qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset
<< " reason: " << parse_error.errorString();
qDebug() << *response;
return;
}
try {
auto obj = Json::requireObject(Json::requireObject(doc), "data");
FlameMod::loadIndexedPack(pack, obj);
} catch (const JSONValidationError& e) {
qDebug() << doc;
qWarning() << "Error while reading mod info: " << e.cause();
}
LocalModUpdateTask update_metadata(indexDir(), pack, vers);
QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit);
update_metadata.start();
});
job->start();
loop.exec();
}
return ResourceFolderModel::installResource(file_path);
}

View File

@ -48,6 +48,7 @@
#include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
class LegacyInstance; class LegacyInstance;
class BaseInstance; class BaseInstance;
@ -75,6 +76,7 @@ class ModFolderModel : public ResourceFolderModel {
[[nodiscard]] Task* createParseTask(Resource&) override; [[nodiscard]] Task* createParseTask(Resource&) override;
bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); } bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); }
bool installMod(QString file_path, ModPlatform::IndexedVersion& vers);
bool uninstallMod(const QString& filename, bool preserve_metadata = false); bool uninstallMod(const QString& filename, bool preserve_metadata = false);
/// Deletes all the selected mods /// Deletes all the selected mods

View File

@ -39,9 +39,9 @@ static Version mcVersion(BaseInstance* inst)
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion(); return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
} }
static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
{ {
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders().value(); return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
} }
GetModDependenciesTask::GetModDependenciesTask(QObject* parent, GetModDependenciesTask::GetModDependenciesTask(QObject* parent,
@ -75,7 +75,7 @@ void GetModDependenciesTask::prepare()
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep, ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
const ModPlatform::ResourceProvider providerName) const ModPlatform::ResourceProvider providerName)
{ {
if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) { if (auto isQuilt = m_loaderType & ModPlatform::Quilt; isQuilt || m_loaderType & ModPlatform::Fabric) {
auto overide = ModPlatform::getOverrideDeps(); auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) { auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) {
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt); return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
@ -191,7 +191,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
} }
pDep->version = provider.mod->loadDependencyVersions(dep, arr); pDep->version = provider.mod->loadDependencyVersions(dep, arr);
if (!pDep->version.addonId.isValid()) { if (!pDep->version.addonId.isValid()) {
if (m_loaderType & ResourceAPI::Quilt) { // falback for quilt if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
auto overide = ModPlatform::getOverrideDeps(); auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(), auto over = std::find_if(overide.cbegin(), overide.cend(),
[dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; }); [dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; });
@ -201,6 +201,7 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen
return; return;
} }
} }
removePack(dep.addonId);
qWarning() << "Error while reading mod version empty "; qWarning() << "Error while reading mod version empty ";
qDebug() << doc; qDebug() << doc;
return; return;

View File

@ -81,5 +81,5 @@ class GetModDependenciesTask : public SequentialTask {
Provider m_modrinth_provider; Provider m_modrinth_provider;
Version m_version; Version m_version;
ResourceAPI::ModLoaderTypes m_loaderType; ModPlatform::ModLoaderTypes m_loaderType;
}; };

View File

@ -15,7 +15,7 @@ class CheckUpdateTask : public Task {
public: public:
CheckUpdateTask(QList<Mod*>& mods, CheckUpdateTask(QList<Mod*>& mods,
std::list<Version>& mcVersions, std::list<Version>& mcVersions,
std::optional<ResourceAPI::ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<ModFolderModel> mods_folder) 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){};
@ -55,7 +55,7 @@ class CheckUpdateTask : public Task {
protected: protected:
QList<Mod*>& m_mods; QList<Mod*>& m_mods;
std::list<Version>& m_game_versions; std::list<Version>& m_game_versions;
std::optional<ResourceAPI::ModLoaderTypes> m_loaders; std::optional<ModPlatform::ModLoaderTypes> m_loaders;
std::shared_ptr<ModFolderModel> m_mods_folder; std::shared_ptr<ModFolderModel> m_mods_folder;
std::vector<UpdatableMod> m_updatable; std::vector<UpdatableMod> m_updatable;

View File

@ -83,4 +83,25 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID)
projectID.toString(); projectID.toString();
} }
auto getModLoaderString(ModLoaderType type) -> const QString
{
switch (type) {
case NeoForge:
return "neoforge";
case Forge:
return "forge";
case Cauldron:
return "cauldron";
case LiteLoader:
return "liteloader";
case Fabric:
return "fabric";
case Quilt:
return "quilt";
default:
break;
}
return "";
}
} // namespace ModPlatform } // namespace ModPlatform

View File

@ -30,6 +30,9 @@ class QIODevice;
namespace ModPlatform { namespace ModPlatform {
enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 };
Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
enum class ResourceProvider { MODRINTH, FLAME }; enum class ResourceProvider { MODRINTH, FLAME };
enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK }; enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK };
@ -70,7 +73,7 @@ struct IndexedVersion {
QString downloadUrl; QString downloadUrl;
QString date; QString date;
QString fileName; QString fileName;
QStringList loaders = {}; ModLoaderTypes loaders = {};
QString hash_type; QString hash_type;
QString hash; QString hash;
bool is_preferred = true; bool is_preferred = true;
@ -128,7 +131,6 @@ struct IndexedPack {
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; }); return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
} }
}; };
QString getMetaURL(ResourceProvider provider, QVariant projectID);
struct OverrideDep { struct OverrideDep {
QString quilt; QString quilt;
@ -148,6 +150,14 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
QString getMetaURL(ResourceProvider provider, QVariant projectID); QString getMetaURL(ResourceProvider provider, QVariant projectID);
auto getModLoaderString(ModLoaderType type) -> const QString;
constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept
{
auto x = static_cast<int>(l);
return x && !(x & (x - 1));
}
} // namespace ModPlatform } // namespace ModPlatform
Q_DECLARE_METATYPE(ModPlatform::IndexedPack) Q_DECLARE_METATYPE(ModPlatform::IndexedPack)

View File

@ -54,9 +54,6 @@ class ResourceAPI {
public: public:
virtual ~ResourceAPI() = default; virtual ~ResourceAPI() = default;
enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 };
Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
struct SortingMethod { struct SortingMethod {
// The index of the sorting method. Used to allow for arbitrary ordering in the list of methods. // The index of the sorting method. Used to allow for arbitrary ordering in the list of methods.
// Used by Flame in the API request. // Used by Flame in the API request.
@ -74,7 +71,7 @@ class ResourceAPI {
std::optional<QString> search; std::optional<QString> search;
std::optional<SortingMethod> sorting; std::optional<SortingMethod> sorting;
std::optional<ModLoaderTypes> loaders; std::optional<ModPlatform::ModLoaderTypes> loaders;
std::optional<std::list<Version> > versions; std::optional<std::list<Version> > versions;
}; };
struct SearchCallbacks { struct SearchCallbacks {
@ -87,7 +84,7 @@ class ResourceAPI {
ModPlatform::IndexedPack pack; ModPlatform::IndexedPack pack;
std::optional<std::list<Version> > mcVersions; std::optional<std::list<Version> > mcVersions;
std::optional<ModLoaderTypes> loaders; std::optional<ModPlatform::ModLoaderTypes> loaders;
VersionSearchArgs(VersionSearchArgs const&) = default; VersionSearchArgs(VersionSearchArgs const&) = default;
void operator=(VersionSearchArgs other) void operator=(VersionSearchArgs other)
@ -114,7 +111,7 @@ class ResourceAPI {
struct DependencySearchArgs { struct DependencySearchArgs {
ModPlatform::Dependency dependency; ModPlatform::Dependency dependency;
Version mcVersion; Version mcVersion;
ModLoaderTypes loader; ModPlatform::ModLoaderTypes loader;
}; };
struct DependencySearchCallbacks { struct DependencySearchCallbacks {
@ -161,27 +158,6 @@ class ResourceAPI {
return nullptr; return nullptr;
} }
static auto getModLoaderString(ModLoaderType type) -> const QString
{
switch (type) {
case NeoForge:
return "neoforge";
case Forge:
return "forge";
case Cauldron:
return "cauldron";
case LiteLoader:
return "liteloader";
case Fabric:
return "fabric";
case Quilt:
return "quilt";
default:
break;
}
return "";
}
protected: protected:
[[nodiscard]] inline QString debugName() const { return "External resource API"; } [[nodiscard]] inline QString debugName() const { return "External resource API"; }

View File

@ -1,6 +1,7 @@
#include "FileResolvingTask.h" #include "FileResolvingTask.h"
#include "Json.h" #include "Json.h"
#include "modplatform/ModIndex.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
#include "net/ApiUpload.h" #include "net/ApiUpload.h"
#include "net/Upload.h" #include "net/Upload.h"
@ -102,7 +103,7 @@ void Flame::FileResolvingTask::netJobFinished()
auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash);
auto output = std::make_shared<QByteArray>(); auto output = std::make_shared<QByteArray>();
auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output); auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output);
QObject::connect(dl.get(), &Net::Download::succeeded, [&out]() { out.resolved = true; }); QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [&out]() { out.resolved = true; });
m_checkJob->addNetAction(dl); m_checkJob->addNetAction(dl);
blockedProjects.insert(&out, output); blockedProjects.insert(&out, output);
@ -153,7 +154,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
// If there's more than one mod loader for this version, we can't know for sure // If there's more than one mod loader for this version, we can't know for sure
// which file is relative to each loader, so it's best to not use any one and // which file is relative to each loader, so it's best to not use any one and
// let the user download it manually. // let the user download it manually.
if (file.loaders.size() <= 1) { if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) {
out->url = file.downloadUrl; out->url = file.downloadUrl;
qDebug() << "Found alternative on modrinth " << out->fileName; qDebug() << "Found alternative on modrinth " << out->fileName;
} else { } else {
@ -175,7 +176,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished()
auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId);
auto dl = Net::ApiDownload::makeByteArray(url, output); auto dl = Net::ApiDownload::makeByteArray(url, output);
qDebug() << "Fetching url slug for file:" << mod->fileName; qDebug() << "Fetching url slug for file:" << mod->fileName;
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() { QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [block, index, output]() {
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
auto json = QJsonDocument::fromJson(*output); auto json = QJsonDocument::fromJson(*output);
auto base = auto base =

View File

@ -6,7 +6,6 @@
#include "FlameModIndex.h" #include "FlameModIndex.h"
#include "Application.h" #include "Application.h"
#include "BuildConfig.h"
#include "Json.h" #include "Json.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"
#include "net/ApiUpload.h" #include "net/ApiUpload.h"
@ -131,19 +130,13 @@ auto FlameAPI::getLatestVersion(VersionSearchArgs&& args) -> ModPlatform::Indexe
auto obj = Json::requireObject(doc); auto obj = Json::requireObject(doc);
auto arr = Json::requireArray(obj, "data"); auto arr = Json::requireArray(obj, "data");
QJsonObject latest_file_obj;
ModPlatform::IndexedVersion ver_tmp;
for (auto file : arr) { for (auto file : arr) {
auto file_obj = Json::requireObject(file); auto file_obj = Json::requireObject(file);
auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj); auto file_tmp = FlameMod::loadIndexedPackVersion(file_obj);
if (file_tmp.date > ver_tmp.date) { if (file_tmp.date > ver.date && (!args.loaders.has_value() || !file_tmp.loaders || args.loaders.value() & file_tmp.loaders))
ver_tmp = file_tmp; ver = file_tmp;
latest_file_obj = file_obj;
}
} }
ver = FlameMod::loadIndexedPackVersion(latest_file_obj);
} catch (Json::JsonException& e) { } catch (Json::JsonException& e) {
qCritical() << "Failed to parse response from a version request."; qCritical() << "Failed to parse response from a version request.";
qCritical() << e.what(); qCritical() << e.what();

View File

@ -24,7 +24,10 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override; [[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (NeoForge | Forge | Fabric | Quilt); } static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
{
return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt);
}
private: private:
static int getClassId(ModPlatform::ResourceType type) static int getClassId(ModPlatform::ResourceType type)
@ -38,21 +41,42 @@ class FlameAPI : public NetworkResourceAPI {
} }
} }
static int getMappedModLoader(ModLoaderTypes loaders) static int getMappedModLoader(ModPlatform::ModLoaderType loaders)
{ {
// https://docs.curseforge.com/?http#tocS_ModLoaderType // https://docs.curseforge.com/?http#tocS_ModLoaderType
if (loaders & Forge) switch (loaders) {
return 1; case ModPlatform::Forge:
if (loaders & Fabric) return 1;
return 4; case ModPlatform::Cauldron:
// TODO: remove this once Quilt drops official Fabric support return 2;
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently* case ModPlatform::LiteLoader:
return 4; // FIXME: implement multiple loaders filter return 3;
if (loaders & NeoForge) case ModPlatform::Fabric:
return 6; return 4;
case ModPlatform::Quilt:
return 5;
case ModPlatform::NeoForge:
return 6;
}
return 0; return 0;
} }
static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList
{
QStringList l;
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt }) {
if (types & loader) {
l << QString::number(getMappedModLoader(loader));
}
}
return l;
}
static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString
{
return "[" + getModLoaderStrings(types).join(',') + "]";
}
private: private:
[[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override [[nodiscard]] std::optional<QString> getSearchURL(SearchArgs const& args) const override
{ {
@ -69,7 +93,7 @@ class FlameAPI : public NetworkResourceAPI {
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index)); get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
get_arguments.append("sortOrder=desc"); get_arguments.append("sortOrder=desc");
if (args.loaders.has_value()) if (args.loaders.has_value())
get_arguments.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value()))); get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value())));
get_arguments.append(gameVersionStr); get_arguments.append(gameVersionStr);
return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&'); return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&');
@ -83,47 +107,27 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override [[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override
{ {
auto addonId = args.pack.addonId.toString(); auto addonId = args.pack.addonId.toString();
QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(addonId) }; QString url = QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000").arg(addonId);
QStringList get_parameters;
if (args.mcVersions.has_value()) if (args.mcVersions.has_value())
get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString())); url += QString("&gameVersion=%1").arg(args.mcVersions.value().front().toString());
if (args.loaders.has_value()) { if (args.loaders.has_value() && ModPlatform::hasSingleModLoaderSelected(args.loaders.value())) {
int mappedModLoader = getMappedModLoader(args.loaders.value()); int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loaders.value())));
url += QString("&modLoaderType=%1").arg(mappedModLoader);
if (args.loaders.value() & Quilt) {
auto overide = ModPlatform::getOverrideDeps();
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) {
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt;
});
if (over != overide.cend()) {
mappedModLoader = 5;
}
}
get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader));
} }
return url;
return url + get_parameters.join('&');
}; };
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override [[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
{ {
auto mappedModLoader = getMappedModLoader(args.loader);
auto addonId = args.dependency.addonId.toString(); auto addonId = args.dependency.addonId.toString();
if (args.loader & Quilt) { auto url =
auto overide = ModPlatform::getOverrideDeps(); QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString());
auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { if (args.loader && ModPlatform::hasSingleModLoaderSelected(args.loader)) {
return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loader)));
}); url += QString("&modLoaderType=%1").arg(mappedModLoader);
if (over != overide.cend()) {
mappedModLoader = 5;
}
} }
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3") return url;
.arg(addonId)
.arg(args.mcVersion.toString())
.arg(mappedModLoader);
}; };
}; };

View File

@ -5,13 +5,11 @@
#include <MurmurHash2.h> #include <MurmurHash2.h>
#include <memory> #include <memory>
#include "FileSystem.h"
#include "Json.h" #include "Json.h"
#include "ResourceDownloadTask.h" #include "ResourceDownloadTask.h"
#include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ModFolderModel.h"
#include "minecraft/mod/ResourceFolderModel.h"
#include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "minecraft/mod/tasks/GetModDependenciesTask.h"
#include "net/ApiDownload.h" #include "net/ApiDownload.h"

View File

@ -10,7 +10,7 @@ class FlameCheckUpdate : public CheckUpdateTask {
public: public:
FlameCheckUpdate(QList<Mod*>& mods, FlameCheckUpdate(QList<Mod*>& mods,
std::list<Version>& mcVersions, std::list<Version>& mcVersions,
std::optional<ResourceAPI::ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<ModFolderModel> mods_folder) std::shared_ptr<ModFolderModel> mods_folder)
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
{} {}

View File

@ -81,6 +81,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
QVector<ModPlatform::IndexedVersion> unsortedVersions; QVector<ModPlatform::IndexedVersion> unsortedVersions;
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile(); auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
QString mcVersion = profile->getComponentVersion("net.minecraft"); QString mcVersion = profile->getComponentVersion("net.minecraft");
auto loaders = profile->getSupportedModLoaders();
for (auto versionIter : arr) { for (auto versionIter : arr) {
auto obj = versionIter.toObject(); auto obj = versionIter.toObject();
@ -89,7 +90,8 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
if (!file.addonId.isValid()) if (!file.addonId.isValid())
file.addonId = pack.addonId; file.addonId = pack.addonId;
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid if (file.fileId.isValid() &&
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
unsortedVersions.append(file); unsortedVersions.append(file);
} }
@ -115,6 +117,19 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
if (str.contains('.')) if (str.contains('.'))
file.mcVersion.append(str); file.mcVersion.append(str);
auto loader = str.toLower();
if (loader == "neoforge")
file.loaders |= ModPlatform::NeoForge;
if (loader == "forge")
file.loaders |= ModPlatform::Forge;
if (loader == "cauldron")
file.loaders |= ModPlatform::Cauldron;
if (loader == "liteloader")
file.loaders |= ModPlatform::LiteLoader;
if (loader == "fabric")
file.loaders |= ModPlatform::Fabric;
if (loader == "quilt")
file.loaders |= ModPlatform::Quilt;
} }
file.addonId = Json::requireInteger(obj, "modId"); file.addonId = Json::requireInteger(obj, "modId");
@ -173,8 +188,11 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
return file; return file;
} }
ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst)
{ {
auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
QString mcVersion = profile->getComponentVersion("net.minecraft");
auto loaders = profile->getSupportedModLoaders();
QVector<ModPlatform::IndexedVersion> versions; QVector<ModPlatform::IndexedVersion> versions;
for (auto versionIter : arr) { for (auto versionIter : arr) {
auto obj = versionIter.toObject(); auto obj = versionIter.toObject();
@ -183,7 +201,8 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
if (!file.addonId.isValid()) if (!file.addonId.isValid())
file.addonId = m.addonId; file.addonId = m.addonId;
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid if (file.fileId.isValid() &&
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
versions.append(file); versions.append(file);
} }
@ -192,5 +211,7 @@ ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::
return a.date > b.date; return a.date > b.date;
}; };
std::sort(versions.begin(), versions.end(), orderSortPredicate); std::sort(versions.begin(), versions.end(), orderSortPredicate);
return versions.front(); if (versions.size() != 0)
return versions.front();
return {};
} }

View File

@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
const shared_qobject_ptr<QNetworkAccessManager>& network, const shared_qobject_ptr<QNetworkAccessManager>& network,
const BaseInstance* inst); const BaseInstance* inst);
auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion; auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion;
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion; auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion;
} // namespace FlameMod } // namespace FlameMod

View File

@ -131,7 +131,7 @@ Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args,
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
auto response = std::make_shared<QByteArray>(); auto response = std::make_shared<QByteArray>();
netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); netJob->addNetAction(Net::ApiDownload::makeByteArray(versions_url, response));
QObject::connect(netJob.get(), &NetJob::succeeded, [=] { QObject::connect(netJob.get(), &NetJob::succeeded, [=] {
QJsonParseError parse_error{}; QJsonParseError parse_error{};

View File

@ -60,19 +60,19 @@ Modpack parseDirectory(QString path)
auto name = Json::requireString(obj, "name", "name"); auto name = Json::requireString(obj, "name", "name");
auto version = Json::requireString(obj, "version", "version"); auto version = Json::requireString(obj, "version", "version");
if (name == "neoforge") { if (name == "neoforge") {
modpack.loaderType = ResourceAPI::NeoForge; modpack.loaderType = ModPlatform::NeoForge;
modpack.version = version; modpack.version = version;
break; break;
} else if (name == "forge") { } else if (name == "forge") {
modpack.loaderType = ResourceAPI::Forge; modpack.loaderType = ModPlatform::Forge;
modpack.version = version; modpack.version = version;
break; break;
} else if (name == "fabric") { } else if (name == "fabric") {
modpack.loaderType = ResourceAPI::Fabric; modpack.loaderType = ModPlatform::Fabric;
modpack.version = version; modpack.version = version;
break; break;
} else if (name == "quilt") { } else if (name == "quilt") {
modpack.loaderType = ResourceAPI::Quilt; modpack.loaderType = ModPlatform::Quilt;
modpack.version = version; modpack.version = version;
break; break;
} }

View File

@ -39,7 +39,7 @@ struct Modpack {
// not needed for instance creation // not needed for instance creation
QVariant jvmArgs; QVariant jvmArgs;
std::optional<ResourceAPI::ModLoaderType> loaderType; std::optional<ModPlatform::ModLoaderType> loaderType;
QString loaderVersion; QString loaderVersion;
QIcon icon; QIcon icon;

View File

@ -68,25 +68,25 @@ void PackInstallTask::copySettings()
auto modloader = m_pack.loaderType; auto modloader = m_pack.loaderType;
if (modloader.has_value()) if (modloader.has_value())
switch (modloader.value()) { switch (modloader.value()) {
case ResourceAPI::NeoForge: { case ModPlatform::NeoForge: {
components->setComponentVersion("net.neoforged", m_pack.version, true); components->setComponentVersion("net.neoforged", m_pack.version, true);
break; break;
} }
case ResourceAPI::Forge: { case ModPlatform::Forge: {
components->setComponentVersion("net.minecraftforge", m_pack.version, true); components->setComponentVersion("net.minecraftforge", m_pack.version, true);
break; break;
} }
case ResourceAPI::Fabric: { case ModPlatform::Fabric: {
components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true); components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
break; break;
} }
case ResourceAPI::Quilt: { case ModPlatform::Quilt: {
components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true); components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true);
break; break;
} }
case ResourceAPI::Cauldron: case ModPlatform::Cauldron:
break; break;
case ResourceAPI::LiteLoader: case ModPlatform::LiteLoader:
break; break;
} }
components->saveNow(); components->saveNow();

View File

@ -41,7 +41,7 @@ Task::Ptr ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_f
Task::Ptr ModrinthAPI::latestVersion(QString hash, Task::Ptr ModrinthAPI::latestVersion(QString hash,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<QByteArray> response) std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
@ -71,7 +71,7 @@ Task::Ptr ModrinthAPI::latestVersion(QString hash,
Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes, Task::Ptr ModrinthAPI::latestVersions(const QStringList& hashes,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<QByteArray> response) std::shared_ptr<QByteArray> response)
{ {
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());

View File

@ -19,13 +19,13 @@ class ModrinthAPI : public NetworkResourceAPI {
auto latestVersion(QString hash, auto latestVersion(QString hash,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<QByteArray> response) -> Task::Ptr; std::shared_ptr<QByteArray> response) -> Task::Ptr;
auto latestVersions(const QStringList& hashes, auto latestVersions(const QStringList& hashes,
QString hash_format, QString hash_format,
std::optional<std::list<Version>> mcVersions, std::optional<std::list<Version>> mcVersions,
std::optional<ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<QByteArray> response) -> Task::Ptr; std::shared_ptr<QByteArray> response) -> Task::Ptr;
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override; Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
@ -35,20 +35,19 @@ class ModrinthAPI : public NetworkResourceAPI {
inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; }; inline auto getAuthorURL(const QString& name) const -> QString { return "https://modrinth.com/user/" + name; };
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> const QStringList
{ {
QStringList l; QStringList l;
for (auto loader : { NeoForge, Forge, Fabric, Quilt, LiteLoader }) { for (auto loader :
{ ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) {
if (types & loader) { if (types & loader) {
l << getModLoaderString(loader); l << getModLoaderString(loader);
} }
} }
if ((types & Quilt) && (~types & Fabric)) // Add Fabric if Quilt is in use, if Fabric isn't already there
l << getModLoaderString(Fabric);
return l; return l;
} }
static auto getModLoaderFilters(ModLoaderTypes types) -> const QString static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> const QString
{ {
QStringList l; QStringList l;
for (auto loader : getModLoaderStrings(types)) { for (auto loader : getModLoaderStrings(types)) {
@ -141,9 +140,9 @@ class ModrinthAPI : public NetworkResourceAPI {
return s.isEmpty() ? QString() : s; return s.isEmpty() ? QString() : s;
} }
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool
{ {
return loaders & (NeoForge | Forge | Fabric | Quilt | LiteLoader); return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt | ModPlatform::LiteLoader);
} }
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override [[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override

View File

@ -11,7 +11,6 @@
#include "tasks/ConcurrentTask.h" #include "tasks/ConcurrentTask.h"
#include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ModFolderModel.h"
#include "minecraft/mod/ResourceFolderModel.h"
static ModrinthAPI api; static ModrinthAPI api;
static ModPlatform::ProviderCapabilities ProviderCaps; static ModPlatform::ProviderCapabilities ProviderCaps;
@ -111,11 +110,11 @@ void ModrinthCheckUpdate::executeTask()
// so we may want to filter it // so we may want to filter it
QString loader_filter; QString loader_filter;
if (m_loaders.has_value()) { if (m_loaders.has_value()) {
static auto flags = { ResourceAPI::ModLoaderType::NeoForge, ResourceAPI::ModLoaderType::Forge, static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge,
ResourceAPI::ModLoaderType::Fabric, ResourceAPI::ModLoaderType::Quilt }; ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt };
for (auto flag : flags) { for (auto flag : flags) {
if (m_loaders.value().testFlag(flag)) { if (m_loaders.value().testFlag(flag)) {
loader_filter = api.getModLoaderString(flag); loader_filter = ModPlatform::getModLoaderString(flag);
break; break;
} }
} }

View File

@ -10,7 +10,7 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
public: public:
ModrinthCheckUpdate(QList<Mod*>& mods, ModrinthCheckUpdate(QList<Mod*>& mods,
std::list<Version>& mcVersions, std::list<Version>& mcVersions,
std::optional<ResourceAPI::ModLoaderTypes> loaders, std::optional<ModPlatform::ModLoaderTypes> loaders,
std::shared_ptr<ModFolderModel> mods_folder) std::shared_ptr<ModFolderModel> mods_folder)
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder) : CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
{} {}

View File

@ -93,19 +93,19 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
pack.extraDataLoaded = true; pack.extraDataLoaded = true;
} }
void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst)
QJsonArray& arr,
[[maybe_unused]] const shared_qobject_ptr<QNetworkAccessManager>& network,
const BaseInstance* inst)
{ {
QVector<ModPlatform::IndexedVersion> unsortedVersions; QVector<ModPlatform::IndexedVersion> unsortedVersions;
QString mcVersion = (static_cast<const MinecraftInstance*>(inst))->getPackProfile()->getComponentVersion("net.minecraft"); auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
QString mcVersion = profile->getComponentVersion("net.minecraft");
auto loaders = profile->getSupportedModLoaders();
for (auto versionIter : arr) { for (auto versionIter : arr) {
auto obj = versionIter.toObject(); auto obj = versionIter.toObject();
auto file = loadIndexedPackVersion(obj); auto file = loadIndexedPackVersion(obj);
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid if (file.fileId.isValid() &&
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
unsortedVersions.append(file); unsortedVersions.append(file);
} }
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
@ -134,7 +134,18 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
} }
auto loaders = Json::requireArray(obj, "loaders"); auto loaders = Json::requireArray(obj, "loaders");
for (auto loader : loaders) { for (auto loader : loaders) {
file.loaders.append(loader.toString()); if (loader == "neoforge")
file.loaders |= ModPlatform::NeoForge;
if (loader == "forge")
file.loaders |= ModPlatform::Forge;
if (loader == "cauldron")
file.loaders |= ModPlatform::Cauldron;
if (loader == "liteloader")
file.loaders |= ModPlatform::LiteLoader;
if (loader == "fabric")
file.loaders |= ModPlatform::Fabric;
if (loader == "quilt")
file.loaders |= ModPlatform::Quilt;
} }
file.version = Json::requireString(obj, "name"); file.version = Json::requireString(obj, "name");
file.version_number = Json::requireString(obj, "version_number"); file.version_number = Json::requireString(obj, "version_number");
@ -218,15 +229,20 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t
return {}; return {};
} }
auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion auto Modrinth::loadDependencyVersions([[maybe_unused]] const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst)
-> ModPlatform::IndexedVersion
{ {
QVector<ModPlatform::IndexedVersion> versions; auto profile = (dynamic_cast<const MinecraftInstance*>(inst))->getPackProfile();
QString mcVersion = profile->getComponentVersion("net.minecraft");
auto loaders = profile->getSupportedModLoaders();
QVector<ModPlatform::IndexedVersion> versions;
for (auto versionIter : arr) { for (auto versionIter : arr) {
auto obj = versionIter.toObject(); auto obj = versionIter.toObject();
auto file = loadIndexedPackVersion(obj); auto file = loadIndexedPackVersion(obj);
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid if (file.fileId.isValid() &&
(!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid
versions.append(file); versions.append(file);
} }
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {

View File

@ -26,11 +26,8 @@ namespace Modrinth {
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj); void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj); void loadExtraPackData(ModPlatform::IndexedPack& m, QJsonObject& obj);
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst);
QJsonArray& arr,
const shared_qobject_ptr<QNetworkAccessManager>& network,
const BaseInstance* inst);
auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion; auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion;
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion; auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr, const BaseInstance* inst) -> ModPlatform::IndexedVersion;
} // namespace Modrinth } // namespace Modrinth

View File

@ -218,9 +218,24 @@ void HttpMetaCache::Load()
if (!index.open(QIODevice::ReadOnly)) if (!index.open(QIODevice::ReadOnly))
return; return;
QJsonDocument json = QJsonDocument::fromJson(index.readAll()); QJsonParseError parseError;
QJsonDocument json = QJsonDocument::fromJson(index.readAll(), &parseError);
auto root = Json::requireObject(json, "HttpMetaCache root"); // Fail if the JSON is invalid.
if (parseError.error != QJsonParseError::NoError) {
qCritical() << QString("Failed to parse HttpMetaCache file: %1 at offset %2")
.arg(parseError.errorString(), QString::number(parseError.offset))
.toUtf8();
return;
}
// Make sure the root is an object.
if (!json.isObject()) {
qCritical() << "HttpMetaCache root should be an object.";
return;
}
auto root = json.object();
// check file version first // check file version first
auto version_val = Json::ensureString(root, "version"); auto version_val = Json::ensureString(root, "version");

View File

@ -44,8 +44,6 @@
#include <QPushButton> #include <QPushButton>
#include <QScrollBar> #include <QScrollBar>
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ProgressDialog.h"
#include "ui/widgets/PageContainer.h" #include "ui/widgets/PageContainer.h"
#include "InstancePageProvider.h" #include "InstancePageProvider.h"
@ -76,40 +74,44 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget* parent) : QMainWin
// Add custom buttons to the page container layout. // Add custom buttons to the page container layout.
{ {
auto horizontalLayout = new QHBoxLayout(); auto horizontalLayout = new QHBoxLayout(this);
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout")); horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
horizontalLayout->setContentsMargins(6, -1, 6, -1); horizontalLayout->setContentsMargins(6, -1, 6, -1);
auto btnHelp = new QPushButton(); auto btnHelp = new QPushButton(this);
btnHelp->setText(tr("Help")); btnHelp->setText(tr("Help"));
horizontalLayout->addWidget(btnHelp); horizontalLayout->addWidget(btnHelp);
connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help())); connect(btnHelp, &QPushButton::clicked, m_container, &PageContainer::help);
auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout->addSpacerItem(spacer); horizontalLayout->addSpacerItem(spacer);
m_killButton = new QPushButton(); m_launchButton = new QToolButton(this);
m_launchButton->setText(tr("&Launch"));
m_launchButton->setToolTip(tr("Launch the instance"));
m_launchButton->setPopupMode(QToolButton::MenuButtonPopup);
m_launchButton->setMinimumWidth(80); // HACK!!
horizontalLayout->addWidget(m_launchButton);
connect(m_launchButton, &QPushButton::clicked, this, [this] { APPLICATION->launch(m_instance); });
m_killButton = new QPushButton(this);
m_killButton->setText(tr("&Kill"));
m_killButton->setToolTip(tr("Kill the running instance"));
m_killButton->setShortcut(QKeySequence(tr("Ctrl+K")));
horizontalLayout->addWidget(m_killButton); horizontalLayout->addWidget(m_killButton);
connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked())); connect(m_killButton, &QPushButton::clicked, this, [this] { APPLICATION->kill(m_instance); });
m_launchOfflineButton = new QPushButton(); updateButtons();
horizontalLayout->addWidget(m_launchOfflineButton);
m_launchOfflineButton->setText(tr("Launch Offline"));
m_launchDemoButton = new QPushButton(); m_closeButton = new QPushButton(this);
horizontalLayout->addWidget(m_launchDemoButton);
m_launchDemoButton->setText(tr("Launch Demo"));
updateLaunchButtons();
connect(m_launchOfflineButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftOffline_clicked()));
connect(m_launchDemoButton, SIGNAL(clicked(bool)), SLOT(on_btnLaunchMinecraftDemo_clicked()));
m_closeButton = new QPushButton();
m_closeButton->setText(tr("Close")); m_closeButton->setText(tr("Close"));
horizontalLayout->addWidget(m_closeButton); horizontalLayout->addWidget(m_closeButton);
connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked())); connect(m_closeButton, &QPushButton::clicked, this, &QMainWindow::close);
m_container->addButtons(horizontalLayout); m_container->addButtons(horizontalLayout);
connect(m_instance.get(), &BaseInstance::profilerChanged, this, &InstanceWindow::updateButtons);
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceWindow::updateButtons);
} }
// restore window state // restore window state
@ -149,47 +151,18 @@ void InstanceWindow::on_instanceStatusChanged(BaseInstance::Status, BaseInstance
} }
} }
void InstanceWindow::updateLaunchButtons() void InstanceWindow::updateButtons()
{ {
if (m_instance->isRunning()) { m_launchButton->setEnabled(m_instance->canLaunch());
m_launchOfflineButton->setEnabled(false); m_killButton->setEnabled(m_instance->isRunning());
m_launchDemoButton->setEnabled(false);
m_killButton->setText(tr("Kill"));
m_killButton->setObjectName("killButton");
m_killButton->setToolTip(tr("Kill the running instance"));
} else if (!m_instance->canLaunch()) {
m_launchOfflineButton->setEnabled(false);
m_launchDemoButton->setEnabled(false);
m_killButton->setText(tr("Launch"));
m_killButton->setObjectName("launchButton");
m_killButton->setToolTip(tr("Launch the instance"));
m_killButton->setEnabled(false);
} else {
m_launchOfflineButton->setEnabled(true);
// Disable demo-mode if not available. QMenu* launchMenu = m_launchButton->menu();
auto instance = dynamic_cast<MinecraftInstance*>(m_instance.get()); if (launchMenu)
if (instance) { launchMenu->clear();
m_launchDemoButton->setEnabled(instance->supportsDemo()); else
} launchMenu = new QMenu(this);
m_instance->populateLaunchMenu(launchMenu);
m_killButton->setText(tr("Launch")); m_launchButton->setMenu(launchMenu);
m_killButton->setObjectName("launchButton");
m_killButton->setToolTip(tr("Launch the instance"));
}
// NOTE: this is a hack to force the button to recalculate its style
m_killButton->setStyleSheet("/* */");
m_killButton->setStyleSheet(QString());
}
void InstanceWindow::on_btnLaunchMinecraftOffline_clicked()
{
APPLICATION->launch(m_instance, false, false, nullptr);
}
void InstanceWindow::on_btnLaunchMinecraftDemo_clicked()
{
APPLICATION->launch(m_instance, false, true, nullptr);
} }
void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc) void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc)
@ -199,18 +172,13 @@ void InstanceWindow::instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> pr
void InstanceWindow::runningStateChanged(bool running) void InstanceWindow::runningStateChanged(bool running)
{ {
updateLaunchButtons(); updateButtons();
m_container->refreshContainer(); m_container->refreshContainer();
if (running) { if (running) {
selectPage("log"); selectPage("log");
} }
} }
void InstanceWindow::on_closeButton_clicked()
{
close();
}
void InstanceWindow::closeEvent(QCloseEvent* event) void InstanceWindow::closeEvent(QCloseEvent* event)
{ {
bool proceed = true; bool proceed = true;
@ -233,15 +201,6 @@ bool InstanceWindow::saveAll()
return m_container->saveAll(); return m_container->saveAll();
} }
void InstanceWindow::on_btnKillMinecraft_clicked()
{
if (m_instance->isRunning()) {
APPLICATION->kill(m_instance);
} else {
APPLICATION->launch(m_instance, true, false, nullptr);
}
}
QString InstanceWindow::instanceId() QString InstanceWindow::instanceId()
{ {
return m_instance->id(); return m_instance->id();
@ -252,17 +211,15 @@ bool InstanceWindow::selectPage(QString pageId)
return m_container->selectPage(pageId); return m_container->selectPage(pageId);
} }
BasePage* InstanceWindow::selectedPage() const
{
return m_container->selectedPage();
}
void InstanceWindow::refreshContainer() void InstanceWindow::refreshContainer()
{ {
m_container->refreshContainer(); m_container->refreshContainer();
} }
InstanceWindow::~InstanceWindow() {} BasePage* InstanceWindow::selectedPage() const
{
return m_container->selectedPage();
}
bool InstanceWindow::requestClose() bool InstanceWindow::requestClose()
{ {

View File

@ -38,6 +38,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QToolButton>
#include "LaunchController.h" #include "LaunchController.h"
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
@ -53,7 +54,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
public: public:
explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0); explicit InstanceWindow(InstancePtr proc, QWidget* parent = 0);
virtual ~InstanceWindow(); virtual ~InstanceWindow() = default;
bool selectPage(QString pageId) override; bool selectPage(QString pageId) override;
BasePage* selectedPage() const override; BasePage* selectedPage() const override;
@ -71,11 +72,6 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
void isClosing(); void isClosing();
private slots: private slots:
void on_closeButton_clicked();
void on_btnKillMinecraft_clicked();
void on_btnLaunchMinecraftOffline_clicked();
void on_btnLaunchMinecraftDemo_clicked();
void instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc); void instanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc);
void runningStateChanged(bool running); void runningStateChanged(bool running);
void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus); void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus);
@ -84,7 +80,7 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
void closeEvent(QCloseEvent*) override; void closeEvent(QCloseEvent*) override;
private: private:
void updateLaunchButtons(); void updateButtons();
private: private:
shared_qobject_ptr<LaunchTask> m_proc; shared_qobject_ptr<LaunchTask> m_proc;
@ -92,7 +88,6 @@ class InstanceWindow : public QMainWindow, public BasePageContainer {
bool m_doNotSave = false; bool m_doNotSave = false;
PageContainer* m_container = nullptr; PageContainer* m_container = nullptr;
QPushButton* m_closeButton = nullptr; QPushButton* m_closeButton = nullptr;
QToolButton* m_launchButton = nullptr;
QPushButton* m_killButton = nullptr; QPushButton* m_killButton = nullptr;
QPushButton* m_launchOfflineButton = nullptr;
QPushButton* m_launchDemoButton = nullptr;
}; };

View File

@ -43,7 +43,6 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "ui/dialogs/ExportToModListDialog.h"
#include "ui_MainWindow.h" #include "ui_MainWindow.h"
#include <QDir> #include <QDir>
@ -90,17 +89,14 @@
#include <news/NewsChecker.h> #include <news/NewsChecker.h>
#include <tools/BaseProfiler.h> #include <tools/BaseProfiler.h>
#include <updater/ExternalUpdater.h> #include <updater/ExternalUpdater.h>
#include "InstancePageProvider.h"
#include "InstanceWindow.h" #include "InstanceWindow.h"
#include "JavaCommon.h"
#include "LaunchController.h"
#include "ui/dialogs/AboutDialog.h" #include "ui/dialogs/AboutDialog.h"
#include "ui/dialogs/CopyInstanceDialog.h" #include "ui/dialogs/CopyInstanceDialog.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/EditAccountDialog.h"
#include "ui/dialogs/ExportInstanceDialog.h" #include "ui/dialogs/ExportInstanceDialog.h"
#include "ui/dialogs/ExportPackDialog.h" #include "ui/dialogs/ExportPackDialog.h"
#include "ui/dialogs/ExportToModListDialog.h"
#include "ui/dialogs/IconPickerDialog.h" #include "ui/dialogs/IconPickerDialog.h"
#include "ui/dialogs/ImportResourceDialog.h" #include "ui/dialogs/ImportResourceDialog.h"
#include "ui/dialogs/NewInstanceDialog.h" #include "ui/dialogs/NewInstanceDialog.h"
@ -113,17 +109,22 @@
#include "ui/themes/ThemeManager.h" #include "ui/themes/ThemeManager.h"
#include "ui/widgets/LabeledToolButton.h" #include "ui/widgets/LabeledToolButton.h"
#include "minecraft/PackProfile.h"
#include "minecraft/VersionFile.h"
#include "minecraft/WorldList.h" #include "minecraft/WorldList.h"
#include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ModFolderModel.h"
#include "minecraft/mod/ResourcePackFolderModel.h"
#include "minecraft/mod/ShaderPackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h"
#include "minecraft/mod/TexturePackFolderModel.h"
#include "minecraft/mod/tasks/LocalResourceParse.h" #include "minecraft/mod/tasks/LocalResourceParse.h"
#include "modplatform/ModIndex.h"
#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/FlameModIndex.h"
#include "KonamiCode.h" #include "KonamiCode.h"
#include "InstanceCopyTask.h" #include "InstanceCopyTask.h"
#include "InstanceImportTask.h"
#include "Json.h" #include "Json.h"
@ -553,71 +554,15 @@ void MainWindow::updateMainToolBar()
ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool()); ui->mainToolBar->setVisible(ui->menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
} }
void MainWindow::updateToolsMenu() void MainWindow::updateLaunchButton()
{ {
bool currentInstanceRunning = m_selectedInstance && m_selectedInstance->isRunning();
ui->actionLaunchInstance->setDisabled(!m_selectedInstance || currentInstanceRunning);
ui->actionLaunchInstanceOffline->setDisabled(!m_selectedInstance || currentInstanceRunning);
ui->actionLaunchInstanceDemo->setDisabled(!m_selectedInstance || currentInstanceRunning);
QMenu* launchMenu = ui->actionLaunchInstance->menu(); QMenu* launchMenu = ui->actionLaunchInstance->menu();
if (launchMenu) { if (launchMenu)
launchMenu->clear(); launchMenu->clear();
} else { else
launchMenu = new QMenu(this); launchMenu = new QMenu(this);
} if (m_selectedInstance)
QAction* normalLaunch = launchMenu->addAction(tr("Launch")); m_selectedInstance->populateLaunchMenu(launchMenu);
normalLaunch->setShortcut(QKeySequence::Open);
QAction* normalLaunchOffline = launchMenu->addAction(tr("Launch Offline"));
normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O")));
QAction* normalLaunchDemo = launchMenu->addAction(tr("Launch Demo"));
normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O")));
if (m_selectedInstance) {
normalLaunch->setEnabled(m_selectedInstance->canLaunch());
normalLaunchOffline->setEnabled(m_selectedInstance->canLaunch());
normalLaunchDemo->setEnabled(m_selectedInstance->canLaunch());
connect(normalLaunch, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, true, false); });
connect(normalLaunchOffline, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, false, false); });
connect(normalLaunchDemo, &QAction::triggered, [this]() { APPLICATION->launch(m_selectedInstance, false, true); });
} else {
normalLaunch->setDisabled(true);
normalLaunchOffline->setDisabled(true);
normalLaunchDemo->setDisabled(true);
}
// Disable demo-mode if not available.
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
if (instance) {
normalLaunchDemo->setEnabled(instance->supportsDemo());
}
QString profilersTitle = tr("Profilers");
launchMenu->addSeparator()->setText(profilersTitle);
for (auto profiler : APPLICATION->profilers().values()) {
QAction* profilerAction = launchMenu->addAction(profiler->name());
QAction* profilerOfflineAction = launchMenu->addAction(tr("%1 Offline").arg(profiler->name()));
QString error;
if (!profiler->check(&error)) {
profilerAction->setDisabled(true);
profilerOfflineAction->setDisabled(true);
QString profilerToolTip = tr("Profiler not setup correctly. Go into settings, \"External Tools\".");
profilerAction->setToolTip(profilerToolTip);
profilerOfflineAction->setToolTip(profilerToolTip);
} else if (m_selectedInstance) {
profilerAction->setEnabled(m_selectedInstance->canLaunch());
profilerOfflineAction->setEnabled(m_selectedInstance->canLaunch());
connect(profilerAction, &QAction::triggered,
[this, profiler]() { APPLICATION->launch(m_selectedInstance, true, false, profiler.get()); });
connect(profilerOfflineAction, &QAction::triggered,
[this, profiler]() { APPLICATION->launch(m_selectedInstance, false, false, profiler.get()); });
} else {
profilerAction->setDisabled(true);
profilerOfflineAction->setDisabled(true);
}
}
ui->actionLaunchInstance->setMenu(launchMenu); ui->actionLaunchInstance->setMenu(launchMenu);
} }
@ -927,7 +872,7 @@ void MainWindow::finalizeInstance(InstancePtr inst)
} else { } else {
CustomMessageBox::selectable(this, tr("Error"), CustomMessageBox::selectable(this, tr("Error"),
tr("The launcher cannot download Minecraft or update instances unless you have at least " tr("The launcher cannot download Minecraft or update instances unless you have at least "
"one account added.\nPlease add your Mojang or Minecraft account."), "one account added.\nPlease add your Microsoft or Mojang account."),
QMessageBox::Warning) QMessageBox::Warning)
->show(); ->show();
} }
@ -980,6 +925,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
if (url.scheme().isEmpty()) if (url.scheme().isEmpty())
url.setScheme("file"); url.setScheme("file");
ModPlatform::IndexedVersion version;
QMap<QString, QString> extra_info; QMap<QString, QString> extra_info;
QUrl local_url; QUrl local_url;
if (!url.isLocalFile()) { // download the remote resource and identify if (!url.isLocalFile()) { // download the remote resource and identify
@ -989,6 +935,11 @@ void MainWindow::processURLs(QList<QUrl> urls)
// format of url curseforge://install?addonId=IDHERE&fileId=IDHERE // format of url curseforge://install?addonId=IDHERE&fileId=IDHERE
QUrlQuery query(url); QUrlQuery query(url);
if (query.allQueryItemValues("addonId").isEmpty() || query.allQueryItemValues("fileId").isEmpty()) {
qDebug() << "Invalid curseforge link:" << url;
continue;
}
auto addonId = query.allQueryItemValues("addonId")[0]; auto addonId = query.allQueryItemValues("addonId")[0];
auto fileId = query.allQueryItemValues("fileId")[0]; auto fileId = query.allQueryItemValues("fileId")[0];
@ -1000,20 +951,19 @@ void MainWindow::processURLs(QList<QUrl> urls)
auto api = FlameAPI(); auto api = FlameAPI();
auto job = api.getFile(addonId, fileId, array); auto job = api.getFile(addonId, fileId, array);
QString resource_name;
connect(job.get(), &Task::failed, this, connect(job.get(), &Task::failed, this,
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &resource_name] { connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &version] {
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str(); qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
auto doc = Json::requireDocument(*array); auto doc = Json::requireDocument(*array);
auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data"); auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
// No way to find out if it's a mod or a modpack before here // No way to find out if it's a mod or a modpack before here
// And also we need to check if it ends with .zip, instead of any better way // And also we need to check if it ends with .zip, instead of any better way
auto fileName = Json::ensureString(data, "fileName"); version = FlameMod::loadIndexedPackVersion(data);
auto fileName = version.fileName;
// Have to use ensureString then use QUrl to get proper url encoding // Have to use ensureString then use QUrl to get proper url encoding
dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl")); dl_url = QUrl(version.downloadUrl);
if (!dl_url.isValid()) { if (!dl_url.isValid()) {
CustomMessageBox::selectable( CustomMessageBox::selectable(
this, tr("Error"), this, tr("Error"),
@ -1024,7 +974,6 @@ void MainWindow::processURLs(QList<QUrl> urls)
} }
QFileInfo dl_file(dl_url.fileName()); QFileInfo dl_file(dl_url.fileName());
resource_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
}); });
{ // drop stack { // drop stack
@ -1099,7 +1048,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName; qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName;
break; break;
case PackedResourceType::Mod: case PackedResourceType::Mod:
minecraftInst->loaderModList()->installMod(localFileName); minecraftInst->loaderModList()->installMod(localFileName, version);
break; break;
case PackedResourceType::ShaderPack: case PackedResourceType::ShaderPack:
minecraftInst->shaderPackList()->installResource(localFileName); minecraftInst->shaderPackList()->installResource(localFileName);
@ -1278,7 +1227,7 @@ void MainWindow::globalSettingsClosed()
proxymodel->invalidate(); proxymodel->invalidate();
proxymodel->sort(0); proxymodel->sort(0);
updateMainToolBar(); updateMainToolBar();
updateToolsMenu(); updateLaunchButton();
updateThemeMenu(); updateThemeMenu();
updateStatusCenter(); updateStatusCenter();
// This needs to be done to prevent UI elements disappearing in the event the config is changed // This needs to be done to prevent UI elements disappearing in the event the config is changed
@ -1408,10 +1357,11 @@ void MainWindow::on_actionDeleteInstance_triggered()
if (APPLICATION->instances()->trashInstance(id)) { if (APPLICATION->instances()->trashInstance(id)) {
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething()); ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
return; } else {
APPLICATION->instances()->deleteInstance(id);
} }
APPLICATION->settings()->set("SelectedInstance", QString());
APPLICATION->instances()->deleteInstance(id); selectionBad();
} }
void MainWindow::on_actionExportInstanceZip_triggered() void MainWindow::on_actionExportInstanceZip_triggered()
@ -1513,20 +1463,6 @@ void MainWindow::activateInstance(InstancePtr instance)
APPLICATION->launch(instance); APPLICATION->launch(instance);
} }
void MainWindow::on_actionLaunchInstanceOffline_triggered()
{
if (m_selectedInstance) {
APPLICATION->launch(m_selectedInstance, false);
}
}
void MainWindow::on_actionLaunchInstanceDemo_triggered()
{
if (m_selectedInstance) {
APPLICATION->launch(m_selectedInstance, false, true);
}
}
void MainWindow::on_actionKillInstance_triggered() void MainWindow::on_actionKillInstance_triggered()
{ {
if (m_selectedInstance && m_selectedInstance->isRunning()) { if (m_selectedInstance && m_selectedInstance->isRunning()) {
@ -1700,6 +1636,7 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
} }
if (m_selectedInstance) { if (m_selectedInstance) {
disconnect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance); disconnect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
disconnect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance);
} }
QString id = current.data(InstanceList::InstanceIDRole).toString(); QString id = current.data(InstanceList::InstanceIDRole).toString();
m_selectedInstance = APPLICATION->instances()->getInstanceById(id); m_selectedInstance = APPLICATION->instances()->getInstanceById(id);
@ -1707,14 +1644,6 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
ui->instanceToolBar->setEnabled(true); ui->instanceToolBar->setEnabled(true);
setInstanceActionsEnabled(true); setInstanceActionsEnabled(true);
ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch()); ui->actionLaunchInstance->setEnabled(m_selectedInstance->canLaunch());
ui->actionLaunchInstanceOffline->setEnabled(m_selectedInstance->canLaunch());
ui->actionLaunchInstanceDemo->setEnabled(m_selectedInstance->canLaunch());
// Disable demo-mode if not available.
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
if (instance) {
ui->actionLaunchInstanceDemo->setEnabled(instance->supportsDemo());
}
ui->actionKillInstance->setEnabled(m_selectedInstance->isRunning()); ui->actionKillInstance->setEnabled(m_selectedInstance->isRunning());
ui->actionExportInstance->setEnabled(m_selectedInstance->canExport()); ui->actionExportInstance->setEnabled(m_selectedInstance->canExport());
@ -1723,18 +1652,13 @@ void MainWindow::instanceChanged(const QModelIndex& current, [[maybe_unused]] co
updateStatusCenter(); updateStatusCenter();
updateInstanceToolIcon(m_selectedInstance->iconKey()); updateInstanceToolIcon(m_selectedInstance->iconKey());
updateToolsMenu(); updateLaunchButton();
APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id()); APPLICATION->settings()->set("SelectedInstance", m_selectedInstance->id());
connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance); connect(m_selectedInstance.get(), &BaseInstance::runningStatusChanged, this, &MainWindow::refreshCurrentInstance);
connect(m_selectedInstance.get(), &BaseInstance::profilerChanged, this, &MainWindow::refreshCurrentInstance);
} else { } else {
ui->instanceToolBar->setEnabled(false);
setInstanceActionsEnabled(false);
ui->actionLaunchInstance->setEnabled(false);
ui->actionLaunchInstanceOffline->setEnabled(false);
ui->actionLaunchInstanceDemo->setEnabled(false);
ui->actionKillInstance->setEnabled(false);
APPLICATION->settings()->set("SelectedInstance", QString()); APPLICATION->settings()->set("SelectedInstance", QString());
selectionBad(); selectionBad();
return; return;
@ -1759,11 +1683,12 @@ void MainWindow::selectionBad()
{ {
// start by reseting everything... // start by reseting everything...
m_selectedInstance = nullptr; m_selectedInstance = nullptr;
m_statusLeft->setText(tr("No instance selected"));
statusBar()->clearMessage(); statusBar()->clearMessage();
ui->instanceToolBar->setEnabled(false); ui->instanceToolBar->setEnabled(false);
setInstanceActionsEnabled(false); setInstanceActionsEnabled(false);
updateToolsMenu(); updateLaunchButton();
renameButton->setText(tr("Rename Instance")); renameButton->setText(tr("Rename Instance"));
updateInstanceToolIcon("grass"); updateInstanceToolIcon("grass");
@ -1810,7 +1735,9 @@ void MainWindow::updateStatusCenter()
int timePlayed = APPLICATION->instances()->getTotalPlayTime(); int timePlayed = APPLICATION->instances()->getTotalPlayTime();
if (timePlayed > 0) { if (timePlayed > 0) {
m_statusCenter->setText(tr("Total playtime: %1").arg(Time::prettifyDuration(timePlayed))); m_statusCenter->setText(
tr("Total playtime: %1")
.arg(Time::prettifyDuration(timePlayed, APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool())));
} }
} }
// "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here) // "Instance actions" are actions that require an instance to be selected (i.e. "new instance" is not here)
@ -1826,7 +1753,7 @@ void MainWindow::setInstanceActionsEnabled(bool enabled)
ui->actionCreateInstanceShortcut->setEnabled(enabled); ui->actionCreateInstanceShortcut->setEnabled(enabled);
} }
void MainWindow::refreshCurrentInstance([[maybe_unused]] bool running) void MainWindow::refreshCurrentInstance()
{ {
auto current = view->selectionModel()->currentIndex(); auto current = view->selectionModel()->currentIndex();
instanceChanged(current, current); instanceChanged(current, current);

View File

@ -144,10 +144,6 @@ class MainWindow : public QMainWindow {
void on_actionLaunchInstance_triggered(); void on_actionLaunchInstance_triggered();
void on_actionLaunchInstanceOffline_triggered();
void on_actionLaunchInstanceDemo_triggered();
void on_actionKillInstance_triggered(); void on_actionKillInstance_triggered();
void on_actionDeleteInstance_triggered(); void on_actionDeleteInstance_triggered();
@ -155,10 +151,7 @@ class MainWindow : public QMainWindow {
void deleteGroup(); void deleteGroup();
void undoTrashInstance(); void undoTrashInstance();
inline void on_actionExportInstance_triggered() inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); }
{
on_actionExportInstanceZip_triggered();
}
void on_actionExportInstanceZip_triggered(); void on_actionExportInstanceZip_triggered();
void on_actionExportInstanceMrPack_triggered(); void on_actionExportInstanceMrPack_triggered();
void on_actionExportInstanceFlamePack_triggered(); void on_actionExportInstanceFlamePack_triggered();
@ -181,7 +174,7 @@ class MainWindow : public QMainWindow {
void updateMainToolBar(); void updateMainToolBar();
void updateToolsMenu(); void updateLaunchButton();
void updateThemeMenu(); void updateThemeMenu();
@ -215,7 +208,7 @@ class MainWindow : public QMainWindow {
void keyReleaseEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override;
#endif #endif
void refreshCurrentInstance(bool running); void refreshCurrentInstance();
private: private:
void retranslateUi(); void retranslateUi();

View File

@ -440,22 +440,6 @@
<string>Ctrl+D</string> <string>Ctrl+D</string>
</property> </property>
</action> </action>
<action name="actionLaunchInstanceOffline">
<property name="text">
<string>Launch &amp;Offline</string>
</property>
<property name="toolTip">
<string>Launch the selected instance in offline mode.</string>
</property>
</action>
<action name="actionLaunchInstanceDemo">
<property name="text">
<string>Launch &amp;Demo</string>
</property>
<property name="toolTip">
<string>Launch the selected instance in demo mode.</string>
</property>
</action>
<action name="actionExportInstance"> <action name="actionExportInstance">
<property name="icon"> <property name="icon">
<iconset theme="export"> <iconset theme="export">

View File

@ -8,8 +8,6 @@
#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameAPI.h"
#include "ui_ReviewMessageBox.h" #include "ui_ReviewMessageBox.h"
#include "FileSystem.h"
#include "Json.h"
#include "Markdown.h" #include "Markdown.h"
#include "tasks/ConcurrentTask.h" #include "tasks/ConcurrentTask.h"
@ -33,9 +31,9 @@ static std::list<Version> mcVersions(BaseInstance* inst)
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() }; return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() };
} }
static std::optional<ResourceAPI::ModLoaderTypes> mcLoaders(BaseInstance* inst) static std::optional<ModPlatform::ModLoaderTypes> mcLoaders(BaseInstance* inst)
{ {
return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders() }; return { static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders() };
} }
ModUpdateDialog::ModUpdateDialog(QWidget* parent, ModUpdateDialog::ModUpdateDialog(QWidget* parent,

View File

@ -258,7 +258,7 @@ QList<BasePage*> ModDownloadDialog::getPages()
{ {
QList<BasePage*> pages; QList<BasePage*> pages;
auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getModLoaders().value(); auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getSupportedModLoaders().value();
if (ModrinthAPI::validateModLoaders(loaders)) if (ModrinthAPI::validateModLoaders(loaders))
pages.append(ModrinthModPage::create(this, *m_instance)); pages.append(ModrinthModPage::create(this, *m_instance));

View File

@ -64,7 +64,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new
ui->setupUi(this); ui->setupUi(this);
ui->listView->setEmptyString( ui->listView->setEmptyString(
tr("Welcome!\n" tr("Welcome!\n"
"If you're new here, you can click the \"Add\" button to add your Mojang or Minecraft account.")); "If you're new here, you can select the \"Add Microsoft\" or \"Add Mojang\" buttons to link your Microsoft and/or Mojang "
"accounts."));
ui->listView->setEmptyMode(VersionListView::String); ui->listView->setEmptyMode(VersionListView::String);
ui->listView->setContextMenuPolicy(Qt::CustomContextMenu); ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
@ -85,6 +86,8 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new
connect(selectionModel, &QItemSelectionModel::selectionChanged, connect(selectionModel, &QItemSelectionModel::selectionChanged,
[this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); }); [this]([[maybe_unused]] const QItemSelection& sel, [[maybe_unused]] const QItemSelection& dsel) { updateButtonStates(); });
connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu); connect(ui->listView, &VersionListView::customContextMenuRequested, this, &AccountListPage::ShowContextMenu);
connect(ui->listView, &VersionListView::activated, this,
[this](const QModelIndex& index) { m_accounts->setDefaultAccount(m_accounts->at(index.row())); });
connect(m_accounts.get(), &AccountList::listChanged, this, &AccountListPage::listChanged); connect(m_accounts.get(), &AccountList::listChanged, this, &AccountListPage::listChanged);
connect(m_accounts.get(), &AccountList::listActivityChanged, this, &AccountListPage::listChanged); connect(m_accounts.get(), &AccountList::listActivityChanged, this, &AccountListPage::listChanged);

View File

@ -185,6 +185,7 @@ void JavaPage::updateThresholds()
{ {
auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
unsigned int maxMem = ui->maxMemSpinBox->value(); unsigned int maxMem = ui->maxMemSpinBox->value();
unsigned int minMem = ui->minMemSpinBox->value();
QString iconName; QString iconName;
@ -194,6 +195,9 @@ void JavaPage::updateThresholds()
} else if (maxMem > (sysMiB * 0.9)) { } else if (maxMem > (sysMiB * 0.9)) {
iconName = "status-yellow"; iconName = "status-yellow";
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
} else if (maxMem < minMem) {
iconName = "status-yellow";
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
} else { } else {
iconName = "status-good"; iconName = "status-good";
ui->labelMaxMemIcon->setToolTip(""); ui->labelMaxMemIcon->setToolTip("");

View File

@ -2,7 +2,6 @@
/* /*
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2023 seth <getchoo at tuta dot io>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -115,13 +114,11 @@ void MinecraftPage::applySettings()
s->set("ShowGameTime", ui->showGameTime->isChecked()); s->set("ShowGameTime", ui->showGameTime->isChecked());
s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked()); s->set("ShowGlobalGameTime", ui->showGlobalGameTime->isChecked());
s->set("RecordGameTime", ui->recordGameTime->isChecked()); s->set("RecordGameTime", ui->recordGameTime->isChecked());
s->set("ShowGameTimeWithoutDays", ui->showGameTimeWithoutDays->isChecked());
// Miscellaneous // Miscellaneous
s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked()); s->set("CloseAfterLaunch", ui->closeAfterLaunchCheck->isChecked());
s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked()); s->set("QuitAfterGameStop", ui->quitAfterGameStopCheck->isChecked());
// Mod loader settings
s->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked());
} }
void MinecraftPage::loadSettings() void MinecraftPage::loadSettings()
@ -169,11 +166,10 @@ void MinecraftPage::loadSettings()
ui->showGameTime->setChecked(s->get("ShowGameTime").toBool()); ui->showGameTime->setChecked(s->get("ShowGameTime").toBool());
ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool()); ui->showGlobalGameTime->setChecked(s->get("ShowGlobalGameTime").toBool());
ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool()); ui->recordGameTime->setChecked(s->get("RecordGameTime").toBool());
ui->showGameTimeWithoutDays->setChecked(s->get("ShowGameTimeWithoutDays").toBool());
ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool()); ui->closeAfterLaunchCheck->setChecked(s->get("CloseAfterLaunch").toBool());
ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool());
ui->disableQuiltBeaconCheckBox->setChecked(s->get("DisableQuiltBeacon").toBool());
} }
void MinecraftPage::retranslate() void MinecraftPage::retranslate()

View File

@ -138,6 +138,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="showGameTimeWithoutDays">
<property name="text">
<string>Show time spent playing in hours</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -190,25 +197,6 @@
<string>Tweaks</string> <string>Tweaks</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_12"> <layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QGroupBox" name="modLoaderSettingsGroupBox">
<property name="title">
<string>Mod loader settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QCheckBox" name="disableQuiltBeaconCheckBox">
<property name="text">
<string>Disable Quilt Loader Beacon</string>
</property>
<property name="toolTip">
<string>Disable Quilt loader's beacon for counting monthly active users</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="nativeLibWorkaroundGroupBox"> <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
<property name="title"> <property name="title">

View File

@ -152,6 +152,7 @@ void ExternalResourcesPage::retranslate()
void ExternalResourcesPage::itemActivated(const QModelIndex&) void ExternalResourcesPage::itemActivated(const QModelIndex&)
{ {
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()); auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
m_model->setResourceEnabled(selection.indexes(), EnableAction::TOGGLE);
} }
void ExternalResourcesPage::filterTextChanged(const QString& newContents) void ExternalResourcesPage::filterTextChanged(const QString& newContents)

View File

@ -3,7 +3,6 @@
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 seth <getchoo at tuta dot io>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -254,14 +253,6 @@ void InstanceSettingsPage::applySettings()
m_settings->reset("InstanceAccountId"); m_settings->reset("InstanceAccountId");
} }
bool overrideModLoaderSettings = ui->modLoaderSettingsGroupBox->isChecked();
m_settings->set("OverrideModLoaderSettings", overrideModLoaderSettings);
if (overrideModLoaderSettings) {
m_settings->set("DisableQuiltBeacon", ui->disableQuiltBeaconCheckBox->isChecked());
} else {
m_settings->reset("DisableQuiltBeacon");
}
// FIXME: This should probably be called by a signal instead // FIXME: This should probably be called by a signal instead
m_instance->updateRuntimeContext(); m_instance->updateRuntimeContext();
} }
@ -365,10 +356,6 @@ void InstanceSettingsPage::loadSettings()
ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool());
updateAccountsMenu(); updateAccountsMenu();
// Mod loader specific settings
ui->modLoaderSettingsGroupBox->setChecked(m_settings->get("OverrideModLoaderSettings").toBool());
ui->disableQuiltBeaconCheckBox->setChecked(m_settings->get("DisableQuiltBeacon").toBool());
} }
void InstanceSettingsPage::on_javaDetectBtn_clicked() void InstanceSettingsPage::on_javaDetectBtn_clicked()
@ -491,6 +478,7 @@ void InstanceSettingsPage::updateThresholds()
{ {
auto sysMiB = Sys::getSystemRam() / Sys::mebibyte; auto sysMiB = Sys::getSystemRam() / Sys::mebibyte;
unsigned int maxMem = ui->maxMemSpinBox->value(); unsigned int maxMem = ui->maxMemSpinBox->value();
unsigned int minMem = ui->minMemSpinBox->value();
QString iconName; QString iconName;
@ -500,6 +488,9 @@ void InstanceSettingsPage::updateThresholds()
} else if (maxMem > (sysMiB * 0.9)) { } else if (maxMem > (sysMiB * 0.9)) {
iconName = "status-yellow"; iconName = "status-yellow";
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
} else if (maxMem < minMem) {
iconName = "status-yellow";
ui->labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
} else { } else {
iconName = "status-good"; iconName = "status-good";
ui->labelMaxMemIcon->setToolTip(""); ui->labelMaxMemIcon->setToolTip("");

View File

@ -583,31 +583,6 @@
<string>Miscellaneous</string> <string>Miscellaneous</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_9"> <layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QGroupBox" name="modLoaderSettingsGroupBox">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="title">
<string>Mod loader settings</string>
</property>
<layout class="QVBoxLayout" name="VerticalLayout_16">
<item>
<widget class="QCheckBox" name="disableQuiltBeaconCheckBox">
<property name="text">
<string>Disable Quilt Loader Beacon</string>
</property>
<property name="toolTip">
<string>Disable Quilt loader's beacon for counting monthly active users</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="gameTimeGroupBox"> <widget class="QGroupBox" name="gameTimeGroupBox">
<property name="enabled"> <property name="enabled">

View File

@ -3,7 +3,7 @@
* Prism Launcher - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me> * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -354,14 +354,8 @@ class ServersModel : public QAbstractListModel {
} }
} }
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override { return parent.isValid() ? 0 : m_servers.size(); }
{ int columnCount(const QModelIndex& parent) const override { return parent.isValid() ? 0 : COLUMN_COUNT; }
return parent.isValid() ? 0 : m_servers.size();
}
int columnCount(const QModelIndex& parent) const override
{
return parent.isValid() ? 0 : COLUMN_COUNT;
}
Server* at(int index) Server* at(int index)
{ {
@ -445,10 +439,7 @@ class ServersModel : public QAbstractListModel {
qDebug() << "Changed:" << path; qDebug() << "Changed:" << path;
load(); load();
} }
void fileChanged(const QString& path) void fileChanged(const QString& path) { qDebug() << "Changed:" << path; }
{
qDebug() << "Changed:" << path;
}
private slots: private slots:
void save_internal() void save_internal()
@ -492,10 +483,7 @@ class ServersModel : public QAbstractListModel {
m_saveTimer.stop(); m_saveTimer.stop();
} }
bool saveIsScheduled() const bool saveIsScheduled() const { return m_dirty; }
{
return m_dirty;
}
void updateFSObserver() void updateFSObserver()
{ {
@ -743,7 +731,7 @@ void ServersPage::on_actionMove_Down_triggered()
void ServersPage::on_actionJoin_triggered() void ServersPage::on_actionJoin_triggered()
{ {
const auto& address = m_model->at(currentServer)->m_address; const auto& address = m_model->at(currentServer)->m_address;
APPLICATION->launch(m_inst, true, false, nullptr, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address))); APPLICATION->launch(m_inst, true, false, std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(address)));
} }
#include "ServersPage.moc" #include "ServersPage.moc"

View File

@ -166,14 +166,17 @@ VersionPage::VersionPage(MinecraftInstance* inst, QWidget* parent) : QMainWindow
ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection); ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->packageView->setContextMenuPolicy(Qt::CustomContextMenu); ui->packageView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->packageView->selectionModel(), &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent);
auto smodel = ui->packageView->selectionModel(); auto smodel = ui->packageView->selectionModel();
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::versionCurrent);
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent); connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls); connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls);
updateVersionControls(); updateVersionControls();
preselect(0); preselect(0);
connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu); connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu);
connect(ui->packageView, &QAbstractItemView::activated, this, [this](const QModelIndex& index) {
auto component = m_profile->getComponent(index.row());
component->setEnabled(!component->isEnabled());
});
connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged); connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged);
} }
@ -408,7 +411,7 @@ void VersionPage::on_actionDownload_All_triggered()
if (!APPLICATION->accounts()->anyAccountIsValid()) { if (!APPLICATION->accounts()->anyAccountIsValid()) {
CustomMessageBox::selectable(this, tr("Error"), CustomMessageBox::selectable(this, tr("Error"),
tr("Cannot download Minecraft or update instances unless you have at least " tr("Cannot download Minecraft or update instances unless you have at least "
"one account added.\nPlease add your Mojang or Minecraft account."), "one account added.\nPlease add your Microsoft or Mojang account."),
QMessageBox::Warning) QMessageBox::Warning)
->show(); ->show();
return; return;

View File

@ -33,7 +33,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
auto sort = getCurrentSortingMethodByIndex(); auto sort = getCurrentSortingMethodByIndex();
return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getModLoaders(), versions }; return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions };
} }
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry)
@ -48,7 +48,7 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en
if (!m_filter->versions.empty()) if (!m_filter->versions.empty())
versions = m_filter->versions; versions = m_filter->versions;
return { pack, versions, profile->getModLoaders() }; return { pack, versions, profile->getSupportedModLoaders() };
} }
ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry) ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry)

View File

@ -124,8 +124,7 @@ void ModPage::updateVersionList()
auto version = current_pack->versions[i]; auto version = current_pack->versions[i];
bool valid = false; bool valid = false;
for (auto& mcVer : m_filter->versions) { for (auto& mcVer : m_filter->versions) {
// NOTE: Flame doesn't care about loader, so passing it changes nothing. if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) {
if (validateVersion(version, mcVer.toString(), packProfile->getModLoaders())) {
valid = true; valid = true;
break; break;
} }

View File

@ -55,7 +55,7 @@ class ModPage : public ResourcePage {
virtual auto validateVersion(ModPlatform::IndexedVersion& ver, virtual auto validateVersion(ModPlatform::IndexedVersion& ver,
QString mineVer, QString mineVer,
std::optional<ResourceAPI::ModLoaderTypes> loaders = {}) const -> bool = 0; std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const -> bool = 0;
[[nodiscard]] bool supportsFiltering() const override { return true; }; [[nodiscard]] bool supportsFiltering() const override { return true; };
auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; } auto getFilter() const -> const std::shared_ptr<ModFilterWidget::Filter> { return m_filter; }

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
#include "ShaderPackPage.h" #include "ShaderPackPage.h"
#include "modplatform/ModIndex.h"
#include "ui_ResourcePage.h" #include "ui_ResourcePage.h"
#include "ShaderPackModel.h" #include "ShaderPackModel.h"
@ -48,7 +49,7 @@ void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pac
const std::shared_ptr<ResourceFolderModel> base_model) const std::shared_ptr<ResourceFolderModel> base_model)
{ {
QString custom_target_folder; QString custom_target_folder;
if (version.loaders.contains(QStringLiteral("canvas"))) if (version.loaders & ModPlatform::Cauldron)
custom_target_folder = QStringLiteral("resourcepacks"); custom_target_folder = QStringLiteral("resourcepacks");
m_model->addPack(pack, version, base_model, false, custom_target_folder); m_model->addPack(pack, version, base_model, false, custom_target_folder);
} }

View File

@ -31,7 +31,7 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr
auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
{ {
return FlameMod::loadDependencyVersions(m, arr); return FlameMod::loadDependencyVersions(m, arr, &m_base_instance);
} }
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray

View File

@ -68,10 +68,10 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) :
auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver,
QString mineVer, QString mineVer,
std::optional<ResourceAPI::ModLoaderTypes> loaders) const -> bool std::optional<ModPlatform::ModLoaderTypes> loaders) const -> bool
{ {
Q_UNUSED(loaders); return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() &&
return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty(); (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders);
} }
bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const

View File

@ -95,7 +95,7 @@ class FlameModPage : public ModPage {
bool validateVersion(ModPlatform::IndexedVersion& ver, bool validateVersion(ModPlatform::IndexedVersion& ver,
QString mineVer, QString mineVer,
std::optional<ResourceAPI::ModLoaderTypes> loaders = {}) const override; std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const override;
bool optedOut(ModPlatform::IndexedVersion& ver) const override; bool optedOut(ModPlatform::IndexedVersion& ver) const override;
void openUrl(const QUrl& url) override; void openUrl(const QUrl& url) override;

View File

@ -39,12 +39,12 @@ void ModrinthModModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObjec
void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
{ {
::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance);
} }
auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
{ {
return ::Modrinth::loadDependencyVersions(m, arr); return ::Modrinth::loadDependencyVersions(m, arr, &m_base_instance);
} }
auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
@ -66,7 +66,7 @@ void ModrinthResourcePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, Q
void ModrinthResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) void ModrinthResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
{ {
::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance);
} }
auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
@ -88,7 +88,7 @@ void ModrinthTexturePackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJ
void ModrinthTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) void ModrinthTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
{ {
::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance);
} }
auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
@ -110,7 +110,7 @@ void ModrinthShaderPackModel::loadExtraPackInfo(ModPlatform::IndexedPack& m, QJs
void ModrinthShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) void ModrinthShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr)
{ {
::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); ::Modrinth::loadIndexedPackVersions(m, arr, &m_base_instance);
} }
auto ModrinthShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray auto ModrinthShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray

View File

@ -65,21 +65,9 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan
auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver,
QString mineVer, QString mineVer,
std::optional<ResourceAPI::ModLoaderTypes> loaders) const -> bool std::optional<ModPlatform::ModLoaderTypes> loaders) const -> bool
{ {
auto loaderCompatible = !loaders.has_value(); return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders);
if (!loaderCompatible) {
auto loaderStrings = ModrinthAPI::getModLoaderStrings(loaders.value());
for (auto remoteLoader : ver.loaders) {
if (loaderStrings.contains(remoteLoader)) {
loaderCompatible = true;
break;
}
}
}
return ver.mcVersion.contains(mineVer) && loaderCompatible;
} }
ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance)

View File

@ -93,7 +93,7 @@ class ModrinthModPage : public ModPage {
[[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; }
auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional<ResourceAPI::ModLoaderTypes> loaders = {}) const auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional<ModPlatform::ModLoaderTypes> loaders = {}) const
-> bool override; -> bool override;
}; };

View File

@ -186,12 +186,20 @@ QString JavaSettingsWidget::javaPath() const
int JavaSettingsWidget::maxHeapSize() const int JavaSettingsWidget::maxHeapSize() const
{ {
return m_maxMemSpinBox->value(); auto min = m_minMemSpinBox->value();
auto max = m_maxMemSpinBox->value();
if (max < min)
max = min;
return max;
} }
int JavaSettingsWidget::minHeapSize() const int JavaSettingsWidget::minHeapSize() const
{ {
return m_minMemSpinBox->value(); auto min = m_minMemSpinBox->value();
auto max = m_maxMemSpinBox->value();
if (min > max)
min = max;
return min;
} }
bool JavaSettingsWidget::permGenEnabled() const bool JavaSettingsWidget::permGenEnabled() const
@ -214,17 +222,9 @@ void JavaSettingsWidget::memoryValueChanged(int)
if (obj == m_minMemSpinBox && min != observedMinMemory) { if (obj == m_minMemSpinBox && min != observedMinMemory) {
observedMinMemory = min; observedMinMemory = min;
actuallyChanged = true; actuallyChanged = true;
if (min > max) {
observedMaxMemory = min;
m_maxMemSpinBox->setValue(min);
}
} else if (obj == m_maxMemSpinBox && max != observedMaxMemory) { } else if (obj == m_maxMemSpinBox && max != observedMaxMemory) {
observedMaxMemory = max; observedMaxMemory = max;
actuallyChanged = true; actuallyChanged = true;
if (min > max) {
observedMinMemory = max;
m_minMemSpinBox->setValue(max);
}
} else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory) { } else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory) {
observedPermGenMemory = permgen; observedPermGenMemory = permgen;
actuallyChanged = true; actuallyChanged = true;
@ -361,8 +361,8 @@ void JavaSettingsWidget::checkJavaPath(const QString& path)
setJavaStatus(JavaStatus::Pending); setJavaStatus(JavaStatus::Pending);
m_checker.reset(new JavaChecker()); m_checker.reset(new JavaChecker());
m_checker->m_path = path; m_checker->m_path = path;
m_checker->m_minMem = m_minMemSpinBox->value(); m_checker->m_minMem = minHeapSize();
m_checker->m_maxMem = m_maxMemSpinBox->value(); m_checker->m_maxMem = maxHeapSize();
if (m_permGenSpinBox->isVisible()) { if (m_permGenSpinBox->isVisible()) {
m_checker->m_permGen = m_permGenSpinBox->value(); m_checker->m_permGen = m_permGenSpinBox->value();
} }
@ -415,6 +415,9 @@ void JavaSettingsWidget::updateThresholds()
} else if (observedMaxMemory > (m_availableMemory * 0.9)) { } else if (observedMaxMemory > (m_availableMemory * 0.9)) {
iconName = "status-yellow"; iconName = "status-yellow";
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity.")); m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
} else if (observedMaxMemory < observedMinMemory) {
iconName = "status-yellow";
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
} else { } else {
iconName = "status-good"; iconName = "status-good";
m_labelMaxMemIcon->setToolTip(""); m_labelMaxMemIcon->setToolTip("");

View File

@ -220,5 +220,5 @@ void LocalPeer::receiveConnection()
socket->waitForBytesWritten(1000); socket->waitForBytesWritten(1000);
socket->waitForDisconnected(1000); // make sure client reads ack socket->waitForDisconnected(1000); // make sure client reads ack
delete socket; delete socket;
emit messageReceived(uMsg); //### (might take a long time to return) emit messageReceived(uMsg); // ### (might take a long time to return)
} }

View File

@ -25,8 +25,7 @@
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" "aarch64-linux"
# Disabled due to our packages not supporting darwin yet. "x86_64-darwin"
# "x86_64-darwin" "aarch64-darwin"
# "aarch64-darwin"
]; ];
} }

View File

@ -23,18 +23,13 @@
types_or = ["c" "c++" "java" "json" "objective-c"]; types_or = ["c" "c++" "java" "json" "objective-c"];
}; };
}; };
tools.clang-tools = pkgs.clang-tools_16;
}; };
}; };
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
inherit (self.checks.${system}.pre-commit-check) shellHook; inherit (self.checks.${system}.pre-commit-check) shellHook;
packages = with pkgs; [
nodePackages.markdownlint-cli
alejandra
deadnix
clang-tools
nil
];
inputsFrom = [self.packages.${system}.prismlauncher-unwrapped]; inputsFrom = [self.packages.${system}.prismlauncher-unwrapped];
buildInputs = with pkgs; [ccache ninja]; buildInputs = with pkgs; [ccache ninja];

View File

@ -17,6 +17,7 @@
mkPrism = qt: mkPrism = qt:
qt.callPackage ./package.nix { qt.callPackage ./package.nix {
inherit (inputs) libnbtplusplus; inherit (inputs) libnbtplusplus;
inherit (prev.darwin.apple_sdk.frameworks) Cocoa;
inherit self version; inherit self version;
}; };
in { in {

View File

@ -1,7 +1,10 @@
{ {
lib, lib,
stdenv, stdenv,
canonicalize-jars-hook,
cmake, cmake,
cmark,
Cocoa,
ninja, ninja,
jdk17, jdk17,
zlib, zlib,
@ -9,57 +12,62 @@
quazip, quazip,
extra-cmake-modules, extra-cmake-modules,
tomlplusplus, tomlplusplus,
cmark,
ghc_filesystem, ghc_filesystem,
gamemode, gamemode,
msaClientID ? null, msaClientID ? null,
gamemodeSupport ? true, gamemodeSupport ? stdenv.isLinux,
self, self,
version, version,
libnbtplusplus, libnbtplusplus,
}: }:
stdenv.mkDerivation rec { assert lib.assertMsg (stdenv.isLinux || !gamemodeSupport) "gamemodeSupport is only available on Linux";
pname = "prismlauncher-unwrapped"; stdenv.mkDerivation rec {
inherit version; pname = "prismlauncher-unwrapped";
inherit version;
src = lib.cleanSource self; src = lib.cleanSource self;
nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja]; nativeBuildInputs = [extra-cmake-modules cmake jdk17 ninja canonicalize-jars-hook];
buildInputs = buildInputs =
[ [
qtbase qtbase
zlib zlib
quazip quazip
ghc_filesystem ghc_filesystem
tomlplusplus tomlplusplus
cmark cmark
] ]
++ lib.optional gamemodeSupport gamemode; ++ lib.optional gamemodeSupport gamemode
++ lib.optionals stdenv.isDarwin [Cocoa];
hardeningEnable = ["pie"]; hardeningEnable = lib.optionals stdenv.isLinux ["pie"];
cmakeFlags = cmakeFlags =
lib.optionals (msaClientID != null) ["-DLauncher_MSA_CLIENT_ID=${msaClientID}"] [
++ lib.optionals (lib.versionOlder qtbase.version "6") ["-DLauncher_QT_VERSION_MAJOR=5"]; "-DLauncher_BUILD_PLATFORM=nixpkgs"
]
++ lib.optionals (msaClientID != null) ["-DLauncher_MSA_CLIENT_ID=${msaClientID}"]
++ lib.optionals (lib.versionOlder qtbase.version "6") ["-DLauncher_QT_VERSION_MAJOR=5"]
++ lib.optionals stdenv.isDarwin ["-DINSTALL_BUNDLE=nodeps" "-DMACOSX_SPARKLE_UPDATE_FEED_URL=''"];
postUnpack = '' postUnpack = ''
rm -rf source/libraries/libnbtplusplus rm -rf source/libraries/libnbtplusplus
ln -s ${libnbtplusplus} source/libraries/libnbtplusplus ln -s ${libnbtplusplus} source/libraries/libnbtplusplus
'';
dontWrapQtApps = true;
meta = with lib; {
homepage = "https://prismlauncher.org/";
description = "A free, open source launcher for Minecraft";
longDescription = ''
Allows you to have multiple, separate instances of Minecraft (each with
their own mods, texture packs, saves, etc) and helps you manage them and
their associated options with a simple interface.
''; '';
platforms = platforms.linux;
changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}"; dontWrapQtApps = true;
license = licenses.gpl3Only;
maintainers = with maintainers; [minion3665 Scrumplex]; meta = with lib; {
}; homepage = "https://prismlauncher.org/";
} description = "A free, open source launcher for Minecraft";
longDescription = ''
Allows you to have multiple, separate instances of Minecraft (each with
their own mods, texture packs, saves, etc) and helps you manage them and
their associated options with a simple interface.
'';
platforms = with platforms; linux ++ darwin;
changelog = "https://github.com/PrismLauncher/PrismLauncher/releases/tag/${version}";
license = licenses.gpl3Only;
maintainers = with maintainers; [minion3665 Scrumplex getchoo];
};
}

View File

@ -353,10 +353,7 @@ class FileSystemTest : public QObject {
} }
} }
void test_getDesktop() void test_getDesktop() { QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); }
{
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
}
void test_link() void test_link()
{ {