mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-04-29 22:24:26 +02:00
Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into lambda
This commit is contained in:
commit
0e80aae1b8
@ -242,6 +242,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
|
||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||
{ { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" },
|
||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||
@ -257,6 +258,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
m_serverToJoin = parser.value("server");
|
||||
m_worldToJoin = parser.value("world");
|
||||
m_profileToUse = parser.value("profile");
|
||||
if (parser.isSet("offline")) {
|
||||
m_offline = true;
|
||||
m_offlineName = parser.value("offline");
|
||||
}
|
||||
m_liveCheck = parser.isSet("alive");
|
||||
|
||||
m_instanceIdToShowWindowOf = parser.value("show");
|
||||
@ -271,8 +276,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
}
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) {
|
||||
std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl;
|
||||
if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) &&
|
||||
m_instanceIdToLaunch.isEmpty()) {
|
||||
std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl;
|
||||
m_status = Application::Failed;
|
||||
return;
|
||||
}
|
||||
@ -397,6 +403,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
||||
if (!m_profileToUse.isEmpty()) {
|
||||
launch.args["profile"] = m_profileToUse;
|
||||
}
|
||||
if (m_offline) {
|
||||
launch.args["offline_enabled"] = "true";
|
||||
launch.args["offline_name"] = m_offlineName;
|
||||
}
|
||||
m_peerInstance->sendMessage(launch.serialize(), timeout);
|
||||
}
|
||||
m_status = Application::Succeeded;
|
||||
@ -1209,7 +1219,7 @@ void Application::performMainStartupAction()
|
||||
qDebug() << " Launching with account" << m_profileToUse;
|
||||
}
|
||||
|
||||
launch(inst, true, false, targetToJoin, accountToUse);
|
||||
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1308,6 +1318,8 @@ void Application::messageReceived(const QByteArray& message)
|
||||
QString server = received.args["server"];
|
||||
QString world = received.args["world"];
|
||||
QString profile = received.args["profile"];
|
||||
bool offline = received.args["offline_enabled"] == "true";
|
||||
QString offlineName = received.args["offline_name"];
|
||||
|
||||
InstancePtr instance;
|
||||
if (!id.isEmpty()) {
|
||||
@ -1337,7 +1349,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||
}
|
||||
}
|
||||
|
||||
launch(instance, true, false, serverObject, accountObject);
|
||||
launch(instance, !offline, false, serverObject, accountObject, offlineName);
|
||||
} else {
|
||||
qWarning() << "Received invalid message" << message;
|
||||
}
|
||||
@ -1375,7 +1387,12 @@ bool Application::openJsonEditor(const QString& filename)
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
|
||||
bool Application::launch(InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
MinecraftTarget::Ptr targetToJoin,
|
||||
MinecraftAccountPtr accountToUse,
|
||||
const QString& offlineName)
|
||||
{
|
||||
if (m_updateRunning) {
|
||||
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
||||
@ -1395,6 +1412,7 @@ bool Application::launch(InstancePtr instance, bool online, bool demo, Minecraft
|
||||
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
|
||||
controller->setTargetToJoin(targetToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
controller->setOfflineName(offlineName);
|
||||
if (window) {
|
||||
controller->setParentWidget(window);
|
||||
} else if (m_mainWindow) {
|
||||
|
@ -211,7 +211,8 @@ class Application : public QApplication {
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
MinecraftTarget::Ptr targetToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
MinecraftAccountPtr accountToUse = nullptr,
|
||||
const QString& offlineName = QString());
|
||||
bool kill(InstancePtr instance);
|
||||
void closeCurrentWindow();
|
||||
|
||||
@ -300,6 +301,8 @@ class Application : public QApplication {
|
||||
QString m_serverToJoin;
|
||||
QString m_worldToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_offline = false;
|
||||
QString m_offlineName;
|
||||
bool m_liveCheck = false;
|
||||
QList<QUrl> m_urlsToImport;
|
||||
QString m_instanceIdToShowWindowOf;
|
||||
|
@ -345,13 +345,12 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/mod/TexturePackFolderModel.h
|
||||
minecraft/mod/TexturePackFolderModel.cpp
|
||||
minecraft/mod/ShaderPackFolderModel.h
|
||||
minecraft/mod/tasks/BasicFolderLoadTask.h
|
||||
minecraft/mod/tasks/ModFolderLoadTask.h
|
||||
minecraft/mod/tasks/ModFolderLoadTask.cpp
|
||||
minecraft/mod/tasks/ResourceFolderLoadTask.h
|
||||
minecraft/mod/tasks/ResourceFolderLoadTask.cpp
|
||||
minecraft/mod/tasks/LocalModParseTask.h
|
||||
minecraft/mod/tasks/LocalModParseTask.cpp
|
||||
minecraft/mod/tasks/LocalModUpdateTask.h
|
||||
minecraft/mod/tasks/LocalModUpdateTask.cpp
|
||||
minecraft/mod/tasks/LocalResourceUpdateTask.h
|
||||
minecraft/mod/tasks/LocalResourceUpdateTask.cpp
|
||||
minecraft/mod/tasks/LocalDataPackParseTask.h
|
||||
minecraft/mod/tasks/LocalDataPackParseTask.cpp
|
||||
minecraft/mod/tasks/LocalResourcePackParseTask.h
|
||||
@ -1069,8 +1068,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/BlockedModsDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.h
|
||||
ui/dialogs/ResourceUpdateDialog.cpp
|
||||
ui/dialogs/ResourceUpdateDialog.h
|
||||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
|
||||
|
@ -235,10 +235,15 @@ void LaunchController::login()
|
||||
if (!m_session->wants_online) {
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||
if (!ok) {
|
||||
tryagain = false;
|
||||
break;
|
||||
QString name;
|
||||
if (m_offlineName.isEmpty()) {
|
||||
name = askOfflineName(m_session->player_name, m_session->demo, ok);
|
||||
if (!ok) {
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
name = m_offlineName;
|
||||
}
|
||||
m_session->MakeOffline(name);
|
||||
// offline flavored game from here :3
|
||||
|
@ -56,6 +56,8 @@ class LaunchController : public Task {
|
||||
|
||||
void setOnline(bool online) { m_online = online; }
|
||||
|
||||
void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; }
|
||||
|
||||
void setDemo(bool demo) { m_demo = demo; }
|
||||
|
||||
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
|
||||
@ -88,6 +90,7 @@ class LaunchController : public Task {
|
||||
private:
|
||||
BaseProfilerFactory* m_profiler = nullptr;
|
||||
bool m_online = true;
|
||||
QString m_offlineName;
|
||||
bool m_demo = false;
|
||||
InstancePtr m_instance;
|
||||
QWidget* m_parentWidget = nullptr;
|
||||
|
@ -33,7 +33,7 @@ class shared_qobject_ptr : public QSharedPointer<T> {
|
||||
{}
|
||||
|
||||
void reset() { QSharedPointer<T>::reset(); }
|
||||
void reset(T*&& other)
|
||||
void reset(T* other)
|
||||
{
|
||||
shared_qobject_ptr<T> t(other);
|
||||
this->swap(t);
|
||||
|
@ -35,9 +35,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
QString custom_target_folder)
|
||||
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder)
|
||||
{
|
||||
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model && is_indexed) {
|
||||
m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version));
|
||||
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource);
|
||||
if (is_indexed) {
|
||||
m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version));
|
||||
connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource);
|
||||
|
||||
addTask(m_update_task);
|
||||
}
|
||||
@ -91,12 +91,8 @@ void ResourceDownloadTask::downloadSucceeded()
|
||||
m_filesNetJob.reset();
|
||||
auto name = std::get<0>(to_delete);
|
||||
auto filename = std::get<1>(to_delete);
|
||||
if (!name.isEmpty() && filename != m_pack_version.fileName) {
|
||||
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model)
|
||||
model->uninstallMod(filename, true);
|
||||
else
|
||||
m_pack_model->uninstallResource(filename);
|
||||
}
|
||||
if (!name.isEmpty() && filename != m_pack_version.fileName)
|
||||
m_pack_model->uninstallResource(filename, true);
|
||||
}
|
||||
|
||||
void ResourceDownloadTask::downloadFailed(QString reason)
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "net/NetJob.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class ResourceFolderModel;
|
||||
@ -50,7 +50,7 @@ class ResourceDownloadTask : public SequentialTask {
|
||||
QString m_custom_target_folder;
|
||||
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
LocalModUpdateTask::Ptr m_update_task;
|
||||
LocalResourceUpdateTask::Ptr m_update_task;
|
||||
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadFailed(QString reason);
|
||||
|
@ -1203,7 +1203,7 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList()
|
||||
{
|
||||
if (!m_loader_mod_list) {
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed));
|
||||
m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed, true));
|
||||
}
|
||||
return m_loader_mod_list;
|
||||
}
|
||||
@ -1212,7 +1212,7 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList()
|
||||
{
|
||||
if (!m_core_mod_list) {
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed));
|
||||
m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed, true));
|
||||
}
|
||||
return m_core_mod_list;
|
||||
}
|
||||
@ -1229,7 +1229,8 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList()
|
||||
std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList()
|
||||
{
|
||||
if (!m_resource_pack_list) {
|
||||
m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this));
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this, is_indexed, true));
|
||||
}
|
||||
return m_resource_pack_list;
|
||||
}
|
||||
@ -1237,7 +1238,8 @@ std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList()
|
||||
std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList()
|
||||
{
|
||||
if (!m_texture_pack_list) {
|
||||
m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this));
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this, is_indexed, true));
|
||||
}
|
||||
return m_texture_pack_list;
|
||||
}
|
||||
@ -1245,11 +1247,17 @@ std::shared_ptr<TexturePackFolderModel> MinecraftInstance::texturePackList()
|
||||
std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList()
|
||||
{
|
||||
if (!m_shader_pack_list) {
|
||||
m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this));
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this, is_indexed, true));
|
||||
}
|
||||
return m_shader_pack_list;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<ResourceFolderModel>> MinecraftInstance::resourceLists()
|
||||
{
|
||||
return { loaderModList(), coreModList(), nilModList(), resourcePackList(), texturePackList(), shaderPackList() };
|
||||
}
|
||||
|
||||
std::shared_ptr<WorldList> MinecraftInstance::worldList()
|
||||
{
|
||||
if (!m_world_list) {
|
||||
|
@ -116,6 +116,7 @@ class MinecraftInstance : public BaseInstance {
|
||||
std::shared_ptr<ResourcePackFolderModel> resourcePackList();
|
||||
std::shared_ptr<TexturePackFolderModel> texturePackList();
|
||||
std::shared_ptr<ShaderPackFolderModel> shaderPackList();
|
||||
QList<std::shared_ptr<ResourceFolderModel>> resourceLists();
|
||||
std::shared_ptr<WorldList> worldList();
|
||||
std::shared_ptr<GameOptions> gameOptionsModel();
|
||||
|
||||
|
@ -74,12 +74,12 @@ void MSADeviceCodeStep::perform()
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAutorizationFinished);
|
||||
connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAuthorizationFinished);
|
||||
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
struct DeviceAutorizationResponse {
|
||||
struct DeviceAuthorizationResponse {
|
||||
QString device_code;
|
||||
QString user_code;
|
||||
QString verification_uri;
|
||||
@ -90,17 +90,17 @@ struct DeviceAutorizationResponse {
|
||||
QString error_description;
|
||||
};
|
||||
|
||||
DeviceAutorizationResponse parseDeviceAutorizationResponse(const QByteArray& data)
|
||||
DeviceAuthorizationResponse parseDeviceAuthorizationResponse(const QByteArray& data)
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse device autorization response due to err:" << err.errorString();
|
||||
qWarning() << "Failed to parse device authorization response due to err:" << err.errorString();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!doc.isObject()) {
|
||||
qWarning() << "Device autorization response is not an object";
|
||||
qWarning() << "Device authorization response is not an object";
|
||||
return {};
|
||||
}
|
||||
auto obj = doc.object();
|
||||
@ -111,9 +111,9 @@ DeviceAutorizationResponse parseDeviceAutorizationResponse(const QByteArray& dat
|
||||
};
|
||||
}
|
||||
|
||||
void MSADeviceCodeStep::deviceAutorizationFinished()
|
||||
void MSADeviceCodeStep::deviceAuthorizationFinished()
|
||||
{
|
||||
auto rsp = parseDeviceAutorizationResponse(*m_response);
|
||||
auto rsp = parseDeviceAuthorizationResponse(*m_response);
|
||||
if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) {
|
||||
qWarning() << "Device authorization failed:" << rsp.error;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD,
|
||||
@ -208,12 +208,12 @@ AuthenticationResponse parseAuthenticationResponse(const QByteArray& data)
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse device autorization response due to err:" << err.errorString();
|
||||
qWarning() << "Failed to parse device authorization response due to err:" << err.errorString();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!doc.isObject()) {
|
||||
qWarning() << "Device autorization response is not an object";
|
||||
qWarning() << "Device authorization response is not an object";
|
||||
return {};
|
||||
}
|
||||
auto obj = doc.object();
|
||||
@ -274,4 +274,4 @@ void MSADeviceCodeStep::authenticationFinished()
|
||||
m_data->msaToken.refresh_token = rsp.refresh_token;
|
||||
m_data->msaToken.token = rsp.access_token;
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got"));
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class MSADeviceCodeStep : public AuthStep {
|
||||
void authorizeWithBrowser(QString url, QString code, int expiresIn);
|
||||
|
||||
private slots:
|
||||
void deviceAutorizationFinished();
|
||||
void deviceAuthorizationFinished();
|
||||
void startPoolTimer();
|
||||
void authenticateUser();
|
||||
void authenticationFinished();
|
||||
|
@ -26,33 +26,48 @@
|
||||
// launcher/minecraft/mod/Mod.h
|
||||
class Mod;
|
||||
|
||||
/* Abstraction file for easily changing the way metadata is stored / handled
|
||||
* Needs to be a class because of -Wunused-function and no C++17 [[maybe_unused]]
|
||||
* */
|
||||
class Metadata {
|
||||
public:
|
||||
using ModStruct = Packwiz::V1::Mod;
|
||||
using ModSide = Packwiz::V1::Side;
|
||||
namespace Metadata {
|
||||
using ModStruct = Packwiz::V1::Mod;
|
||||
using ModSide = Packwiz::V1::Side;
|
||||
|
||||
static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct
|
||||
{
|
||||
return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version);
|
||||
}
|
||||
inline auto create(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct
|
||||
{
|
||||
return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version);
|
||||
}
|
||||
|
||||
static auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct
|
||||
{
|
||||
return Packwiz::V1::createModFormat(index_dir, internal_mod, mod_slug);
|
||||
}
|
||||
inline auto create(const QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct
|
||||
{
|
||||
return Packwiz::V1::createModFormat(index_dir, internal_mod, std::move(mod_slug));
|
||||
}
|
||||
|
||||
static void update(QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); }
|
||||
inline void update(const QDir& index_dir, ModStruct& mod)
|
||||
{
|
||||
Packwiz::V1::updateModIndex(index_dir, mod);
|
||||
}
|
||||
|
||||
static void remove(QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); }
|
||||
inline void remove(const QDir& index_dir, QString mod_slug)
|
||||
{
|
||||
Packwiz::V1::deleteModIndex(index_dir, mod_slug);
|
||||
}
|
||||
|
||||
static void remove(QDir& index_dir, QVariant& mod_id) { Packwiz::V1::deleteModIndex(index_dir, mod_id); }
|
||||
inline void remove(const QDir& index_dir, QVariant& mod_id)
|
||||
{
|
||||
Packwiz::V1::deleteModIndex(index_dir, mod_id);
|
||||
}
|
||||
|
||||
static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); }
|
||||
inline auto get(const QDir& index_dir, QString mod_slug) -> ModStruct
|
||||
{
|
||||
return Packwiz::V1::getIndexForMod(index_dir, std::move(mod_slug));
|
||||
}
|
||||
|
||||
static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); }
|
||||
inline auto get(const QDir& index_dir, QVariant& mod_id) -> ModStruct
|
||||
{
|
||||
return Packwiz::V1::getIndexForMod(index_dir, mod_id);
|
||||
}
|
||||
|
||||
static auto modSideToString(ModSide side) -> QString { return Packwiz::V1::sideToString(side); }
|
||||
};
|
||||
inline auto modSideToString(ModSide side) -> QString
|
||||
{
|
||||
return Packwiz::V1::sideToString(side);
|
||||
}
|
||||
|
||||
}; // namespace Metadata
|
||||
|
@ -38,42 +38,22 @@
|
||||
#include "Mod.h"
|
||||
#include <qpixmap.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
|
||||
#include "MTPixmapCache.h"
|
||||
#include "MetadataHandler.h"
|
||||
#include "Resource.h"
|
||||
#include "Version.h"
|
||||
#include "minecraft/mod/ModDetails.h"
|
||||
#include "minecraft/mod/Resource.h"
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details()
|
||||
{
|
||||
m_enabled = (file.suffix() != "disabled");
|
||||
}
|
||||
|
||||
Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) : Mod(mods_dir.absoluteFilePath(metadata.filename))
|
||||
{
|
||||
m_name = metadata.name;
|
||||
m_local_details.metadata = std::make_shared<Metadata::ModStruct>(std::move(metadata));
|
||||
}
|
||||
|
||||
void Mod::setStatus(ModStatus status)
|
||||
{
|
||||
m_local_details.status = status;
|
||||
}
|
||||
void Mod::setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata)
|
||||
{
|
||||
if (status() == ModStatus::NoMetadata)
|
||||
setStatus(ModStatus::Installed);
|
||||
|
||||
m_local_details.metadata = metadata;
|
||||
}
|
||||
|
||||
void Mod::setDetails(const ModDetails& details)
|
||||
{
|
||||
m_local_details = details;
|
||||
@ -101,33 +81,28 @@ int Mod::compare(const Resource& other, SortType type) const
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
case SortType::PROVIDER: {
|
||||
return QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
||||
}
|
||||
case SortType::SIDE: {
|
||||
if (side() > cast_other->side())
|
||||
return 1;
|
||||
else if (side() < cast_other->side())
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
case SortType::LOADERS: {
|
||||
if (loaders() > cast_other->loaders())
|
||||
return 1;
|
||||
else if (loaders() < cast_other->loaders())
|
||||
return -1;
|
||||
auto compare_result = QString::compare(side(), cast_other->side(), Qt::CaseInsensitive);
|
||||
if (compare_result != 0)
|
||||
return compare_result;
|
||||
break;
|
||||
}
|
||||
case SortType::MC_VERSIONS: {
|
||||
auto thisVersion = mcVersions().join(",");
|
||||
auto otherVersion = cast_other->mcVersions().join(",");
|
||||
return QString::compare(thisVersion, otherVersion, Qt::CaseInsensitive);
|
||||
auto compare_result = QString::compare(mcVersions(), cast_other->mcVersions(), Qt::CaseInsensitive);
|
||||
if (compare_result != 0)
|
||||
return compare_result;
|
||||
break;
|
||||
}
|
||||
case SortType::LOADERS: {
|
||||
auto compare_result = QString::compare(loaders(), cast_other->loaders(), Qt::CaseInsensitive);
|
||||
if (compare_result != 0)
|
||||
return compare_result;
|
||||
break;
|
||||
}
|
||||
case SortType::RELEASE_TYPE: {
|
||||
if (releaseType() > cast_other->releaseType())
|
||||
return 1;
|
||||
else if (releaseType() < cast_other->releaseType())
|
||||
return -1;
|
||||
auto compare_result = QString::compare(releaseType(), cast_other->releaseType(), Qt::CaseInsensitive);
|
||||
if (compare_result != 0)
|
||||
return compare_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -148,28 +123,6 @@ bool Mod::applyFilter(QRegularExpression filter) const
|
||||
return Resource::applyFilter(filter);
|
||||
}
|
||||
|
||||
auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
|
||||
{
|
||||
if (!preserve_metadata) {
|
||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||
|
||||
destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
return Resource::destroy(attempt_trash);
|
||||
}
|
||||
|
||||
void Mod::destroyMetadata(QDir& index_dir)
|
||||
{
|
||||
if (metadata()) {
|
||||
Metadata::remove(index_dir, metadata()->slug);
|
||||
} else {
|
||||
auto n = name();
|
||||
Metadata::remove(index_dir, n);
|
||||
}
|
||||
m_local_details.metadata = nullptr;
|
||||
}
|
||||
|
||||
auto Mod::details() const -> const ModDetails&
|
||||
{
|
||||
return m_local_details;
|
||||
@ -181,10 +134,7 @@ auto Mod::name() const -> QString
|
||||
if (!d_name.isEmpty())
|
||||
return d_name;
|
||||
|
||||
if (metadata())
|
||||
return metadata()->name;
|
||||
|
||||
return m_name;
|
||||
return Resource::name();
|
||||
}
|
||||
|
||||
auto Mod::version() const -> QString
|
||||
@ -192,16 +142,55 @@ auto Mod::version() const -> QString
|
||||
return details().version;
|
||||
}
|
||||
|
||||
auto Mod::homeurl() const -> QString
|
||||
auto Mod::homepage() const -> QString
|
||||
{
|
||||
return details().homeurl;
|
||||
QString metaUrl = Resource::homepage();
|
||||
|
||||
if (metaUrl.isEmpty())
|
||||
return details().homeurl;
|
||||
else
|
||||
return metaUrl;
|
||||
}
|
||||
|
||||
auto Mod::metaurl() const -> QString
|
||||
auto Mod::loaders() const -> QString
|
||||
{
|
||||
if (metadata() == nullptr)
|
||||
return homeurl();
|
||||
return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id);
|
||||
if (metadata()) {
|
||||
QStringList loaders;
|
||||
auto modLoaders = metadata()->loaders;
|
||||
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, ModPlatform::Fabric,
|
||||
ModPlatform::Quilt }) {
|
||||
if (modLoaders & loader) {
|
||||
loaders << getModLoaderAsString(loader);
|
||||
}
|
||||
}
|
||||
return loaders.join(", ");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Mod::side() const -> QString
|
||||
{
|
||||
if (metadata())
|
||||
return Metadata::modSideToString(metadata()->side);
|
||||
|
||||
return Metadata::modSideToString(Metadata::ModSide::UniversalSide);
|
||||
}
|
||||
|
||||
auto Mod::mcVersions() const -> QString
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->mcVersions.join(", ");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Mod::releaseType() const -> QString
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->releaseType.toString();
|
||||
|
||||
return ModPlatform::IndexedVersionType().toString();
|
||||
}
|
||||
|
||||
auto Mod::description() const -> QString
|
||||
@ -214,73 +203,17 @@ auto Mod::authors() const -> QStringList
|
||||
return details().authors;
|
||||
}
|
||||
|
||||
auto Mod::status() const -> ModStatus
|
||||
{
|
||||
return details().status;
|
||||
}
|
||||
|
||||
auto Mod::metadata() -> std::shared_ptr<Metadata::ModStruct>
|
||||
{
|
||||
return m_local_details.metadata;
|
||||
}
|
||||
|
||||
auto Mod::metadata() const -> const std::shared_ptr<Metadata::ModStruct>
|
||||
{
|
||||
return m_local_details.metadata;
|
||||
}
|
||||
|
||||
void Mod::finishResolvingWithDetails(ModDetails&& details)
|
||||
{
|
||||
m_is_resolving = false;
|
||||
m_is_resolved = true;
|
||||
|
||||
std::shared_ptr<Metadata::ModStruct> metadata = details.metadata;
|
||||
if (details.status == ModStatus::Unknown)
|
||||
details.status = m_local_details.status;
|
||||
|
||||
m_local_details = std::move(details);
|
||||
if (metadata)
|
||||
setMetadata(std::move(metadata));
|
||||
if (!iconPath().isEmpty()) {
|
||||
m_packImageCacheKey.wasReadAttempt = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto Mod::provider() const -> std::optional<QString>
|
||||
{
|
||||
if (metadata())
|
||||
return ModPlatform::ProviderCapabilities::readableName(metadata()->provider);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Mod::side() const -> Metadata::ModSide
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->side;
|
||||
return Metadata::ModSide::UniversalSide;
|
||||
}
|
||||
|
||||
auto Mod::releaseType() const -> ModPlatform::IndexedVersionType
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->releaseType;
|
||||
return ModPlatform::IndexedVersionType::VersionType::Unknown;
|
||||
}
|
||||
|
||||
auto Mod::loaders() const -> ModPlatform::ModLoaderTypes
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->loaders;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Mod::mcVersions() const -> QStringList
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->mcVersions;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Mod::licenses() const -> const QList<ModLicense>&
|
||||
{
|
||||
return details().licenses;
|
||||
|
@ -48,7 +48,6 @@
|
||||
|
||||
#include "ModDetails.h"
|
||||
#include "Resource.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class Mod : public Resource {
|
||||
Q_OBJECT
|
||||
@ -58,24 +57,20 @@ class Mod : public Resource {
|
||||
|
||||
Mod() = default;
|
||||
Mod(const QFileInfo& file);
|
||||
Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata);
|
||||
Mod(QString file_path) : Mod(QFileInfo(file_path)) {}
|
||||
|
||||
auto details() const -> const ModDetails&;
|
||||
auto name() const -> QString override;
|
||||
auto version() const -> QString;
|
||||
auto homeurl() const -> QString;
|
||||
auto homepage() const -> QString override;
|
||||
auto description() const -> QString;
|
||||
auto authors() const -> QStringList;
|
||||
auto status() const -> ModStatus;
|
||||
auto provider() const -> std::optional<QString>;
|
||||
auto licenses() const -> const QList<ModLicense>&;
|
||||
auto issueTracker() const -> QString;
|
||||
auto metaurl() const -> QString;
|
||||
auto side() const -> Metadata::ModSide;
|
||||
auto loaders() const -> ModPlatform::ModLoaderTypes;
|
||||
auto mcVersions() const -> QStringList;
|
||||
auto releaseType() const -> ModPlatform::IndexedVersionType;
|
||||
auto side() const -> QString;
|
||||
auto loaders() const -> QString;
|
||||
auto mcVersions() const -> QString;
|
||||
auto releaseType() const -> QString;
|
||||
|
||||
/** Get the intneral path to the mod's icon file*/
|
||||
QString iconPath() const { return m_local_details.icon_file; }
|
||||
@ -84,17 +79,11 @@ class Mod : public Resource {
|
||||
/** Thread-safe. */
|
||||
QPixmap setIcon(QImage new_image) const;
|
||||
|
||||
auto metadata() -> std::shared_ptr<Metadata::ModStruct>;
|
||||
auto metadata() const -> const std::shared_ptr<Metadata::ModStruct>;
|
||||
|
||||
void setStatus(ModStatus status);
|
||||
void setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata);
|
||||
void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared<Metadata::ModStruct>(metadata)); }
|
||||
void setDetails(const ModDetails& details);
|
||||
|
||||
bool valid() const override;
|
||||
|
||||
[[nodiscard]] int compare(Resource const& other, SortType type) const override;
|
||||
[[nodiscard]] int compare(const Resource & other, SortType type) const override;
|
||||
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
||||
|
||||
// Delete all the files of this mod
|
||||
|
@ -43,13 +43,6 @@
|
||||
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
|
||||
enum class ModStatus {
|
||||
Installed, // Both JAR and Metadata are present
|
||||
NotInstalled, // Only the Metadata is present
|
||||
NoMetadata, // Only the JAR is present
|
||||
Unknown, // Default status
|
||||
};
|
||||
|
||||
struct ModLicense {
|
||||
QString name = {};
|
||||
QString id = {};
|
||||
@ -149,12 +142,6 @@ struct ModDetails {
|
||||
/* Path of mod logo */
|
||||
QString icon_file = {};
|
||||
|
||||
/* Installation status of the mod */
|
||||
ModStatus status = ModStatus::Unknown;
|
||||
|
||||
/* Metadata information, if any */
|
||||
std::shared_ptr<Metadata::ModStruct> metadata = nullptr;
|
||||
|
||||
ModDetails() = default;
|
||||
|
||||
/** Metadata should be handled manually to properly set the mod status. */
|
||||
@ -169,40 +156,9 @@ struct ModDetails {
|
||||
, issue_tracker(other.issue_tracker)
|
||||
, licenses(other.licenses)
|
||||
, icon_file(other.icon_file)
|
||||
, status(other.status)
|
||||
{}
|
||||
|
||||
ModDetails& operator=(const ModDetails& other)
|
||||
{
|
||||
this->mod_id = other.mod_id;
|
||||
this->name = other.name;
|
||||
this->version = other.version;
|
||||
this->mcversion = other.mcversion;
|
||||
this->homeurl = other.homeurl;
|
||||
this->description = other.description;
|
||||
this->authors = other.authors;
|
||||
this->issue_tracker = other.issue_tracker;
|
||||
this->licenses = other.licenses;
|
||||
this->icon_file = other.icon_file;
|
||||
this->status = other.status;
|
||||
ModDetails& operator=(const ModDetails& other) = default;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ModDetails& operator=(const ModDetails&& other)
|
||||
{
|
||||
this->mod_id = other.mod_id;
|
||||
this->name = other.name;
|
||||
this->version = other.version;
|
||||
this->mcversion = other.mcversion;
|
||||
this->homeurl = other.homeurl;
|
||||
this->description = other.description;
|
||||
this->authors = other.authors;
|
||||
this->issue_tracker = other.issue_tracker;
|
||||
this->licenses = other.licenses;
|
||||
this->icon_file = other.icon_file;
|
||||
this->status = other.status;
|
||||
|
||||
return *this;
|
||||
}
|
||||
ModDetails& operator=(ModDetails&& other) = default;
|
||||
};
|
||||
|
@ -48,21 +48,19 @@
|
||||
#include <QThreadPool>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "minecraft/mod/Resource.h"
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.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)
|
||||
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
|
||||
ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
|
||||
: ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent)
|
||||
{
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size", "Side", "Loaders",
|
||||
"Minecraft Versions", "Release Type" });
|
||||
@ -92,7 +90,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
case NameColumn:
|
||||
return m_resources[row]->name();
|
||||
case VersionColumn: {
|
||||
switch (m_resources[row]->type()) {
|
||||
switch (at(row).type()) {
|
||||
case ResourceType::FOLDER:
|
||||
return tr("Folder");
|
||||
case ResourceType::SINGLEFILE:
|
||||
@ -100,64 +98,50 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return at(row)->version();
|
||||
return at(row).version();
|
||||
}
|
||||
case DateColumn:
|
||||
return m_resources[row]->dateTimeChanged();
|
||||
return at(row).dateTimeChanged();
|
||||
case ProviderColumn: {
|
||||
auto provider = at(row)->provider();
|
||||
if (!provider.has_value()) {
|
||||
//: Unknown mod provider (i.e. not Modrinth, CurseForge, etc...)
|
||||
return tr("Unknown");
|
||||
}
|
||||
|
||||
return provider.value();
|
||||
return at(row).provider();
|
||||
}
|
||||
case SideColumn: {
|
||||
return Metadata::modSideToString(at(row)->side());
|
||||
return at(row).side();
|
||||
}
|
||||
case LoadersColumn: {
|
||||
QStringList loaders;
|
||||
auto modLoaders = at(row)->loaders();
|
||||
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader,
|
||||
ModPlatform::Fabric, ModPlatform::Quilt }) {
|
||||
if (modLoaders & loader) {
|
||||
loaders << getModLoaderAsString(loader);
|
||||
}
|
||||
}
|
||||
return loaders.join(", ");
|
||||
return at(row).loaders();
|
||||
}
|
||||
case McVersionsColumn: {
|
||||
return at(row)->mcVersions().join(", ");
|
||||
return at(row).mcVersions();
|
||||
}
|
||||
case ReleaseTypeColumn: {
|
||||
return at(row)->releaseType().toString();
|
||||
return at(row).releaseType();
|
||||
}
|
||||
case SizeColumn:
|
||||
return m_resources[row]->sizeStr();
|
||||
return at(row).sizeStr();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
if (column == NameColumn) {
|
||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||
if (at(row).isSymLinkUnder(instDirPath())) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||
"\nCanonical Path: %1")
|
||||
.arg(at(row)->fileinfo().canonicalFilePath());
|
||||
.arg(at(row).fileinfo().canonicalFilePath());
|
||||
}
|
||||
if (at(row)->isMoreThanOneHardLink()) {
|
||||
if (at(row).isMoreThanOneHardLink()) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||
}
|
||||
}
|
||||
return m_resources[row]->internal_id();
|
||||
case Qt::DecorationRole: {
|
||||
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||
if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
|
||||
return APPLICATION->getThemedIcon("status-yellow");
|
||||
if (column == ImageColumn) {
|
||||
return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
return at(row).icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -169,7 +153,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const
|
||||
case Qt::CheckStateRole:
|
||||
switch (column) {
|
||||
case ActiveColumn:
|
||||
return at(row)->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
return at(row).enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@ -210,7 +194,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio
|
||||
case DateColumn:
|
||||
return tr("The date and time this mod was last changed (or added).");
|
||||
case ProviderColumn:
|
||||
return tr("Where the mod was downloaded from.");
|
||||
return tr("The source provider of the mod.");
|
||||
case SideColumn:
|
||||
return tr("On what environment the mod is running.");
|
||||
case LoadersColumn:
|
||||
@ -235,133 +219,16 @@ int ModFolderModel::columnCount(const QModelIndex& parent) const
|
||||
return parent.isValid() ? 0 : NUM_COLUMNS;
|
||||
}
|
||||
|
||||
Task* ModFolderModel::createUpdateTask()
|
||||
{
|
||||
auto index_dir = indexDir();
|
||||
auto task = new ModFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load);
|
||||
m_first_folder_load = false;
|
||||
return task;
|
||||
}
|
||||
|
||||
Task* ModFolderModel::createParseTask(Resource& resource)
|
||||
{
|
||||
return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo());
|
||||
}
|
||||
|
||||
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
|
||||
{
|
||||
for (auto mod : allMods()) {
|
||||
if (mod->getOriginalFileName() == filename) {
|
||||
auto index_dir = indexDir();
|
||||
mod->destroy(index_dir, preserve_metadata, false);
|
||||
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
return true;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0) {
|
||||
continue;
|
||||
}
|
||||
auto m = at(i.row());
|
||||
auto index_dir = indexDir();
|
||||
m->destroy(index_dir);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderModel::deleteModsMetadata(const QModelIndexList& indexes)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
return true;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0) {
|
||||
continue;
|
||||
}
|
||||
auto m = at(i.row());
|
||||
auto index_dir = indexDir();
|
||||
m->destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderModel::isValid()
|
||||
{
|
||||
return m_dir.exists() && m_dir.isReadable();
|
||||
}
|
||||
|
||||
bool ModFolderModel::startWatching()
|
||||
{
|
||||
// Remove orphaned metadata next time
|
||||
m_first_folder_load = true;
|
||||
return ResourceFolderModel::startWatching({ m_dir.absolutePath(), indexDir().absolutePath() });
|
||||
}
|
||||
|
||||
bool ModFolderModel::stopWatching()
|
||||
{
|
||||
return ResourceFolderModel::stopWatching({ m_dir.absolutePath(), indexDir().absolutePath() });
|
||||
}
|
||||
|
||||
auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList<Mod*>
|
||||
{
|
||||
QList<Mod*> selected_resources;
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0)
|
||||
continue;
|
||||
|
||||
selected_resources.push_back(at(i.row()));
|
||||
}
|
||||
return selected_resources;
|
||||
}
|
||||
|
||||
auto ModFolderModel::allMods() -> QList<Mod*>
|
||||
{
|
||||
QList<Mod*> mods;
|
||||
|
||||
for (auto& res : qAsConst(m_resources)) {
|
||||
mods.append(static_cast<Mod*>(res.get()));
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
void ModFolderModel::onUpdateSucceeded()
|
||||
{
|
||||
auto update_results = static_cast<ModFolderLoadTask*>(m_current_update_task.get())->result();
|
||||
|
||||
auto& new_mods = update_results->mods;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
auto current_list = m_resources_index.keys();
|
||||
QSet<QString> current_set(current_list.begin(), current_list.end());
|
||||
|
||||
auto new_list = new_mods.keys();
|
||||
QSet<QString> new_set(new_list.begin(), new_list.end());
|
||||
#else
|
||||
QSet<QString> current_set(m_resources_index.keys().toSet());
|
||||
QSet<QString> new_set(new_mods.keys().toSet());
|
||||
#endif
|
||||
|
||||
applyUpdates(current_set, new_set, new_mods);
|
||||
}
|
||||
|
||||
void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
|
||||
{
|
||||
auto iter = m_active_parse_tasks.constFind(ticket);
|
||||
@ -383,47 +250,3 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id)
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -48,10 +48,9 @@
|
||||
#include "ResourceFolderModel.h"
|
||||
|
||||
#include "minecraft/mod/tasks/LocalModParseTask.h"
|
||||
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
|
||||
#include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
class LegacyInstance;
|
||||
class BaseInstance;
|
||||
class QFileSystemWatcher;
|
||||
|
||||
@ -76,8 +75,7 @@ class ModFolderModel : public ResourceFolderModel {
|
||||
ReleaseTypeColumn,
|
||||
NUM_COLUMNS
|
||||
};
|
||||
enum ModStatusAction { Disable, Enable, Toggle };
|
||||
ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true);
|
||||
ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
|
||||
|
||||
virtual QString id() const override { return "mods"; }
|
||||
|
||||
@ -86,34 +84,13 @@ class ModFolderModel : public ResourceFolderModel {
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
|
||||
[[nodiscard]] Task* createUpdateTask() override;
|
||||
[[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new Mod(file); }
|
||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||
|
||||
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);
|
||||
|
||||
/// Deletes all the selected mods
|
||||
bool deleteMods(const QModelIndexList& indexes);
|
||||
bool deleteModsMetadata(const QModelIndexList& indexes);
|
||||
|
||||
bool isValid();
|
||||
|
||||
bool startWatching() override;
|
||||
bool stopWatching() override;
|
||||
|
||||
QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; }
|
||||
|
||||
auto selectedMods(QModelIndexList& indexes) -> QList<Mod*>;
|
||||
auto allMods() -> QList<Mod*>;
|
||||
|
||||
RESOURCE_HELPERS(Mod)
|
||||
|
||||
private slots:
|
||||
void onUpdateSucceeded() override;
|
||||
void onParseSucceeded(int ticket, QString resource_id) override;
|
||||
|
||||
protected:
|
||||
bool m_is_indexed;
|
||||
bool m_first_folder_load = true;
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ void Resource::setFile(QFileInfo file_info)
|
||||
parseFile();
|
||||
}
|
||||
|
||||
std::tuple<QString, qint64> calculateFileSize(const QFileInfo& file)
|
||||
static std::tuple<QString, qint64> calculateFileSize(const QFileInfo& file)
|
||||
{
|
||||
if (file.isDir()) {
|
||||
auto dir = QDir(file.absoluteFilePath());
|
||||
@ -72,6 +72,14 @@ void Resource::parseFile()
|
||||
m_changed_date_time = m_file_info.lastModified();
|
||||
}
|
||||
|
||||
auto Resource::name() const -> QString
|
||||
{
|
||||
if (metadata())
|
||||
return metadata()->name;
|
||||
|
||||
return m_name;
|
||||
}
|
||||
|
||||
static void removeThePrefix(QString& string)
|
||||
{
|
||||
QRegularExpression regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption);
|
||||
@ -79,6 +87,30 @@ static void removeThePrefix(QString& string)
|
||||
string = string.trimmed();
|
||||
}
|
||||
|
||||
auto Resource::provider() const -> QString
|
||||
{
|
||||
if (metadata())
|
||||
return ModPlatform::ProviderCapabilities::readableName(metadata()->provider);
|
||||
|
||||
return tr("Unknown");
|
||||
}
|
||||
|
||||
auto Resource::homepage() const -> QString
|
||||
{
|
||||
if (metadata())
|
||||
return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Resource::setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata)
|
||||
{
|
||||
if (status() == ResourceStatus::NO_METADATA)
|
||||
setStatus(ResourceStatus::INSTALLED);
|
||||
|
||||
m_metadata = metadata;
|
||||
}
|
||||
|
||||
int Resource::compare(const Resource& other, SortType type) const
|
||||
{
|
||||
switch (type) {
|
||||
@ -93,6 +125,7 @@ int Resource::compare(const Resource& other, SortType type) const
|
||||
QString this_name{ name() };
|
||||
QString other_name{ other.name() };
|
||||
|
||||
// TODO do we need this? it could result in 0 being returned
|
||||
removeThePrefix(this_name);
|
||||
removeThePrefix(other_name);
|
||||
|
||||
@ -118,6 +151,12 @@ int Resource::compare(const Resource& other, SortType type) const
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
case SortType::PROVIDER: {
|
||||
auto compare_result = QString::compare(provider(), other.provider(), Qt::CaseInsensitive);
|
||||
if (compare_result != 0)
|
||||
return compare_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -174,10 +213,27 @@ bool Resource::enable(EnableAction action)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resource::destroy(bool attemptTrash)
|
||||
auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
|
||||
{
|
||||
m_type = ResourceType::UNKNOWN;
|
||||
return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
|
||||
|
||||
if (!preserve_metadata) {
|
||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||
destroyMetadata(index_dir);
|
||||
}
|
||||
|
||||
return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
|
||||
}
|
||||
|
||||
auto Resource::destroyMetadata(const QDir& index_dir) -> void
|
||||
{
|
||||
if (metadata()) {
|
||||
Metadata::remove(index_dir, metadata()->slug);
|
||||
} else {
|
||||
auto n = name();
|
||||
Metadata::remove(index_dir, n);
|
||||
}
|
||||
m_metadata = nullptr;
|
||||
}
|
||||
|
||||
bool Resource::isSymLinkUnder(const QString& instPath) const
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
#include "MetadataHandler.h"
|
||||
#include "ModDetails.h"
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
enum class ResourceType {
|
||||
@ -50,7 +52,14 @@ enum class ResourceType {
|
||||
LITEMOD, //!< The resource is a litemod
|
||||
};
|
||||
|
||||
enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE, SIDE, LOADERS, MC_VERSIONS, RELEASE_TYPE };
|
||||
enum class ResourceStatus {
|
||||
INSTALLED, // Both JAR and Metadata are present
|
||||
NOT_INSTALLED, // Only the Metadata is present
|
||||
NO_METADATA, // Only the JAR is present
|
||||
UNKNOWN, // Default status
|
||||
};
|
||||
|
||||
enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE, SIDE, MC_VERSIONS, LOADERS, RELEASE_TYPE };
|
||||
|
||||
enum class EnableAction { ENABLE, DISABLE, TOGGLE };
|
||||
|
||||
@ -84,9 +93,19 @@ class Resource : public QObject {
|
||||
[[nodiscard]] QString sizeStr() const { return m_size_str; }
|
||||
[[nodiscard]] qint64 sizeInfo() const { return m_size_info; }
|
||||
|
||||
[[nodiscard]] virtual auto name() const -> QString { return m_name; }
|
||||
[[nodiscard]] virtual auto name() const -> QString;
|
||||
[[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; }
|
||||
|
||||
[[nodiscard]] auto status() const -> ResourceStatus { return m_status; };
|
||||
[[nodiscard]] auto metadata() -> std::shared_ptr<Metadata::ModStruct> { return m_metadata; }
|
||||
[[nodiscard]] auto metadata() const -> std::shared_ptr<const Metadata::ModStruct> { return m_metadata; }
|
||||
[[nodiscard]] auto provider() const -> QString;
|
||||
[[nodiscard]] virtual auto homepage() const -> QString;
|
||||
|
||||
void setStatus(ResourceStatus status) { m_status = status; }
|
||||
void setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata);
|
||||
void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared<Metadata::ModStruct>(metadata)); }
|
||||
|
||||
/** Compares two Resources, for sorting purposes, considering a ascending order, returning:
|
||||
* > 0: 'this' comes after 'other'
|
||||
* = 0: 'this' is equal to 'other'
|
||||
@ -117,7 +136,9 @@ class Resource : public QObject {
|
||||
}
|
||||
|
||||
// Delete all files of this resource.
|
||||
bool destroy(bool attemptTrash = true);
|
||||
auto destroy(const QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
||||
// Delete the metadata only.
|
||||
auto destroyMetadata(const QDir& index_dir) -> void;
|
||||
|
||||
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
||||
|
||||
@ -146,6 +167,11 @@ class Resource : public QObject {
|
||||
/* The type of file we're dealing with. */
|
||||
ResourceType m_type = ResourceType::UNKNOWN;
|
||||
|
||||
/* Installation status of the resource. */
|
||||
ResourceStatus m_status = ResourceStatus::UNKNOWN;
|
||||
|
||||
std::shared_ptr<Metadata::ModStruct> m_metadata = nullptr;
|
||||
|
||||
/* Whether the resource is enabled (e.g. shows up in the game) or not. */
|
||||
bool m_enabled = true;
|
||||
|
||||
|
@ -11,20 +11,25 @@
|
||||
#include <QStyle>
|
||||
#include <QThreadPool>
|
||||
#include <QUrl>
|
||||
#include <utility>
|
||||
|
||||
#include "Application.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include "QVariantUtils.h"
|
||||
#include "StringUtils.h"
|
||||
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
||||
#include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
#include "settings/Setting.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
|
||||
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
|
||||
ResourceFolderModel::ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
|
||||
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this), m_is_indexed(is_indexed)
|
||||
{
|
||||
if (create_dir) {
|
||||
FS::ensureFolderPathExists(m_dir.absolutePath());
|
||||
@ -48,6 +53,9 @@ ResourceFolderModel::~ResourceFolderModel()
|
||||
|
||||
bool ResourceFolderModel::startWatching(const QStringList& paths)
|
||||
{
|
||||
// Remove orphaned metadata next time
|
||||
m_first_folder_load = true;
|
||||
|
||||
if (m_is_watching)
|
||||
return false;
|
||||
|
||||
@ -158,11 +166,55 @@ bool ResourceFolderModel::installResource(QString original_path)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::uninstallResource(QString file_name)
|
||||
bool ResourceFolderModel::installResourceWithFlameMetadata(QString 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();
|
||||
}
|
||||
LocalResourceUpdateTask update_metadata(indexDir(), pack, vers);
|
||||
QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit);
|
||||
update_metadata.start();
|
||||
});
|
||||
|
||||
job->start();
|
||||
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
return installResource(std::move(path));
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::uninstallResource(QString file_name, bool preserve_metadata)
|
||||
{
|
||||
for (auto& resource : m_resources) {
|
||||
if (resource->fileinfo().fileName() == file_name) {
|
||||
auto res = resource->destroy(false);
|
||||
auto res = resource->destroy(indexDir(), preserve_metadata, false);
|
||||
|
||||
update();
|
||||
|
||||
@ -178,13 +230,11 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
|
||||
return true;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0) {
|
||||
if (i.column() != 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& resource = m_resources.at(i.row());
|
||||
|
||||
resource->destroy();
|
||||
resource->destroy(indexDir());
|
||||
}
|
||||
|
||||
update();
|
||||
@ -192,6 +242,22 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
return;
|
||||
|
||||
for (auto i : indexes) {
|
||||
if (i.column() != 0)
|
||||
continue;
|
||||
|
||||
auto& resource = m_resources.at(i.row());
|
||||
resource->destroyMetadata(indexDir());
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action)
|
||||
{
|
||||
if (indexes.isEmpty())
|
||||
@ -299,7 +365,7 @@ void ResourceFolderModel::resolveResource(Resource* res)
|
||||
|
||||
void ResourceFolderModel::onUpdateSucceeded()
|
||||
{
|
||||
auto update_results = static_cast<BasicFolderLoadTask*>(m_current_update_task.get())->result();
|
||||
auto update_results = static_cast<ResourceFolderLoadTask*>(m_current_update_task.get())->result();
|
||||
|
||||
auto& new_resources = update_results->resources;
|
||||
|
||||
@ -329,7 +395,11 @@ void ResourceFolderModel::onParseSucceeded(int ticket, QString resource_id)
|
||||
|
||||
Task* ResourceFolderModel::createUpdateTask()
|
||||
{
|
||||
return new BasicFolderLoadTask(m_dir);
|
||||
auto index_dir = indexDir();
|
||||
auto task = new ResourceFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load,
|
||||
[this](const QFileInfo& file) { return createResource(file); });
|
||||
m_first_folder_load = false;
|
||||
return task;
|
||||
}
|
||||
|
||||
bool ResourceFolderModel::hasPendingParseTasks() const
|
||||
@ -419,6 +489,8 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
|
||||
return m_resources[row]->name();
|
||||
case DateColumn:
|
||||
return m_resources[row]->dateTimeChanged();
|
||||
case ProviderColumn:
|
||||
return m_resources[row]->provider();
|
||||
case SizeColumn:
|
||||
return m_resources[row]->sizeStr();
|
||||
default:
|
||||
@ -490,22 +562,23 @@ QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orien
|
||||
case ActiveColumn:
|
||||
case NameColumn:
|
||||
case DateColumn:
|
||||
case ProviderColumn:
|
||||
case SizeColumn:
|
||||
return columnNames().at(section);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
case Qt::ToolTipRole: {
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
switch (section) {
|
||||
case ActiveColumn:
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
return tr("Is the resource enabled?");
|
||||
case NameColumn:
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
return tr("The name of the resource.");
|
||||
case DateColumn:
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
return tr("The date and time this resource was last changed (or added).");
|
||||
case ProviderColumn:
|
||||
return tr("The source provider of the resource.");
|
||||
case SizeColumn:
|
||||
return tr("The size of the resource.");
|
||||
default:
|
||||
|
@ -19,6 +19,58 @@
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
|
||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||
#define RESOURCE_HELPERS(T) \
|
||||
[[nodiscard]] T& operator[](int index) \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] T& at(int index) \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] const T& at(int index) const \
|
||||
{ \
|
||||
return *static_cast<const T*>(m_resources.at(index).get()); \
|
||||
} \
|
||||
[[nodiscard]] T& first() \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources.first().get()); \
|
||||
} \
|
||||
[[nodiscard]] T& last() \
|
||||
{ \
|
||||
return *static_cast<T*>(m_resources.last().get()); \
|
||||
} \
|
||||
[[nodiscard]] T* find(QString id) \
|
||||
{ \
|
||||
auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), \
|
||||
[id](Resource::Ptr const& r) { return r->internal_id() == id; }); \
|
||||
if (iter == m_resources.constEnd()) \
|
||||
return nullptr; \
|
||||
return static_cast<T*>((*iter).get()); \
|
||||
} \
|
||||
QList<T*> selected##T##s(const QModelIndexList& indexes) \
|
||||
{ \
|
||||
QList<T*> result; \
|
||||
for (const QModelIndex& index : indexes) { \
|
||||
if (index.column() != 0) \
|
||||
continue; \
|
||||
\
|
||||
result.append(&at(index.row())); \
|
||||
} \
|
||||
return result; \
|
||||
} \
|
||||
QList<T*> all##T##s() \
|
||||
{ \
|
||||
QList<T*> result; \
|
||||
result.reserve(m_resources.size()); \
|
||||
\
|
||||
for (const Resource::Ptr& resource : m_resources) \
|
||||
result.append(static_cast<T*>(resource.get())); \
|
||||
\
|
||||
return result; \
|
||||
}
|
||||
|
||||
/** A basic model for external resources.
|
||||
*
|
||||
* This model manages a list of resources. As such, external users of such resources do not own them,
|
||||
@ -29,7 +81,7 @@ class QSortFilterProxyModel;
|
||||
class ResourceFolderModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true);
|
||||
ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
|
||||
~ResourceFolderModel() override;
|
||||
|
||||
virtual QString id() const { return "resource"; }
|
||||
@ -49,8 +101,10 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
bool stopWatching(const QStringList& paths);
|
||||
|
||||
/* Helper methods for subclasses, using a predetermined list of paths. */
|
||||
virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); }
|
||||
virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); }
|
||||
virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
|
||||
virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); }
|
||||
|
||||
QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; }
|
||||
|
||||
/** Given a path in the system, install that resource, moving it to its place in the
|
||||
* instance file hierarchy.
|
||||
@ -59,12 +113,15 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
*/
|
||||
virtual bool installResource(QString path);
|
||||
|
||||
virtual bool installResourceWithFlameMetadata(QString path, ModPlatform::IndexedVersion& vers);
|
||||
|
||||
/** Uninstall (i.e. remove all data about it) a resource, given its file name.
|
||||
*
|
||||
* Returns whether the removal was successful.
|
||||
*/
|
||||
virtual bool uninstallResource(QString file_name);
|
||||
virtual bool uninstallResource(QString file_name, bool preserve_metadata = false);
|
||||
virtual bool deleteResources(const QModelIndexList&);
|
||||
virtual void deleteMetadata(const QModelIndexList&);
|
||||
|
||||
/** Applies the given 'action' to the resources in 'indexes'.
|
||||
*
|
||||
@ -80,9 +137,7 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
|
||||
[[nodiscard]] qsizetype size() const { return m_resources.size(); }
|
||||
[[nodiscard]] bool empty() const { return size() == 0; }
|
||||
[[nodiscard]] Resource& at(int index) { return *m_resources.at(index); }
|
||||
[[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); }
|
||||
[[nodiscard]] QList<Resource::Ptr> const& all() const { return m_resources; }
|
||||
RESOURCE_HELPERS(Resource)
|
||||
|
||||
[[nodiscard]] QDir const& dir() const { return m_dir; }
|
||||
|
||||
@ -96,7 +151,8 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
/* Qt behavior */
|
||||
|
||||
/* Basic columns */
|
||||
enum Columns { ActiveColumn = 0, NameColumn, DateColumn, SizeColumn, NUM_COLUMNS };
|
||||
enum Columns { ActiveColumn = 0, NameColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS };
|
||||
|
||||
QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; }
|
||||
|
||||
[[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast<int>(size()); }
|
||||
@ -153,7 +209,9 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
* This Task is normally executed when opening a page, so it shouldn't contain much heavy work.
|
||||
* If such work is needed, try using it in the Task create by createParseTask() instead!
|
||||
*/
|
||||
[[nodiscard]] virtual Task* createUpdateTask();
|
||||
[[nodiscard]] Task* createUpdateTask();
|
||||
|
||||
[[nodiscard]] virtual Resource* createResource(const QFileInfo& info) { return new Resource(info); }
|
||||
|
||||
/** This creates a new parse task to be executed by onUpdateSucceeded().
|
||||
*
|
||||
@ -195,19 +253,22 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
protected:
|
||||
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
|
||||
// As such, the order in with they appear is very important!
|
||||
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::SIZE };
|
||||
QStringList m_column_names = { "Enable", "Name", "Last Modified", "Size" };
|
||||
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Size") };
|
||||
QList<SortType> m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE };
|
||||
QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider", "Size" };
|
||||
QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") };
|
||||
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||
QHeaderView::Interactive };
|
||||
QList<bool> m_columnsHideable = { false, false, true, true };
|
||||
QList<bool> m_columnsHiddenByDefault = { false, false, false, false };
|
||||
QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
QList<bool> m_columnsHideable = { false, false, true, true, true };
|
||||
QList<bool> m_columnsHiddenByDefault = { false, false, false, false, true };
|
||||
|
||||
QDir m_dir;
|
||||
BaseInstance* m_instance;
|
||||
QFileSystemWatcher m_watcher;
|
||||
bool m_is_watching = false;
|
||||
|
||||
bool m_is_indexed;
|
||||
bool m_first_folder_load = true;
|
||||
|
||||
Task::Ptr m_current_update_task = nullptr;
|
||||
bool m_scheduled_update = false;
|
||||
|
||||
@ -221,37 +282,6 @@ class ResourceFolderModel : public QAbstractListModel {
|
||||
std::atomic<int> m_next_resolution_ticket = 0;
|
||||
};
|
||||
|
||||
/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */
|
||||
#define RESOURCE_HELPERS(T) \
|
||||
[[nodiscard]] T* operator[](int index) \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] T* at(int index) \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources[index].get()); \
|
||||
} \
|
||||
[[nodiscard]] const T* at(int index) const \
|
||||
{ \
|
||||
return static_cast<const T*>(m_resources.at(index).get()); \
|
||||
} \
|
||||
[[nodiscard]] T* first() \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources.first().get()); \
|
||||
} \
|
||||
[[nodiscard]] T* last() \
|
||||
{ \
|
||||
return static_cast<T*>(m_resources.last().get()); \
|
||||
} \
|
||||
[[nodiscard]] T* find(QString id) \
|
||||
{ \
|
||||
auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), \
|
||||
[id](Resource::Ptr const& r) { return r->internal_id() == id; }); \
|
||||
if (iter == m_resources.constEnd()) \
|
||||
return nullptr; \
|
||||
return static_cast<T*>((*iter).get()); \
|
||||
}
|
||||
|
||||
/* Template definition to avoid some code duplication */
|
||||
template <typename T>
|
||||
void ResourceFolderModel::applyUpdates(QSet<QString>& current_set, QSet<QString>& new_set, QMap<QString, T>& new_resources)
|
||||
|
@ -44,19 +44,20 @@
|
||||
#include "Application.h"
|
||||
#include "Version.h"
|
||||
|
||||
#include "minecraft/mod/Resource.h"
|
||||
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
||||
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
|
||||
#include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
|
||||
|
||||
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance)
|
||||
ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
|
||||
: ResourceFolderModel(dir, instance, is_indexed, create_dir, parent)
|
||||
{
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Size" });
|
||||
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Size") });
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::SIZE };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Provider", "Size" });
|
||||
m_column_names_translated =
|
||||
QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider"), tr("Size") });
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT,
|
||||
SortType::DATE, SortType::PROVIDER, SortType::SIZE };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
m_columnsHideable = { false, true, false, true, true, true };
|
||||
m_columnsHiddenByDefault = { false, false, false, false, false, false };
|
||||
m_columnsHideable = { false, true, false, true, true, true, true };
|
||||
}
|
||||
|
||||
QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
@ -73,12 +74,12 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
case NameColumn:
|
||||
return m_resources[row]->name();
|
||||
case PackFormatColumn: {
|
||||
auto resource = at(row);
|
||||
auto pack_format = resource->packFormat();
|
||||
auto& resource = at(row);
|
||||
auto pack_format = resource.packFormat();
|
||||
if (pack_format == 0)
|
||||
return tr("Unrecognized");
|
||||
|
||||
auto version_bounds = resource->compatibleVersions();
|
||||
auto version_bounds = resource.compatibleVersions();
|
||||
if (version_bounds.first.toString().isEmpty())
|
||||
return QString::number(pack_format);
|
||||
|
||||
@ -87,17 +88,18 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
case DateColumn:
|
||||
return m_resources[row]->dateTimeChanged();
|
||||
case ProviderColumn:
|
||||
return m_resources[row]->provider();
|
||||
case SizeColumn:
|
||||
return m_resources[row]->sizeStr();
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
case Qt::DecorationRole: {
|
||||
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||
if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
|
||||
return APPLICATION->getThemedIcon("status-yellow");
|
||||
if (column == ImageColumn) {
|
||||
return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -107,14 +109,14 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
|
||||
}
|
||||
if (column == NameColumn) {
|
||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||
if (at(row).isSymLinkUnder(instDirPath())) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||
"\nCanonical Path: %1")
|
||||
.arg(at(row)->fileinfo().canonicalFilePath());
|
||||
.arg(at(row).fileinfo().canonicalFilePath());
|
||||
;
|
||||
}
|
||||
if (at(row)->isMoreThanOneHardLink()) {
|
||||
if (at(row).isMoreThanOneHardLink()) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||
}
|
||||
@ -129,7 +131,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
case Qt::CheckStateRole:
|
||||
switch (column) {
|
||||
case ActiveColumn:
|
||||
return at(row)->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
return at(row).enabled() ? Qt::Checked : Qt::Unchecked;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
@ -148,6 +150,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O
|
||||
case PackFormatColumn:
|
||||
case DateColumn:
|
||||
case ImageColumn:
|
||||
case ProviderColumn:
|
||||
case SizeColumn:
|
||||
return columnNames().at(section);
|
||||
default:
|
||||
@ -157,7 +160,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O
|
||||
case Qt::ToolTipRole:
|
||||
switch (section) {
|
||||
case ActiveColumn:
|
||||
return tr("Is the resource pack enabled? (Only valid for ZIPs)");
|
||||
return tr("Is the resource pack enabled?");
|
||||
case NameColumn:
|
||||
return tr("The name of the resource pack.");
|
||||
case PackFormatColumn:
|
||||
@ -165,6 +168,8 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O
|
||||
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
|
||||
case DateColumn:
|
||||
return tr("The date and time this resource pack was last changed (or added).");
|
||||
case ProviderColumn:
|
||||
return tr("The source provider of the resource pack.");
|
||||
case SizeColumn:
|
||||
return tr("The size of the resource pack.");
|
||||
default:
|
||||
@ -185,11 +190,6 @@ int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const
|
||||
return parent.isValid() ? 0 : NUM_COLUMNS;
|
||||
}
|
||||
|
||||
Task* ResourcePackFolderModel::createUpdateTask()
|
||||
{
|
||||
return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared<ResourcePack>(entry); });
|
||||
}
|
||||
|
||||
Task* ResourcePackFolderModel::createParseTask(Resource& resource)
|
||||
{
|
||||
return new LocalResourcePackParseTask(m_next_resolution_ticket, static_cast<ResourcePack&>(resource));
|
||||
|
@ -7,18 +7,18 @@
|
||||
class ResourcePackFolderModel : public ResourceFolderModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, SizeColumn, NUM_COLUMNS };
|
||||
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS };
|
||||
|
||||
explicit ResourcePackFolderModel(const QString& dir, BaseInstance* instance);
|
||||
explicit ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
|
||||
|
||||
virtual QString id() const override { return "resourcepacks"; }
|
||||
QString id() const override { return "resourcepacks"; }
|
||||
|
||||
[[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
[[nodiscard]] int columnCount(const QModelIndex& parent) const override;
|
||||
|
||||
[[nodiscard]] Task* createUpdateTask() override;
|
||||
[[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new ResourcePack(file); }
|
||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||
|
||||
RESOURCE_HELPERS(ResourcePack)
|
||||
|
@ -2,24 +2,24 @@
|
||||
|
||||
#include "ResourceFolderModel.h"
|
||||
#include "minecraft/mod/ShaderPack.h"
|
||||
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
||||
#include "minecraft/mod/tasks/LocalShaderPackParseTask.h"
|
||||
|
||||
class ShaderPackFolderModel : public ResourceFolderModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {}
|
||||
explicit ShaderPackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr)
|
||||
: ResourceFolderModel(dir, instance, is_indexed, create_dir, parent)
|
||||
{}
|
||||
|
||||
virtual QString id() const override { return "shaderpacks"; }
|
||||
|
||||
[[nodiscard]] Task* createUpdateTask() override
|
||||
{
|
||||
return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared<ShaderPack>(entry); });
|
||||
}
|
||||
[[nodiscard]] Resource* createResource(const QFileInfo& info) override { return new ShaderPack(info); }
|
||||
|
||||
[[nodiscard]] Task* createParseTask(Resource& resource) override
|
||||
{
|
||||
return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast<ShaderPack&>(resource));
|
||||
}
|
||||
|
||||
RESOURCE_HELPERS(ShaderPack);
|
||||
};
|
||||
|
@ -39,22 +39,18 @@
|
||||
|
||||
#include "TexturePackFolderModel.h"
|
||||
|
||||
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
|
||||
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
|
||||
#include "minecraft/mod/tasks/ResourceFolderLoadTask.h"
|
||||
|
||||
TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance)
|
||||
TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent)
|
||||
: ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent)
|
||||
{
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" });
|
||||
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") });
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive,
|
||||
QHeaderView::Interactive };
|
||||
m_columnsHideable = { false, true, false, true, true };
|
||||
}
|
||||
|
||||
Task* TexturePackFolderModel::createUpdateTask()
|
||||
{
|
||||
return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared<TexturePack>(entry); });
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Provider", "Size" });
|
||||
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") });
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
m_columnsHideable = { false, true, false, true, true, true };
|
||||
m_columnsHiddenByDefault = { false, false, false, false, false, true };
|
||||
}
|
||||
|
||||
Task* TexturePackFolderModel::createParseTask(Resource& resource)
|
||||
@ -77,6 +73,8 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
return m_resources[row]->name();
|
||||
case DateColumn:
|
||||
return m_resources[row]->dateTimeChanged();
|
||||
case ProviderColumn:
|
||||
return m_resources[row]->provider();
|
||||
case SizeColumn:
|
||||
return m_resources[row]->sizeStr();
|
||||
default:
|
||||
@ -84,14 +82,14 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
if (column == NameColumn) {
|
||||
if (at(row)->isSymLinkUnder(instDirPath())) {
|
||||
if (at(row).isSymLinkUnder(instDirPath())) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||
"\nCanonical Path: %1")
|
||||
.arg(at(row)->fileinfo().canonicalFilePath());
|
||||
.arg(at(row).fileinfo().canonicalFilePath());
|
||||
;
|
||||
}
|
||||
if (at(row)->isMoreThanOneHardLink()) {
|
||||
if (at(row).isMoreThanOneHardLink()) {
|
||||
return m_resources[row]->internal_id() +
|
||||
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
|
||||
}
|
||||
@ -99,10 +97,10 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const
|
||||
|
||||
return m_resources[row]->internal_id();
|
||||
case Qt::DecorationRole: {
|
||||
if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
|
||||
if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
|
||||
return APPLICATION->getThemedIcon("status-yellow");
|
||||
if (column == ImageColumn) {
|
||||
return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -130,6 +128,7 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or
|
||||
case NameColumn:
|
||||
case DateColumn:
|
||||
case ImageColumn:
|
||||
case ProviderColumn:
|
||||
case SizeColumn:
|
||||
return columnNames().at(section);
|
||||
default:
|
||||
@ -138,14 +137,13 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or
|
||||
case Qt::ToolTipRole: {
|
||||
switch (section) {
|
||||
case ActiveColumn:
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
return tr("Is the texture pack enabled?");
|
||||
case NameColumn:
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
return tr("The name of the texture pack.");
|
||||
case DateColumn:
|
||||
//: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc.
|
||||
return tr("The date and time this texture pack was last changed (or added).");
|
||||
case ProviderColumn:
|
||||
return tr("The source provider of the texture pack.");
|
||||
case SizeColumn:
|
||||
return tr("The size of the texture pack.");
|
||||
default:
|
||||
|
@ -44,9 +44,9 @@ class TexturePackFolderModel : public ResourceFolderModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, SizeColumn, NUM_COLUMNS };
|
||||
enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS };
|
||||
|
||||
explicit TexturePackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance);
|
||||
explicit TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr);
|
||||
|
||||
virtual QString id() const override { return "texturepacks"; }
|
||||
|
||||
@ -55,8 +55,7 @@ class TexturePackFolderModel : public ResourceFolderModel {
|
||||
[[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
[[nodiscard]] int columnCount(const QModelIndex& parent) const override;
|
||||
|
||||
explicit TexturePackFolderModel(const QString& dir, BaseInstance* instance);
|
||||
[[nodiscard]] Task* createUpdateTask() override;
|
||||
[[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new TexturePack(file); }
|
||||
[[nodiscard]] Task* createParseTask(Resource&) override;
|
||||
|
||||
RESOURCE_HELPERS(TexturePack)
|
||||
|
@ -47,11 +47,6 @@ class LocalModParseTask : public Task {
|
||||
|
||||
[[nodiscard]] int token() const { return m_token; }
|
||||
|
||||
private:
|
||||
void processAsZip();
|
||||
void processAsFolder();
|
||||
void processAsLitemod();
|
||||
|
||||
private:
|
||||
int m_token;
|
||||
ResourceType m_type;
|
||||
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "LocalModUpdateTask.h"
|
||||
#include "LocalResourceUpdateTask.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
@ -26,12 +26,12 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version)
|
||||
: m_index_dir(index_dir), m_mod(mod), m_mod_version(mod_version)
|
||||
LocalResourceUpdateTask::LocalResourceUpdateTask(QDir index_dir, ModPlatform::IndexedPack& project, ModPlatform::IndexedVersion& version)
|
||||
: m_index_dir(index_dir), m_project(project), m_version(version)
|
||||
{
|
||||
// Ensure a '.index' folder exists in the mods folder, and create it if it does not
|
||||
if (!FS::ensureFolderPathExists(index_dir.path())) {
|
||||
emitFailed(QString("Unable to create index for mod %1!").arg(m_mod.name));
|
||||
emitFailed(QString("Unable to create index directory at %1!").arg(index_dir.absolutePath()));
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
@ -39,28 +39,28 @@ LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack&
|
||||
#endif
|
||||
}
|
||||
|
||||
void LocalModUpdateTask::executeTask()
|
||||
void LocalResourceUpdateTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Updating index for mod:\n%1").arg(m_mod.name));
|
||||
setStatus(tr("Updating index for resource:\n%1").arg(m_project.name));
|
||||
|
||||
auto old_metadata = Metadata::get(m_index_dir, m_mod.addonId);
|
||||
auto old_metadata = Metadata::get(m_index_dir, m_project.addonId);
|
||||
if (old_metadata.isValid()) {
|
||||
emit hasOldMod(old_metadata.name, old_metadata.filename);
|
||||
if (m_mod.slug.isEmpty())
|
||||
m_mod.slug = old_metadata.slug;
|
||||
emit hasOldResource(old_metadata.name, old_metadata.filename);
|
||||
if (m_project.slug.isEmpty())
|
||||
m_project.slug = old_metadata.slug;
|
||||
}
|
||||
|
||||
auto pw_mod = Metadata::create(m_index_dir, m_mod, m_mod_version);
|
||||
auto pw_mod = Metadata::create(m_index_dir, m_project, m_version);
|
||||
if (pw_mod.isValid()) {
|
||||
Metadata::update(m_index_dir, pw_mod);
|
||||
emitSucceeded();
|
||||
} else {
|
||||
qCritical() << "Tried to update an invalid mod!";
|
||||
qCritical() << "Tried to update an invalid resource!";
|
||||
emitFailed(tr("Invalid metadata"));
|
||||
}
|
||||
}
|
||||
|
||||
auto LocalModUpdateTask::abort() -> bool
|
||||
auto LocalResourceUpdateTask::abort() -> bool
|
||||
{
|
||||
emitAborted();
|
||||
return true;
|
@ -23,12 +23,12 @@
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class LocalModUpdateTask : public Task {
|
||||
class LocalResourceUpdateTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using Ptr = shared_qobject_ptr<LocalModUpdateTask>;
|
||||
using Ptr = shared_qobject_ptr<LocalResourceUpdateTask>;
|
||||
|
||||
explicit LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version);
|
||||
explicit LocalResourceUpdateTask(QDir index_dir, ModPlatform::IndexedPack& project, ModPlatform::IndexedVersion& version);
|
||||
|
||||
auto canAbort() const -> bool override { return true; }
|
||||
auto abort() -> bool override;
|
||||
@ -38,10 +38,10 @@ class LocalModUpdateTask : public Task {
|
||||
void executeTask() override;
|
||||
|
||||
signals:
|
||||
void hasOldMod(QString name, QString filename);
|
||||
void hasOldResource(QString name, QString filename);
|
||||
|
||||
private:
|
||||
QDir m_index_dir;
|
||||
ModPlatform::IndexedPack m_mod;
|
||||
ModPlatform::IndexedVersion m_mod_version;
|
||||
ModPlatform::IndexedPack m_project;
|
||||
ModPlatform::IndexedVersion m_version;
|
||||
};
|
@ -34,7 +34,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ModFolderLoadTask.h"
|
||||
#include "ResourceFolderLoadTask.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "FileSystem.h"
|
||||
@ -42,17 +42,22 @@
|
||||
|
||||
#include <QThread>
|
||||
|
||||
ModFolderLoadTask::ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan)
|
||||
ResourceFolderLoadTask::ResourceFolderLoadTask(const QDir& resource_dir,
|
||||
const QDir& index_dir,
|
||||
bool is_indexed,
|
||||
bool clean_orphan,
|
||||
std::function<Resource*(const QFileInfo&)> create_function)
|
||||
: Task(nullptr, false)
|
||||
, m_mods_dir(mods_dir)
|
||||
, m_resource_dir(resource_dir)
|
||||
, m_index_dir(index_dir)
|
||||
, m_is_indexed(is_indexed)
|
||||
, m_clean_orphan(clean_orphan)
|
||||
, m_create_func(create_function)
|
||||
, m_result(new Result())
|
||||
, m_thread_to_spawn_into(thread())
|
||||
{}
|
||||
|
||||
void ModFolderLoadTask::executeTask()
|
||||
void ResourceFolderLoadTask::executeTask()
|
||||
{
|
||||
if (thread() != m_thread_to_spawn_into)
|
||||
connect(this, &Task::finished, this->thread(), &QThread::quit);
|
||||
@ -63,8 +68,8 @@ void ModFolderLoadTask::executeTask()
|
||||
}
|
||||
|
||||
// Read JAR files that don't have metadata
|
||||
m_mods_dir.refresh();
|
||||
for (auto entry : m_mods_dir.entryInfoList()) {
|
||||
m_resource_dir.refresh();
|
||||
for (auto entry : m_resource_dir.entryInfoList()) {
|
||||
auto filePath = entry.absoluteFilePath();
|
||||
if (auto app = APPLICATION_DYN; app && app->checkQSavePath(filePath)) {
|
||||
continue;
|
||||
@ -74,32 +79,33 @@ void ModFolderLoadTask::executeTask()
|
||||
FS::move(filePath, newFilePath);
|
||||
entry = QFileInfo(newFilePath);
|
||||
}
|
||||
Mod* mod(new Mod(entry));
|
||||
|
||||
if (mod->enabled()) {
|
||||
if (m_result->mods.contains(mod->internal_id())) {
|
||||
m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed);
|
||||
Resource* resource = m_create_func(entry);
|
||||
|
||||
if (resource->enabled()) {
|
||||
if (m_result->resources.contains(resource->internal_id())) {
|
||||
m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED);
|
||||
// Delete the object we just created, since a valid one is already in the mods list.
|
||||
delete mod;
|
||||
delete resource;
|
||||
} else {
|
||||
m_result->mods[mod->internal_id()].reset(std::move(mod));
|
||||
m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata);
|
||||
m_result->resources[resource->internal_id()].reset(resource);
|
||||
m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA);
|
||||
}
|
||||
} else {
|
||||
QString chopped_id = mod->internal_id().chopped(9);
|
||||
if (m_result->mods.contains(chopped_id)) {
|
||||
m_result->mods[mod->internal_id()].reset(std::move(mod));
|
||||
QString chopped_id = resource->internal_id().chopped(9);
|
||||
if (m_result->resources.contains(chopped_id)) {
|
||||
m_result->resources[resource->internal_id()].reset(resource);
|
||||
|
||||
auto metadata = m_result->mods[chopped_id]->metadata();
|
||||
auto metadata = m_result->resources[chopped_id]->metadata();
|
||||
if (metadata) {
|
||||
mod->setMetadata(*metadata);
|
||||
resource->setMetadata(*metadata);
|
||||
|
||||
m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed);
|
||||
m_result->mods.remove(chopped_id);
|
||||
m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED);
|
||||
m_result->resources.remove(chopped_id);
|
||||
}
|
||||
} else {
|
||||
m_result->mods[mod->internal_id()].reset(std::move(mod));
|
||||
m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata);
|
||||
m_result->resources[resource->internal_id()].reset(resource);
|
||||
m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,17 +113,17 @@ void ModFolderLoadTask::executeTask()
|
||||
// Remove orphan metadata to prevent issues
|
||||
// See https://github.com/PolyMC/PolyMC/issues/996
|
||||
if (m_clean_orphan) {
|
||||
QMutableMapIterator iter(m_result->mods);
|
||||
QMutableMapIterator iter(m_result->resources);
|
||||
while (iter.hasNext()) {
|
||||
auto mod = iter.next().value();
|
||||
if (mod->status() == ModStatus::NotInstalled) {
|
||||
mod->destroy(m_index_dir, false, false);
|
||||
auto resource = iter.next().value();
|
||||
if (resource->status() == ResourceStatus::NOT_INSTALLED) {
|
||||
resource->destroy(m_index_dir, false, false);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto mod : m_result->mods)
|
||||
for (auto mod : m_result->resources)
|
||||
mod->moveToThread(m_thread_to_spawn_into);
|
||||
|
||||
if (m_aborted)
|
||||
@ -126,18 +132,18 @@ void ModFolderLoadTask::executeTask()
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void ModFolderLoadTask::getFromMetadata()
|
||||
void ResourceFolderLoadTask::getFromMetadata()
|
||||
{
|
||||
m_index_dir.refresh();
|
||||
for (auto entry : m_index_dir.entryList(QDir::Files)) {
|
||||
auto metadata = Metadata::get(m_index_dir, entry);
|
||||
|
||||
if (!metadata.isValid()) {
|
||||
if (!metadata.isValid())
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* mod = new Mod(m_mods_dir, metadata);
|
||||
mod->setStatus(ModStatus::NotInstalled);
|
||||
m_result->mods[mod->internal_id()].reset(std::move(mod));
|
||||
auto* resource = m_create_func(QFileInfo(m_resource_dir.filePath(metadata.filename)));
|
||||
resource->setMetadata(metadata);
|
||||
resource->setStatus(ResourceStatus::NOT_INSTALLED);
|
||||
m_result->resources[resource->internal_id()].reset(resource);
|
||||
}
|
||||
}
|
@ -44,17 +44,21 @@
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class ModFolderLoadTask : public Task {
|
||||
class ResourceFolderLoadTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Result {
|
||||
QMap<QString, Mod::Ptr> mods;
|
||||
QMap<QString, Resource::Ptr> resources;
|
||||
};
|
||||
using ResultPtr = std::shared_ptr<Result>;
|
||||
ResultPtr result() const { return m_result; }
|
||||
|
||||
public:
|
||||
ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan = false);
|
||||
ResourceFolderLoadTask(const QDir& resource_dir,
|
||||
const QDir& index_dir,
|
||||
bool is_indexed,
|
||||
bool clean_orphan,
|
||||
std::function<Resource*(const QFileInfo&)> create_function);
|
||||
|
||||
[[nodiscard]] bool canAbort() const override { return true; }
|
||||
bool abort() override
|
||||
@ -69,9 +73,10 @@ class ModFolderLoadTask : public Task {
|
||||
void getFromMetadata();
|
||||
|
||||
private:
|
||||
QDir m_mods_dir, m_index_dir;
|
||||
QDir m_resource_dir, m_index_dir;
|
||||
bool m_is_indexed;
|
||||
bool m_clean_orphan;
|
||||
std::function<Resource*(QFileInfo const&)> m_create_func;
|
||||
ResultPtr m_result;
|
||||
|
||||
std::atomic<bool> m_aborted = false;
|
@ -3,6 +3,7 @@
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class ResourceDownloadTask;
|
||||
@ -12,13 +13,18 @@ class CheckUpdateTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CheckUpdateTask(QList<Mod*>& mods,
|
||||
CheckUpdateTask(QList<Resource*>& resources,
|
||||
std::list<Version>& mcVersions,
|
||||
QList<ModPlatform::ModLoaderType> loadersList,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders_list(loadersList), m_mods_folder(mods_folder) {};
|
||||
std::shared_ptr<ResourceFolderModel> resourceModel)
|
||||
: Task()
|
||||
, m_resources(resources)
|
||||
, m_game_versions(mcVersions)
|
||||
, m_loaders_list(std::move(loadersList))
|
||||
, m_resource_model(std::move(resourceModel))
|
||||
{}
|
||||
|
||||
struct UpdatableMod {
|
||||
struct Update {
|
||||
QString name;
|
||||
QString old_hash;
|
||||
QString old_version;
|
||||
@ -30,28 +36,28 @@ class CheckUpdateTask : public Task {
|
||||
bool enabled = true;
|
||||
|
||||
public:
|
||||
UpdatableMod(QString name,
|
||||
QString old_h,
|
||||
QString old_v,
|
||||
QString new_v,
|
||||
std::optional<ModPlatform::IndexedVersionType> new_v_type,
|
||||
QString changelog,
|
||||
ModPlatform::ResourceProvider p,
|
||||
shared_qobject_ptr<ResourceDownloadTask> t,
|
||||
bool enabled = true)
|
||||
: name(name)
|
||||
, old_hash(old_h)
|
||||
, old_version(old_v)
|
||||
, new_version(new_v)
|
||||
, new_version_type(new_v_type)
|
||||
, changelog(changelog)
|
||||
Update(QString name,
|
||||
QString old_h,
|
||||
QString old_v,
|
||||
QString new_v,
|
||||
std::optional<ModPlatform::IndexedVersionType> new_v_type,
|
||||
QString changelog,
|
||||
ModPlatform::ResourceProvider p,
|
||||
shared_qobject_ptr<ResourceDownloadTask> t,
|
||||
bool enabled = true)
|
||||
: name(std::move(name))
|
||||
, old_hash(std::move(old_h))
|
||||
, old_version(std::move(old_v))
|
||||
, new_version(std::move(new_v))
|
||||
, new_version_type(std::move(new_v_type))
|
||||
, changelog(std::move(changelog))
|
||||
, provider(p)
|
||||
, download(t)
|
||||
, download(std::move(t))
|
||||
, enabled(enabled)
|
||||
{}
|
||||
};
|
||||
|
||||
auto getUpdatable() -> std::vector<UpdatableMod>&& { return std::move(m_updatable); }
|
||||
auto getUpdates() -> std::vector<Update>&& { return std::move(m_updates); }
|
||||
auto getDependencies() -> QList<std::shared_ptr<GetModDependenciesTask::PackDependency>>&& { return std::move(m_deps); }
|
||||
|
||||
public slots:
|
||||
@ -61,14 +67,14 @@ class CheckUpdateTask : public Task {
|
||||
void executeTask() override = 0;
|
||||
|
||||
signals:
|
||||
void checkFailed(Mod* failed, QString reason, QUrl recover_url = {});
|
||||
void checkFailed(Resource* failed, QString reason, QUrl recover_url = {});
|
||||
|
||||
protected:
|
||||
QList<Mod*>& m_mods;
|
||||
QList<Resource*>& m_resources;
|
||||
std::list<Version>& m_game_versions;
|
||||
QList<ModPlatform::ModLoaderType> m_loaders_list;
|
||||
std::shared_ptr<ModFolderModel> m_mods_folder;
|
||||
std::shared_ptr<ResourceFolderModel> m_resource_model;
|
||||
|
||||
std::vector<UpdatableMod> m_updatable;
|
||||
std::vector<Update> m_updates;
|
||||
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> m_deps;
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "Json.h"
|
||||
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
@ -18,55 +18,56 @@
|
||||
static ModrinthAPI modrinth_api;
|
||||
static FlameAPI flame_api;
|
||||
|
||||
EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
EnsureMetadataTask::EnsureMetadataTask(Resource* resource, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(nullptr), m_index_dir(dir), m_provider(prov), m_hashing_task(nullptr), m_current_task(nullptr)
|
||||
{
|
||||
auto hash_task = createNewHash(mod);
|
||||
auto hash_task = createNewHash(resource);
|
||||
if (!hash_task)
|
||||
return;
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); });
|
||||
connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); });
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); });
|
||||
connect(hash_task.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); });
|
||||
hash_task->start();
|
||||
}
|
||||
|
||||
EnsureMetadataTask::EnsureMetadataTask(QList<Mod*>& mods, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
EnsureMetadataTask::EnsureMetadataTask(QList<Resource*>& resources, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr)
|
||||
{
|
||||
m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()));
|
||||
for (auto* mod : mods) {
|
||||
auto hash_task = createNewHash(mod);
|
||||
for (auto* resource : resources) {
|
||||
auto hash_task = createNewHash(resource);
|
||||
if (!hash_task)
|
||||
continue;
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); });
|
||||
connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); });
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); });
|
||||
connect(hash_task.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); });
|
||||
m_hashing_task->addTask(hash_task);
|
||||
}
|
||||
}
|
||||
EnsureMetadataTask::EnsureMetadataTask(QHash<QString, Mod*>& mods, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(nullptr), m_mods(mods), m_index_dir(dir), m_provider(prov), m_current_task(nullptr)
|
||||
|
||||
EnsureMetadataTask::EnsureMetadataTask(QHash<QString, Resource*>& resources, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(nullptr), m_resources(resources), m_index_dir(dir), m_provider(prov), m_current_task(nullptr)
|
||||
{}
|
||||
|
||||
Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Mod* mod)
|
||||
Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Resource* resource)
|
||||
{
|
||||
if (!mod || !mod->valid() || mod->type() == ResourceType::FOLDER)
|
||||
if (!resource || !resource->valid() || resource->type() == ResourceType::FOLDER)
|
||||
return nullptr;
|
||||
|
||||
return Hashing::createHasher(mod->fileinfo().absoluteFilePath(), m_provider);
|
||||
return Hashing::createHasher(resource->fileinfo().absoluteFilePath(), m_provider);
|
||||
}
|
||||
|
||||
QString EnsureMetadataTask::getExistingHash(Mod* mod)
|
||||
QString EnsureMetadataTask::getExistingHash(Resource* resource)
|
||||
{
|
||||
// Check for already computed hashes
|
||||
// (linear on the number of mods vs. linear on the size of the mod's JAR)
|
||||
auto it = m_mods.keyValueBegin();
|
||||
while (it != m_mods.keyValueEnd()) {
|
||||
if ((*it).second == mod)
|
||||
auto it = m_resources.keyValueBegin();
|
||||
while (it != m_resources.keyValueEnd()) {
|
||||
if ((*it).second == resource)
|
||||
break;
|
||||
it++;
|
||||
}
|
||||
|
||||
// We already have the hash computed
|
||||
if (it != m_mods.keyValueEnd()) {
|
||||
if (it != m_resources.keyValueEnd()) {
|
||||
return (*it).first;
|
||||
}
|
||||
|
||||
@ -86,25 +87,25 @@ bool EnsureMetadataTask::abort()
|
||||
|
||||
void EnsureMetadataTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Checking if mods have metadata..."));
|
||||
setStatus(tr("Checking if resources have metadata..."));
|
||||
|
||||
for (auto* mod : m_mods) {
|
||||
if (!mod->valid()) {
|
||||
qDebug() << "Mod" << mod->name() << "is invalid!";
|
||||
emitFail(mod);
|
||||
for (auto* resource : m_resources) {
|
||||
if (!resource->valid()) {
|
||||
qDebug() << "Resource" << resource->name() << "is invalid!";
|
||||
emitFail(resource);
|
||||
continue;
|
||||
}
|
||||
|
||||
// They already have the right metadata :o
|
||||
if (mod->status() != ModStatus::NoMetadata && mod->metadata() && mod->metadata()->provider == m_provider) {
|
||||
qDebug() << "Mod" << mod->name() << "already has metadata!";
|
||||
emitReady(mod);
|
||||
if (resource->status() != ResourceStatus::NO_METADATA && resource->metadata() && resource->metadata()->provider == m_provider) {
|
||||
qDebug() << "Resource" << resource->name() << "already has metadata!";
|
||||
emitReady(resource);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Folders don't have metadata
|
||||
if (mod->type() == ResourceType::FOLDER) {
|
||||
emitReady(mod);
|
||||
if (resource->type() == ResourceType::FOLDER) {
|
||||
emitReady(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,9 +121,9 @@ void EnsureMetadataTask::executeTask()
|
||||
}
|
||||
|
||||
auto invalidade_leftover = [this] {
|
||||
for (auto mod = m_mods.constBegin(); mod != m_mods.constEnd(); mod++)
|
||||
emitFail(mod.value(), mod.key(), RemoveFromList::No);
|
||||
m_mods.clear();
|
||||
for (auto resource = m_resources.constBegin(); resource != m_resources.constEnd(); resource++)
|
||||
emitFail(resource.value(), resource.key(), RemoveFromList::No);
|
||||
m_resources.clear();
|
||||
|
||||
emitSucceeded();
|
||||
};
|
||||
@ -162,53 +163,53 @@ void EnsureMetadataTask::executeTask()
|
||||
m_current_task.reset();
|
||||
});
|
||||
|
||||
if (m_mods.size() > 1)
|
||||
if (m_resources.size() > 1)
|
||||
setStatus(tr("Requesting metadata information from %1...").arg(ModPlatform::ProviderCapabilities::readableName(m_provider)));
|
||||
else if (!m_mods.empty())
|
||||
else if (!m_resources.empty())
|
||||
setStatus(tr("Requesting metadata information from %1 for '%2'...")
|
||||
.arg(ModPlatform::ProviderCapabilities::readableName(m_provider), m_mods.begin().value()->name()));
|
||||
.arg(ModPlatform::ProviderCapabilities::readableName(m_provider), m_resources.begin().value()->name()));
|
||||
|
||||
m_current_task = version_task;
|
||||
version_task->start();
|
||||
}
|
||||
|
||||
void EnsureMetadataTask::emitReady(Mod* m, QString key, RemoveFromList remove)
|
||||
void EnsureMetadataTask::emitReady(Resource* resource, QString key, RemoveFromList remove)
|
||||
{
|
||||
if (!m) {
|
||||
qCritical() << "Tried to mark a null mod as ready.";
|
||||
if (!resource) {
|
||||
qCritical() << "Tried to mark a null resource as ready.";
|
||||
if (!key.isEmpty())
|
||||
m_mods.remove(key);
|
||||
m_resources.remove(key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << QString("Generated metadata for %1").arg(m->name());
|
||||
emit metadataReady(m);
|
||||
qDebug() << QString("Generated metadata for %1").arg(resource->name());
|
||||
emit metadataReady(resource);
|
||||
|
||||
if (remove == RemoveFromList::Yes) {
|
||||
if (key.isEmpty())
|
||||
key = getExistingHash(m);
|
||||
m_mods.remove(key);
|
||||
key = getExistingHash(resource);
|
||||
m_resources.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove)
|
||||
void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromList remove)
|
||||
{
|
||||
if (!m) {
|
||||
qCritical() << "Tried to mark a null mod as failed.";
|
||||
if (!resource) {
|
||||
qCritical() << "Tried to mark a null resource as failed.";
|
||||
if (!key.isEmpty())
|
||||
m_mods.remove(key);
|
||||
m_resources.remove(key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << QString("Failed to generate metadata for %1").arg(m->name());
|
||||
emit metadataFailed(m);
|
||||
qDebug() << QString("Failed to generate metadata for %1").arg(resource->name());
|
||||
emit metadataFailed(resource);
|
||||
|
||||
if (remove == RemoveFromList::Yes) {
|
||||
if (key.isEmpty())
|
||||
key = getExistingHash(m);
|
||||
m_mods.remove(key);
|
||||
key = getExistingHash(resource);
|
||||
m_resources.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,7 +220,7 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
||||
auto hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response);
|
||||
auto ver_task = modrinth_api.currentVersions(m_resources.keys(), hash_type, response);
|
||||
|
||||
// Prevents unfortunate timings when aborting the task
|
||||
if (!ver_task)
|
||||
@ -239,20 +240,20 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
||||
|
||||
try {
|
||||
auto entries = Json::requireObject(doc);
|
||||
for (auto& hash : m_mods.keys()) {
|
||||
auto mod = m_mods.find(hash).value();
|
||||
for (auto& hash : m_resources.keys()) {
|
||||
auto resource = m_resources.find(hash).value();
|
||||
try {
|
||||
auto entry = Json::requireObject(entries, hash);
|
||||
|
||||
setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name()));
|
||||
qDebug() << "Getting version for" << mod->name() << "from Modrinth";
|
||||
setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name()));
|
||||
qDebug() << "Getting version for" << resource->name() << "from Modrinth";
|
||||
|
||||
m_temp_versions.insert(hash, Modrinth::loadIndexedPackVersion(entry));
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << entries;
|
||||
|
||||
emitFail(mod);
|
||||
emitFail(resource);
|
||||
}
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
@ -324,23 +325,23 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
||||
|
||||
auto hash = addonIds.find(pack.addonId.toString()).value();
|
||||
|
||||
auto mod_iter = m_mods.find(hash);
|
||||
if (mod_iter == m_mods.end()) {
|
||||
auto resource_iter = m_resources.find(hash);
|
||||
if (resource_iter == m_resources.end()) {
|
||||
qWarning() << "Invalid project id from the API response.";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* mod = mod_iter.value();
|
||||
auto* resource = resource_iter.value();
|
||||
|
||||
try {
|
||||
setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name()));
|
||||
setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name()));
|
||||
|
||||
modrinthCallback(pack, m_temp_versions.find(hash).value(), mod);
|
||||
modrinthCallback(pack, m_temp_versions.find(hash).value(), resource);
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << entries;
|
||||
|
||||
emitFail(mod);
|
||||
emitFail(resource);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -354,7 +355,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QList<uint> fingerprints;
|
||||
for (auto& murmur : m_mods.keys()) {
|
||||
for (auto& murmur : m_resources.keys()) {
|
||||
fingerprints.push_back(murmur.toUInt());
|
||||
}
|
||||
|
||||
@ -394,13 +395,13 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
||||
}
|
||||
|
||||
auto fingerprint = QString::number(Json::ensureVariant(file_obj, "fileFingerprint").toUInt());
|
||||
auto mod = m_mods.find(fingerprint);
|
||||
if (mod == m_mods.end()) {
|
||||
auto resource = m_resources.find(fingerprint);
|
||||
if (resource == m_resources.end()) {
|
||||
qWarning() << "Invalid fingerprint from the API response.";
|
||||
continue;
|
||||
}
|
||||
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*mod)->name()));
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*resource)->name()));
|
||||
|
||||
m_temp_versions.insert(fingerprint, FlameMod::loadIndexedPackVersion(file_obj));
|
||||
}
|
||||
@ -417,7 +418,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
||||
Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
||||
{
|
||||
QHash<QString, QString> addonIds;
|
||||
for (auto const& hash : m_mods.keys()) {
|
||||
for (auto const& hash : m_resources.keys()) {
|
||||
if (m_temp_versions.contains(hash)) {
|
||||
auto data = m_temp_versions.find(hash).value();
|
||||
|
||||
@ -464,20 +465,20 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
||||
|
||||
auto id = QString::number(Json::requireInteger(entry_obj, "id"));
|
||||
auto hash = addonIds.find(id).value();
|
||||
auto mod = m_mods.find(hash).value();
|
||||
auto resource = m_resources.find(hash).value();
|
||||
|
||||
try {
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name()));
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(resource->name()));
|
||||
|
||||
ModPlatform::IndexedPack pack;
|
||||
FlameMod::loadIndexedPack(pack, entry_obj);
|
||||
|
||||
flameCallback(pack, m_temp_versions.find(hash).value(), mod);
|
||||
flameCallback(pack, m_temp_versions.find(hash).value(), resource);
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << entries;
|
||||
|
||||
emitFail(mod);
|
||||
emitFail(resource);
|
||||
}
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
@ -489,17 +490,17 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
||||
return proj_task;
|
||||
}
|
||||
|
||||
void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod* mod)
|
||||
void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource* resource)
|
||||
{
|
||||
// Prevent file name mismatch
|
||||
ver.fileName = mod->fileinfo().fileName();
|
||||
ver.fileName = resource->fileinfo().fileName();
|
||||
if (ver.fileName.endsWith(".disabled"))
|
||||
ver.fileName.chop(9);
|
||||
|
||||
QDir tmp_index_dir(m_index_dir);
|
||||
|
||||
{
|
||||
LocalModUpdateTask update_metadata(m_index_dir, pack, ver);
|
||||
LocalResourceUpdateTask update_metadata(m_index_dir, pack, ver);
|
||||
QEventLoop loop;
|
||||
|
||||
QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit);
|
||||
@ -513,27 +514,27 @@ void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPla
|
||||
auto metadata = Metadata::get(tmp_index_dir, pack.slug);
|
||||
if (!metadata.isValid()) {
|
||||
qCritical() << "Failed to generate metadata at last step!";
|
||||
emitFail(mod);
|
||||
emitFail(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
mod->setMetadata(metadata);
|
||||
resource->setMetadata(metadata);
|
||||
|
||||
emitReady(mod);
|
||||
emitReady(resource);
|
||||
}
|
||||
|
||||
void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod* mod)
|
||||
void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource* resource)
|
||||
{
|
||||
try {
|
||||
// Prevent file name mismatch
|
||||
ver.fileName = mod->fileinfo().fileName();
|
||||
ver.fileName = resource->fileinfo().fileName();
|
||||
if (ver.fileName.endsWith(".disabled"))
|
||||
ver.fileName.chop(9);
|
||||
|
||||
QDir tmp_index_dir(m_index_dir);
|
||||
|
||||
{
|
||||
LocalModUpdateTask update_metadata(m_index_dir, pack, ver);
|
||||
LocalResourceUpdateTask update_metadata(m_index_dir, pack, ver);
|
||||
QEventLoop loop;
|
||||
|
||||
QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit);
|
||||
@ -547,16 +548,16 @@ void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatfo
|
||||
auto metadata = Metadata::get(tmp_index_dir, pack.slug);
|
||||
if (!metadata.isValid()) {
|
||||
qCritical() << "Failed to generate metadata at last step!";
|
||||
emitFail(mod);
|
||||
emitFail(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
mod->setMetadata(metadata);
|
||||
resource->setMetadata(metadata);
|
||||
|
||||
emitReady(mod);
|
||||
emitReady(resource);
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
|
||||
emitFail(mod);
|
||||
emitFail(resource);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "ModIndex.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
|
||||
#include "minecraft/mod/Resource.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
class Mod;
|
||||
class QDir;
|
||||
|
||||
class EnsureMetadataTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QList<Mod*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QHash<QString, Mod*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(Resource*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QList<Resource*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QHash<QString, Resource*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
|
||||
~EnsureMetadataTask() = default;
|
||||
|
||||
@ -37,23 +38,23 @@ class EnsureMetadataTask : public Task {
|
||||
|
||||
// Helpers
|
||||
enum class RemoveFromList { Yes, No };
|
||||
void emitReady(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
void emitFail(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
void emitReady(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
void emitFail(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
|
||||
// Hashes and stuff
|
||||
auto createNewHash(Mod*) -> Hashing::Hasher::Ptr;
|
||||
auto getExistingHash(Mod*) -> QString;
|
||||
auto createNewHash(Resource*) -> Hashing::Hasher::Ptr;
|
||||
auto getExistingHash(Resource*) -> QString;
|
||||
|
||||
private slots:
|
||||
void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod*);
|
||||
void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod*);
|
||||
void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource*);
|
||||
void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource*);
|
||||
|
||||
signals:
|
||||
void metadataReady(Mod*);
|
||||
void metadataFailed(Mod*);
|
||||
void metadataReady(Resource*);
|
||||
void metadataFailed(Resource*);
|
||||
|
||||
private:
|
||||
QHash<QString, Mod*> m_mods;
|
||||
QHash<QString, Resource*> m_resources;
|
||||
QDir m_index_dir;
|
||||
ModPlatform::ResourceProvider m_provider;
|
||||
|
||||
|
@ -87,7 +87,7 @@ class ResourceAPI {
|
||||
struct VersionSearchArgs {
|
||||
ModPlatform::IndexedPack pack;
|
||||
|
||||
std::optional<std::list<Version> > mcVersions;
|
||||
std::optional<std::list<Version>> mcVersions;
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders;
|
||||
|
||||
VersionSearchArgs(VersionSearchArgs const&) = default;
|
||||
|
@ -121,59 +121,65 @@ ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileI
|
||||
* */
|
||||
void FlameCheckUpdate::executeTask()
|
||||
{
|
||||
setStatus(tr("Preparing mods for CurseForge..."));
|
||||
setStatus(tr("Preparing resources for CurseForge..."));
|
||||
|
||||
int i = 0;
|
||||
for (auto* mod : m_mods) {
|
||||
setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name()));
|
||||
setProgress(i++, m_mods.size());
|
||||
for (auto* resource : m_resources) {
|
||||
setStatus(tr("Getting API response from CurseForge for '%1'...").arg(resource->name()));
|
||||
setProgress(i++, m_resources.size());
|
||||
|
||||
auto latest_vers = api.getLatestVersions({ { mod->metadata()->project_id.toString() }, m_game_versions });
|
||||
auto latest_vers = api.getLatestVersions({ { resource->metadata()->project_id.toString() }, m_game_versions });
|
||||
|
||||
// Check if we were aborted while getting the latest version
|
||||
if (m_was_aborted) {
|
||||
aborted();
|
||||
return;
|
||||
}
|
||||
auto latest_ver = api.getLatestVersion(latest_vers, m_loaders_list, mod->loaders());
|
||||
auto latest_ver = api.getLatestVersion(latest_vers, m_loaders_list, resource->metadata()->loaders);
|
||||
|
||||
setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod->name()));
|
||||
setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(resource->name()));
|
||||
|
||||
if (!latest_ver.has_value() || !latest_ver->addonId.isValid()) {
|
||||
emit checkFailed(mod, tr("No valid version found for this mod. It's probably unavailable for the current game "
|
||||
"version / mod loader."));
|
||||
QString reason;
|
||||
if (dynamic_cast<Mod*>(resource) != nullptr)
|
||||
reason =
|
||||
tr("No valid version found for this resource. It's probably unavailable for the current game "
|
||||
"version / mod loader.");
|
||||
else
|
||||
reason = tr("No valid version found for this resource. It's probably unavailable for the current game version.");
|
||||
|
||||
emit checkFailed(resource, reason);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (latest_ver->downloadUrl.isEmpty() && latest_ver->fileId != mod->metadata()->file_id) {
|
||||
if (latest_ver->downloadUrl.isEmpty() && latest_ver->fileId != resource->metadata()->file_id) {
|
||||
auto pack = getProjectInfo(latest_ver.value());
|
||||
auto recover_url = QString("%1/download/%2").arg(pack.websiteUrl, latest_ver->fileId.toString());
|
||||
emit checkFailed(mod, tr("Mod has a new update available, but is not downloadable using CurseForge."), recover_url);
|
||||
emit checkFailed(resource, tr("Resource has a new update available, but is not downloadable using CurseForge."), recover_url);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fake pack with the necessary info to pass to the download task :)
|
||||
auto pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pack->name = mod->name();
|
||||
pack->slug = mod->metadata()->slug;
|
||||
pack->addonId = mod->metadata()->project_id;
|
||||
pack->websiteUrl = mod->homeurl();
|
||||
for (auto& author : mod->authors())
|
||||
pack->authors.append({ author });
|
||||
pack->description = mod->description();
|
||||
pack->name = resource->name();
|
||||
pack->slug = resource->metadata()->slug;
|
||||
pack->addonId = resource->metadata()->project_id;
|
||||
pack->provider = ModPlatform::ResourceProvider::FLAME;
|
||||
if (!latest_ver->hash.isEmpty() && (mod->metadata()->hash != latest_ver->hash || mod->status() == ModStatus::NotInstalled)) {
|
||||
auto old_version = mod->version();
|
||||
if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) {
|
||||
auto current_ver = getFileInfo(latest_ver->addonId.toInt(), mod->metadata()->file_id.toInt());
|
||||
old_version = current_ver.version;
|
||||
if (!latest_ver->hash.isEmpty() &&
|
||||
(resource->metadata()->hash != latest_ver->hash || resource->status() == ResourceStatus::NOT_INSTALLED)) {
|
||||
auto old_version = resource->metadata()->version_number;
|
||||
if (old_version.isEmpty()) {
|
||||
if (resource->status() == ResourceStatus::NOT_INSTALLED)
|
||||
old_version = tr("Not installed");
|
||||
else
|
||||
old_version = tr("Unknown");
|
||||
}
|
||||
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, latest_ver.value(), m_mods_folder);
|
||||
m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver->version, latest_ver->version_type,
|
||||
api.getModFileChangelog(latest_ver->addonId.toInt(), latest_ver->fileId.toInt()),
|
||||
ModPlatform::ResourceProvider::FLAME, download_task, mod->enabled());
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, latest_ver.value(), m_resource_model);
|
||||
m_updates.emplace_back(pack->name, resource->metadata()->hash, old_version, latest_ver->version, latest_ver->version_type,
|
||||
api.getModFileChangelog(latest_ver->addonId.toInt(), latest_ver->fileId.toInt()),
|
||||
ModPlatform::ResourceProvider::FLAME, download_task, resource->enabled());
|
||||
}
|
||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, latest_ver.value()));
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Application.h"
|
||||
#include "modplatform/CheckUpdateTask.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
@ -7,11 +8,11 @@ class FlameCheckUpdate : public CheckUpdateTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameCheckUpdate(QList<Mod*>& mods,
|
||||
FlameCheckUpdate(QList<Resource*>& resources,
|
||||
std::list<Version>& mcVersions,
|
||||
QList<ModPlatform::ModLoaderType> loadersList,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loadersList, mods_folder)
|
||||
std::shared_ptr<ResourceFolderModel> resourceModel)
|
||||
: CheckUpdateTask(resources, mcVersions, std::move(loadersList), std::move(resourceModel))
|
||||
{}
|
||||
|
||||
public slots:
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include "FlameInstanceCreationTask.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
#include "modplatform/flame/FileResolvingTask.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
@ -676,6 +676,7 @@ void FlameCreationTask::validateZIPResources(QEventLoop& loop)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO make this work with other sorts of resource
|
||||
auto task = makeShared<ConcurrentTask>(this, "CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
auto results = m_mod_id_resolver->getResults().files;
|
||||
auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index");
|
||||
@ -683,7 +684,7 @@ void FlameCreationTask::validateZIPResources(QEventLoop& loop)
|
||||
if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) {
|
||||
continue;
|
||||
}
|
||||
task->addTask(makeShared<LocalModUpdateTask>(folder, file.pack, file.version));
|
||||
task->addTask(makeShared<LocalResourceUpdateTask>(folder, file.pack, file.version));
|
||||
}
|
||||
connect(task.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||
m_process_update_file_info_job = task;
|
||||
|
@ -28,7 +28,7 @@ QString toHTML(QList<Mod*> mods, OptionalData extraData)
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name().toHtmlEscaped();
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl().toHtmlEscaped();
|
||||
auto url = mod->homepage().toHtmlEscaped();
|
||||
if (!url.isEmpty())
|
||||
modName = QString("<a href=\"%1\">%2</a>").arg(url, modName);
|
||||
}
|
||||
@ -65,7 +65,7 @@ QString toMarkdown(QList<Mod*> mods, OptionalData extraData)
|
||||
auto meta = mod->metadata();
|
||||
auto modName = toMarkdownEscaped(mod->name());
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl();
|
||||
auto url = mod->homepage();
|
||||
if (!url.isEmpty())
|
||||
modName = QString("[%1](%2)").arg(modName, url);
|
||||
}
|
||||
@ -95,7 +95,7 @@ QString toPlainTXT(QList<Mod*> mods, OptionalData extraData)
|
||||
|
||||
auto line = modName;
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl();
|
||||
auto url = mod->homepage();
|
||||
if (!url.isEmpty())
|
||||
line += QString(" (%1)").arg(url);
|
||||
}
|
||||
@ -124,7 +124,7 @@ QString toJSON(QList<Mod*> mods, OptionalData extraData)
|
||||
QJsonObject line;
|
||||
line["name"] = modName;
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl();
|
||||
auto url = mod->homepage();
|
||||
if (!url.isEmpty())
|
||||
line["url"] = url;
|
||||
}
|
||||
@ -156,7 +156,7 @@ QString toCSV(QList<Mod*> mods, OptionalData extraData)
|
||||
|
||||
data << modName;
|
||||
if (extraData & Url)
|
||||
data << mod->metaurl();
|
||||
data << mod->homepage();
|
||||
if (extraData & Version) {
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
@ -203,7 +203,7 @@ QString exportToModList(QList<Mod*> mods, QString lineTemplate)
|
||||
for (auto mod : mods) {
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name();
|
||||
auto url = mod->metaurl();
|
||||
auto url = mod->homepage();
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
|
@ -14,14 +14,6 @@
|
||||
|
||||
static ModrinthAPI api;
|
||||
|
||||
ModrinthCheckUpdate::ModrinthCheckUpdate(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
QList<ModPlatform::ModLoaderType> loadersList,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loadersList, mods_folder)
|
||||
, m_hash_type(ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first())
|
||||
{}
|
||||
|
||||
bool ModrinthCheckUpdate::abort()
|
||||
{
|
||||
if (m_job)
|
||||
@ -36,24 +28,24 @@ bool ModrinthCheckUpdate::abort()
|
||||
* */
|
||||
void ModrinthCheckUpdate::executeTask()
|
||||
{
|
||||
setStatus(tr("Preparing mods for Modrinth..."));
|
||||
setProgress(0, 9);
|
||||
setStatus(tr("Preparing resources for Modrinth..."));
|
||||
setProgress(0, (m_loaders_list.isEmpty() ? 1 : m_loaders_list.length()) * 2 + 1);
|
||||
|
||||
auto hashing_task =
|
||||
makeShared<ConcurrentTask>(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
for (auto* mod : m_mods) {
|
||||
auto hash = mod->metadata()->hash;
|
||||
for (auto* resource : m_resources) {
|
||||
auto hash = resource->metadata()->hash;
|
||||
|
||||
// Sadly the API can only handle one hash type per call, se we
|
||||
// need to generate a new hash if the current one is innadequate
|
||||
// (though it will rarely happen, if at all)
|
||||
if (mod->metadata()->hash_format != m_hash_type) {
|
||||
auto hash_task = Hashing::createHasher(mod->fileinfo().absoluteFilePath(), ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mappings.insert(hash, mod); });
|
||||
if (resource->metadata()->hash_format != m_hash_type) {
|
||||
auto hash_task = Hashing::createHasher(resource->fileinfo().absoluteFilePath(), ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_mappings.insert(hash, resource); });
|
||||
connect(hash_task.get(), &Task::failed, [this] { failed("Failed to generate hash"); });
|
||||
hashing_task->addTask(hash_task);
|
||||
} else {
|
||||
m_mappings.insert(hash, mod);
|
||||
m_mappings.insert(hash, resource);
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,10 +54,28 @@ void ModrinthCheckUpdate::executeTask()
|
||||
hashing_task->start();
|
||||
}
|
||||
|
||||
void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> response,
|
||||
ModPlatform::ModLoaderTypes loader,
|
||||
bool forceModLoaderCheck)
|
||||
void ModrinthCheckUpdate::getUpdateModsForLoader(std::optional<ModPlatform::ModLoaderTypes> loader)
|
||||
{
|
||||
setStatus(tr("Waiting for the API response from Modrinth..."));
|
||||
setProgress(m_progress + 1, m_progressTotal);
|
||||
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
QStringList hashes = m_mappings.keys();
|
||||
auto job = api.latestVersions(hashes, m_hash_type, m_game_versions, loader, response);
|
||||
|
||||
connect(job.get(), &Task::succeeded, this, [this, response, loader] { checkVersionsResponse(response, loader); });
|
||||
|
||||
connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::checkNextLoader);
|
||||
|
||||
m_job = job;
|
||||
job->start();
|
||||
}
|
||||
|
||||
void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> response, std::optional<ModPlatform::ModLoaderTypes> loader)
|
||||
{
|
||||
setStatus(tr("Parsing the API response from Modrinth..."));
|
||||
setProgress(m_progress + 1, m_progressTotal);
|
||||
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
@ -77,21 +87,21 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> resp
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(tr("Parsing the API response from Modrinth..."));
|
||||
setProgress(m_next_loader_idx * 2, 9);
|
||||
|
||||
try {
|
||||
for (auto hash : m_mappings.keys()) {
|
||||
if (forceModLoaderCheck && !(m_mappings[hash]->loaders() & loader)) {
|
||||
continue;
|
||||
}
|
||||
auto iter = m_mappings.begin();
|
||||
|
||||
while (iter != m_mappings.end()) {
|
||||
const QString hash = iter.key();
|
||||
Resource* resource = iter.value();
|
||||
|
||||
auto project_obj = doc[hash].toObject();
|
||||
|
||||
// If the returned project is empty, but we have Modrinth metadata,
|
||||
// it means this specific version is not available
|
||||
if (project_obj.isEmpty()) {
|
||||
qDebug() << "Mod " << m_mappings.find(hash).value()->name() << " got an empty response." << "Hash: " << hash;
|
||||
|
||||
qDebug() << "Mod " << m_mappings.find(hash).value()->name() << " got an empty response."
|
||||
<< "Hash: " << hash;
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -101,7 +111,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> resp
|
||||
static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge,
|
||||
ModPlatform::ModLoaderType::Quilt, ModPlatform::ModLoaderType::Fabric };
|
||||
for (auto flag : flags) {
|
||||
if (loader.testFlag(flag)) {
|
||||
if (loader.has_value() && loader->testFlag(flag)) {
|
||||
loader_filter = ModPlatform::getModLoaderAsString(flag);
|
||||
break;
|
||||
}
|
||||
@ -116,106 +126,72 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr<QByteArray> resp
|
||||
auto project_ver = Modrinth::loadIndexedPackVersion(project_obj, m_hash_type, loader_filter);
|
||||
if (project_ver.downloadUrl.isEmpty()) {
|
||||
qCritical() << "Modrinth mod without download url!" << project_ver.fileName;
|
||||
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto mod_iter = m_mappings.find(hash);
|
||||
if (mod_iter == m_mappings.end()) {
|
||||
qCritical() << "Failed to remap mod from Modrinth!";
|
||||
continue;
|
||||
}
|
||||
auto mod = *mod_iter;
|
||||
m_mappings.remove(hash);
|
||||
|
||||
auto key = project_ver.hash;
|
||||
|
||||
// Fake pack with the necessary info to pass to the download task :)
|
||||
auto pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pack->name = mod->name();
|
||||
pack->slug = mod->metadata()->slug;
|
||||
pack->addonId = mod->metadata()->project_id;
|
||||
pack->websiteUrl = mod->homeurl();
|
||||
for (auto& author : mod->authors())
|
||||
pack->authors.append({ author });
|
||||
pack->description = mod->description();
|
||||
pack->name = resource->name();
|
||||
pack->slug = resource->metadata()->slug;
|
||||
pack->addonId = resource->metadata()->project_id;
|
||||
pack->provider = ModPlatform::ResourceProvider::MODRINTH;
|
||||
if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) {
|
||||
if (mod->version() == project_ver.version_number)
|
||||
continue;
|
||||
if ((project_ver.hash != hash && project_ver.is_preferred) || (resource->status() == ResourceStatus::NOT_INSTALLED)) {
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_resource_model);
|
||||
|
||||
auto download_task = makeShared<ResourceDownloadTask>(pack, project_ver, m_mods_folder);
|
||||
QString old_version = resource->metadata()->version_number;
|
||||
if (old_version.isEmpty()) {
|
||||
if (resource->status() == ResourceStatus::NOT_INSTALLED)
|
||||
old_version = tr("Not installed");
|
||||
else
|
||||
old_version = tr("Unknown");
|
||||
}
|
||||
|
||||
m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.version_type,
|
||||
project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task, mod->enabled());
|
||||
m_updates.emplace_back(pack->name, hash, old_version, project_ver.version_number, project_ver.version_type,
|
||||
project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task, resource->enabled());
|
||||
}
|
||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, project_ver));
|
||||
|
||||
iter = m_mappings.erase(iter);
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
emitFailed(e.cause() + " : " + e.what());
|
||||
emitFailed(e.cause() + ": " + e.what());
|
||||
return;
|
||||
}
|
||||
checkNextLoader();
|
||||
}
|
||||
|
||||
void ModrinthCheckUpdate::getUpdateModsForLoader(ModPlatform::ModLoaderTypes loader, bool forceModLoaderCheck)
|
||||
{
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
QStringList hashes;
|
||||
if (forceModLoaderCheck) {
|
||||
for (auto hash : m_mappings.keys()) {
|
||||
if (m_mappings[hash]->loaders() & loader) {
|
||||
hashes.append(hash);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hashes = m_mappings.keys();
|
||||
}
|
||||
auto job = api.latestVersions(hashes, m_hash_type, m_game_versions, loader, response);
|
||||
|
||||
connect(job.get(), &Task::succeeded, this,
|
||||
[this, response, loader, forceModLoaderCheck] { checkVersionsResponse(response, loader, forceModLoaderCheck); });
|
||||
|
||||
connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::checkNextLoader);
|
||||
|
||||
setStatus(tr("Waiting for the API response from Modrinth..."));
|
||||
setProgress(m_next_loader_idx * 2 - 1, 9);
|
||||
|
||||
m_job = job;
|
||||
job->start();
|
||||
}
|
||||
|
||||
void ModrinthCheckUpdate::checkNextLoader()
|
||||
{
|
||||
if (m_mappings.isEmpty()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
if (m_next_loader_idx < m_loaders_list.size()) {
|
||||
getUpdateModsForLoader(m_loaders_list.at(m_next_loader_idx));
|
||||
m_next_loader_idx++;
|
||||
|
||||
if (m_loaders_list.isEmpty() && m_loader_idx == 0) {
|
||||
getUpdateModsForLoader({});
|
||||
m_loader_idx++;
|
||||
return;
|
||||
}
|
||||
static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge, ModPlatform::ModLoaderType::Quilt,
|
||||
ModPlatform::ModLoaderType::Fabric };
|
||||
for (auto flag : flags) {
|
||||
if (!m_loaders_list.contains(flag)) {
|
||||
m_loaders_list.append(flag);
|
||||
m_next_loader_idx++;
|
||||
setProgress(m_next_loader_idx * 2 - 1, 9);
|
||||
for (auto m : m_mappings) {
|
||||
if (m->loaders() & flag) {
|
||||
getUpdateModsForLoader(flag, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setProgress(m_next_loader_idx * 2, 9);
|
||||
}
|
||||
|
||||
if (m_loader_idx < m_loaders_list.size()) {
|
||||
getUpdateModsForLoader(m_loaders_list.at(m_loader_idx));
|
||||
m_loader_idx++;
|
||||
return;
|
||||
}
|
||||
for (auto m : m_mappings) {
|
||||
emit checkFailed(m,
|
||||
tr("No valid version found for this mod. It's probably unavailable for the current game version / mod loader."));
|
||||
|
||||
for (auto resource : m_mappings) {
|
||||
QString reason;
|
||||
|
||||
if (dynamic_cast<Mod*>(resource) != nullptr)
|
||||
reason =
|
||||
tr("No valid version found for this resource. It's probably unavailable for the current game "
|
||||
"version / mod loader.");
|
||||
else
|
||||
reason = tr("No valid version found for this resource. It's probably unavailable for the current game version.");
|
||||
|
||||
emit checkFailed(resource, reason);
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
@ -6,23 +6,26 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthCheckUpdate(QList<Mod*>& mods,
|
||||
ModrinthCheckUpdate(QList<Resource*>& resources,
|
||||
std::list<Version>& mcVersions,
|
||||
QList<ModPlatform::ModLoaderType> loadersList,
|
||||
std::shared_ptr<ModFolderModel> mods_folder);
|
||||
std::shared_ptr<ResourceFolderModel> resourceModel)
|
||||
: CheckUpdateTask(resources, mcVersions, std::move(loadersList), std::move(resourceModel))
|
||||
, m_hash_type(ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first())
|
||||
{}
|
||||
|
||||
public slots:
|
||||
bool abort() override;
|
||||
|
||||
protected slots:
|
||||
void executeTask() override;
|
||||
void getUpdateModsForLoader(ModPlatform::ModLoaderTypes loader, bool forceModLoaderCheck = false);
|
||||
void checkVersionsResponse(std::shared_ptr<QByteArray> response, ModPlatform::ModLoaderTypes loader, bool forceModLoaderCheck = false);
|
||||
void getUpdateModsForLoader(std::optional<ModPlatform::ModLoaderTypes> loader);
|
||||
void checkVersionsResponse(std::shared_ptr<QByteArray> response, std::optional<ModPlatform::ModLoaderTypes> loader);
|
||||
void checkNextLoader();
|
||||
|
||||
private:
|
||||
Task::Ptr m_job = nullptr;
|
||||
QHash<QString, Mod*> m_mappings;
|
||||
QHash<QString, Resource*> m_mappings;
|
||||
QString m_hash_type;
|
||||
int m_next_loader_idx = 0;
|
||||
int m_loader_idx = 0;
|
||||
};
|
||||
|
@ -243,7 +243,8 @@ bool ModrinthCreationTask::createInstance()
|
||||
|
||||
auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path);
|
||||
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
|
||||
QHash<QString, Mod*> mods;
|
||||
// TODO make this work with other sorts of resource
|
||||
QHash<QString, Resource*> resources;
|
||||
for (auto file : m_files) {
|
||||
auto fileName = file.path;
|
||||
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||
@ -259,7 +260,7 @@ bool ModrinthCreationTask::createInstance()
|
||||
ModDetails d;
|
||||
d.mod_id = file_path;
|
||||
mod->setDetails(d);
|
||||
mods[file.hash.toHex()] = mod;
|
||||
resources[file.hash.toHex()] = mod;
|
||||
}
|
||||
|
||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
|
||||
@ -302,15 +303,15 @@ bool ModrinthCreationTask::createInstance()
|
||||
loop.exec();
|
||||
|
||||
if (!ended_well) {
|
||||
for (auto m : mods) {
|
||||
delete m;
|
||||
for (auto resource : resources) {
|
||||
delete resource;
|
||||
}
|
||||
return ended_well;
|
||||
}
|
||||
|
||||
QEventLoop ensureMetaLoop;
|
||||
QDir folder = FS::PathCombine(instance.modsRoot(), ".index");
|
||||
auto ensureMetadataTask = makeShared<EnsureMetadataTask>(mods, folder, ModPlatform::ResourceProvider::MODRINTH);
|
||||
auto ensureMetadataTask = makeShared<EnsureMetadataTask>(resources, folder, ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(ensureMetadataTask.get(), &Task::succeeded, this, [&ended_well]() { ended_well = true; });
|
||||
connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit);
|
||||
connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) {
|
||||
@ -323,10 +324,10 @@ bool ModrinthCreationTask::createInstance()
|
||||
m_task = ensureMetadataTask;
|
||||
|
||||
ensureMetaLoop.exec();
|
||||
for (auto m : mods) {
|
||||
delete m;
|
||||
for (auto resource : resources) {
|
||||
delete resource;
|
||||
}
|
||||
mods.clear();
|
||||
resources.clear();
|
||||
|
||||
// Update information of the already installed instance, if any.
|
||||
if (m_instance && ended_well) {
|
||||
|
@ -123,7 +123,7 @@ void ModrinthPackExportTask::collectHashes()
|
||||
modIter != allMods.end()) {
|
||||
const Mod* mod = *modIter;
|
||||
if (mod->metadata() != nullptr) {
|
||||
QUrl& url = mod->metadata()->url;
|
||||
const QUrl& url = mod->metadata()->url;
|
||||
// ensure the url is permitted on modrinth.com
|
||||
if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) {
|
||||
qDebug() << "Resolving" << relative << "from index";
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
namespace Packwiz {
|
||||
|
||||
auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString
|
||||
auto getRealIndexName(const QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString
|
||||
{
|
||||
QFile index_file(index_dir.absoluteFilePath(normalized_fname));
|
||||
|
||||
@ -90,7 +90,7 @@ auto intEntry(toml::table table, QString entry_name) -> int
|
||||
return node.value_or(0);
|
||||
}
|
||||
|
||||
auto V1::createModFormat([[maybe_unused]] QDir& index_dir,
|
||||
auto V1::createModFormat([[maybe_unused]] const QDir& index_dir,
|
||||
ModPlatform::IndexedPack& mod_pack,
|
||||
ModPlatform::IndexedVersion& mod_version) -> Mod
|
||||
{
|
||||
@ -119,10 +119,14 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir,
|
||||
mod.mcVersions.sort();
|
||||
mod.releaseType = mod_version.version_type;
|
||||
|
||||
mod.version_number = mod_version.version_number;
|
||||
if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a version number
|
||||
mod.version_number = mod_version.version;
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod
|
||||
auto V1::createModFormat(const QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod
|
||||
{
|
||||
// Try getting metadata if it exists
|
||||
Mod mod{ getIndexForMod(index_dir, slug) };
|
||||
@ -134,7 +138,7 @@ auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod,
|
||||
return {};
|
||||
}
|
||||
|
||||
void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
||||
void V1::updateModIndex(const QDir& index_dir, Mod& mod)
|
||||
{
|
||||
if (!mod.isValid()) {
|
||||
qCritical() << QString("Tried to update metadata of an invalid mod!");
|
||||
@ -211,6 +215,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
||||
{ "x-prismlauncher-loaders", loaders },
|
||||
{ "x-prismlauncher-mc-versions", mcVersions },
|
||||
{ "x-prismlauncher-release-type", mod.releaseType.toString().toStdString() },
|
||||
{ "x-prismlauncher-version-number", mod.version_number.toStdString() },
|
||||
{ "download",
|
||||
toml::table{
|
||||
{ "mode", mod.mode.toStdString() },
|
||||
@ -228,7 +233,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
||||
index_file.close();
|
||||
}
|
||||
|
||||
void V1::deleteModIndex(QDir& index_dir, QString& mod_slug)
|
||||
void V1::deleteModIndex(const QDir& index_dir, QString& mod_slug)
|
||||
{
|
||||
auto normalized_fname = indexFileName(mod_slug);
|
||||
auto real_fname = getRealIndexName(index_dir, normalized_fname);
|
||||
@ -247,7 +252,7 @@ void V1::deleteModIndex(QDir& index_dir, QString& mod_slug)
|
||||
}
|
||||
}
|
||||
|
||||
void V1::deleteModIndex(QDir& index_dir, QVariant& mod_id)
|
||||
void V1::deleteModIndex(const QDir& index_dir, QVariant& mod_id)
|
||||
{
|
||||
for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) {
|
||||
auto mod = getIndexForMod(index_dir, file_name);
|
||||
@ -259,7 +264,7 @@ void V1::deleteModIndex(QDir& index_dir, QVariant& mod_id)
|
||||
}
|
||||
}
|
||||
|
||||
auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
||||
auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod
|
||||
{
|
||||
Mod mod;
|
||||
|
||||
@ -295,7 +300,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
||||
mod.name = stringEntry(table, "name");
|
||||
mod.filename = stringEntry(table, "filename");
|
||||
mod.side = stringToSide(stringEntry(table, "side"));
|
||||
mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "x-prismlauncher-release-type"));
|
||||
mod.releaseType = ModPlatform::IndexedVersionType(table["x-prismlauncher-release-type"].value_or(""));
|
||||
if (auto loaders = table["x-prismlauncher-loaders"]; loaders && loaders.is_array()) {
|
||||
for (auto&& loader : *loaders.as_array()) {
|
||||
if (loader.is_string()) {
|
||||
@ -315,6 +320,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
||||
mod.mcVersions.sort();
|
||||
}
|
||||
}
|
||||
mod.version_number = table["x-prismlauncher-version-number"].value_or("");
|
||||
|
||||
{ // [download] info
|
||||
auto download_table = table["download"].as_table();
|
||||
@ -356,7 +362,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
||||
return mod;
|
||||
}
|
||||
|
||||
auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod
|
||||
auto V1::getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod
|
||||
{
|
||||
for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) {
|
||||
auto mod = getIndexForMod(index_dir, file_name);
|
||||
|
@ -32,11 +32,13 @@ class Mod;
|
||||
|
||||
namespace Packwiz {
|
||||
|
||||
auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString;
|
||||
auto getRealIndexName(const QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString;
|
||||
|
||||
class V1 {
|
||||
public:
|
||||
enum class Side { ClientSide = 1 << 0, ServerSide = 1 << 1, UniversalSide = ClientSide | ServerSide };
|
||||
|
||||
// can also represent other resources beside loader mods - but this is what packwiz calls it
|
||||
struct Mod {
|
||||
QString slug{};
|
||||
QString name{};
|
||||
@ -56,6 +58,7 @@ class V1 {
|
||||
ModPlatform::ResourceProvider provider{};
|
||||
QVariant file_id{};
|
||||
QVariant project_id{};
|
||||
QString version_number{};
|
||||
|
||||
public:
|
||||
// This is a totally heuristic, but should work for now.
|
||||
@ -70,33 +73,33 @@ class V1 {
|
||||
/* Generates the object representing the information in a mod.pw.toml file via
|
||||
* its common representation in the launcher, when downloading mods.
|
||||
* */
|
||||
static auto createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod;
|
||||
static auto createModFormat(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod;
|
||||
/* Generates the object representing the information in a mod.pw.toml file via
|
||||
* its common representation in the launcher, plus a necessary slug.
|
||||
* */
|
||||
static auto createModFormat(QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod;
|
||||
static auto createModFormat(const QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod;
|
||||
|
||||
/* Updates the mod index for the provided mod.
|
||||
* This creates a new index if one does not exist already
|
||||
* TODO: Ask the user if they want to override, and delete the old mod's files, or keep the old one.
|
||||
* */
|
||||
static void updateModIndex(QDir& index_dir, Mod& mod);
|
||||
static void updateModIndex(const QDir& index_dir, Mod& mod);
|
||||
|
||||
/* Deletes the metadata for the mod with the given slug. If the metadata doesn't exist, it does nothing. */
|
||||
static void deleteModIndex(QDir& index_dir, QString& mod_slug);
|
||||
static void deleteModIndex(const QDir& index_dir, QString& mod_slug);
|
||||
|
||||
/* Deletes the metadata for the mod with the given id. If the metadata doesn't exist, it does nothing. */
|
||||
static void deleteModIndex(QDir& index_dir, QVariant& mod_id);
|
||||
static void deleteModIndex(const QDir& index_dir, QVariant& mod_id);
|
||||
|
||||
/* Gets the metadata for a mod with a particular file name.
|
||||
* If the mod doesn't have a metadata, it simply returns an empty Mod object.
|
||||
* */
|
||||
static auto getIndexForMod(QDir& index_dir, QString slug) -> Mod;
|
||||
static auto getIndexForMod(const QDir& index_dir, QString slug) -> Mod;
|
||||
|
||||
/* Gets the metadata for a mod with a particular id.
|
||||
* If the mod doesn't have a metadata, it simply returns an empty Mod object.
|
||||
* */
|
||||
static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod;
|
||||
static auto getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod;
|
||||
|
||||
static auto sideToString(Side side) -> QString;
|
||||
static auto stringToSide(QString side) -> Side;
|
||||
|
@ -1048,7 +1048,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
||||
qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName;
|
||||
break;
|
||||
case PackedResourceType::Mod:
|
||||
minecraftInst->loaderModList()->installMod(localFileName, version);
|
||||
minecraftInst->loaderModList()->installResourceWithFlameMetadata(localFileName, version);
|
||||
break;
|
||||
case PackedResourceType::ShaderPack:
|
||||
minecraftInst->shaderPackList()->installResource(localFileName);
|
||||
|
@ -88,9 +88,9 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
|
||||
|
||||
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
||||
if (mcInstance) {
|
||||
const QDir index = mcInstance->loaderModList()->indexDir();
|
||||
if (index.exists())
|
||||
proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath()));
|
||||
for (auto& resourceModel : mcInstance->resourceLists())
|
||||
if (resourceModel->indexDir().exists())
|
||||
proxy->ignoreFilesWithPath().insert(root.relativeFilePath(resourceModel->indexDir().absolutePath()));
|
||||
}
|
||||
|
||||
ui->files->setModel(proxy);
|
||||
|
@ -381,7 +381,7 @@ QList<BasePage*> ShaderPackDownloadDialog::getPages()
|
||||
return pages;
|
||||
}
|
||||
|
||||
void ModDownloadDialog::setModMetadata(std::shared_ptr<Metadata::ModStruct> meta)
|
||||
void ResourceDownloadDialog::setResourceMetadata(const std::shared_ptr<Metadata::ModStruct>& meta)
|
||||
{
|
||||
switch (meta->provider) {
|
||||
case ModPlatform::ResourceProvider::MODRINTH:
|
||||
|
@ -69,6 +69,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
|
||||
const QList<DownloadTaskPtr> getTasks();
|
||||
[[nodiscard]] const std::shared_ptr<ResourceFolderModel> getBaseModel() const { return m_base_model; }
|
||||
|
||||
void setResourceMetadata(const std::shared_ptr<Metadata::ModStruct>& meta);
|
||||
|
||||
public slots:
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
@ -107,8 +109,6 @@ class ModDownloadDialog final : public ResourceDownloadDialog {
|
||||
QList<BasePage*> getPages() override;
|
||||
GetModDependenciesTask::Ptr getModDependenciesTask() override;
|
||||
|
||||
void setModMetadata(std::shared_ptr<Metadata::ModStruct>);
|
||||
|
||||
private:
|
||||
BaseInstance* m_instance;
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "ModUpdateDialog.h"
|
||||
#include "Application.h"
|
||||
#include "ResourceUpdateDialog.h"
|
||||
#include "ChooseProviderDialog.h"
|
||||
#include "CustomMessageBox.h"
|
||||
#include "ProgressDialog.h"
|
||||
@ -36,27 +35,29 @@ static QList<ModPlatform::ModLoaderType> mcLoadersList(BaseInstance* inst)
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoadersList();
|
||||
}
|
||||
|
||||
ModUpdateDialog::ModUpdateDialog(QWidget* parent,
|
||||
BaseInstance* instance,
|
||||
const std::shared_ptr<ModFolderModel> mods,
|
||||
QList<Mod*>& search_for,
|
||||
bool includeDeps)
|
||||
: ReviewMessageBox(parent, tr("Confirm mods to update"), "")
|
||||
ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent,
|
||||
BaseInstance* instance,
|
||||
const std::shared_ptr<ResourceFolderModel> resource_model,
|
||||
QList<Resource*>& search_for,
|
||||
bool include_deps,
|
||||
bool filter_loaders)
|
||||
: ReviewMessageBox(parent, tr("Confirm resources to update"), "")
|
||||
, m_parent(parent)
|
||||
, m_mod_model(mods)
|
||||
, m_resource_model(resource_model)
|
||||
, m_candidates(search_for)
|
||||
, m_second_try_metadata(
|
||||
new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()))
|
||||
, m_instance(instance)
|
||||
, m_include_deps(includeDeps)
|
||||
, m_include_deps(include_deps)
|
||||
, m_filter_loaders(filter_loaders)
|
||||
{
|
||||
ReviewMessageBox::setGeometry(0, 0, 800, 600);
|
||||
|
||||
ui->explainLabel->setText(tr("You're about to update the following mods:"));
|
||||
ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!"));
|
||||
ui->explainLabel->setText(tr("You're about to update the following resources:"));
|
||||
ui->onlyCheckedLabel->setText(tr("Only resources with a check will be updated!"));
|
||||
}
|
||||
|
||||
void ModUpdateDialog::checkCandidates()
|
||||
void ResourceUpdateDialog::checkCandidates()
|
||||
{
|
||||
// Ensure mods have valid metadata
|
||||
auto went_well = ensureMetadata();
|
||||
@ -75,8 +76,8 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
ScrollMessageBox message_dialog(m_parent, tr("Metadata generation failed"),
|
||||
tr("Could not generate metadata for the following mods:<br>"
|
||||
"Do you wish to proceed without those mods?"),
|
||||
tr("Could not generate metadata for the following resources:<br>"
|
||||
"Do you wish to proceed without those resources?"),
|
||||
text);
|
||||
message_dialog.setModal(true);
|
||||
if (message_dialog.exec() == QDialog::Rejected) {
|
||||
@ -87,21 +88,25 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
auto versions = mcVersions(m_instance);
|
||||
auto loadersList = mcLoadersList(m_instance);
|
||||
auto loadersList = m_filter_loaders ? mcLoadersList(m_instance) : QList<ModPlatform::ModLoaderType>();
|
||||
|
||||
SequentialTask check_task(m_parent, tr("Checking for updates"));
|
||||
|
||||
if (!m_modrinth_to_update.empty()) {
|
||||
m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loadersList, m_mod_model));
|
||||
m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loadersList, m_resource_model));
|
||||
connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this,
|
||||
[this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({ mod, reason, recover_url }); });
|
||||
[this](Resource* resource, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ resource, reason, recover_url });
|
||||
});
|
||||
check_task.addTask(m_modrinth_check_task);
|
||||
}
|
||||
|
||||
if (!m_flame_to_update.empty()) {
|
||||
m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loadersList, m_mod_model));
|
||||
m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loadersList, m_resource_model));
|
||||
connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this,
|
||||
[this](Mod* mod, QString reason, QUrl recover_url) { m_failed_check_update.append({ mod, reason, recover_url }); });
|
||||
[this](Resource* resource, QString reason, QUrl recover_url) {
|
||||
m_failed_check_update.append({ resource, reason, recover_url });
|
||||
});
|
||||
check_task.addTask(m_flame_check_task);
|
||||
}
|
||||
|
||||
@ -132,11 +137,11 @@ void ModUpdateDialog::checkCandidates()
|
||||
|
||||
// Add found updates for Modrinth
|
||||
if (m_modrinth_check_task) {
|
||||
auto modrinth_updates = m_modrinth_check_task->getUpdatable();
|
||||
auto modrinth_updates = m_modrinth_check_task->getUpdates();
|
||||
for (auto& updatable : modrinth_updates) {
|
||||
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
|
||||
|
||||
appendMod(updatable);
|
||||
appendResource(updatable);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
selectedVers.append(m_modrinth_check_task->getDependencies());
|
||||
@ -144,11 +149,11 @@ void ModUpdateDialog::checkCandidates()
|
||||
|
||||
// Add found updated for Flame
|
||||
if (m_flame_check_task) {
|
||||
auto flame_updates = m_flame_check_task->getUpdatable();
|
||||
auto flame_updates = m_flame_check_task->getUpdates();
|
||||
for (auto& updatable : flame_updates) {
|
||||
qDebug() << QString("Mod %1 has an update available!").arg(updatable.name);
|
||||
|
||||
appendMod(updatable);
|
||||
appendResource(updatable);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
selectedVers.append(m_flame_check_task->getDependencies());
|
||||
@ -175,8 +180,8 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
ScrollMessageBox message_dialog(m_parent, tr("Failed to check for updates"),
|
||||
tr("Could not check or get the following mods for updates:<br>"
|
||||
"Do you wish to proceed without those mods?"),
|
||||
tr("Could not check or get the following resources for updates:<br>"
|
||||
"Do you wish to proceed without those resources?"),
|
||||
text);
|
||||
message_dialog.setModal(true);
|
||||
if (message_dialog.exec() == QDialog::Rejected) {
|
||||
@ -187,59 +192,58 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
if (m_include_deps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies
|
||||
auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, m_mod_model.get(), selectedVers);
|
||||
auto* mod_model = dynamic_cast<ModFolderModel*>(m_resource_model.get());
|
||||
|
||||
connect(depTask.get(), &Task::failed, this,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
if (mod_model != nullptr) {
|
||||
auto depTask = makeShared<GetModDependenciesTask>(this, m_instance, mod_model, selectedVers);
|
||||
|
||||
auto weak = depTask.toWeakRef();
|
||||
connect(depTask.get(), &Task::succeeded, this, [this, weak]() {
|
||||
QStringList warnings;
|
||||
if (auto depTask = weak.lock()) {
|
||||
warnings = depTask->warnings();
|
||||
connect(depTask.get(), &Task::failed, this, [this](const QString& reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
});
|
||||
auto weak = depTask.toWeakRef();
|
||||
connect(depTask.get(), &Task::succeeded, this, [this, weak]() {
|
||||
QStringList warnings;
|
||||
if (auto depTask = weak.lock()) {
|
||||
warnings = depTask->warnings();
|
||||
}
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
|
||||
}
|
||||
});
|
||||
|
||||
ProgressDialog progress_dialog_deps(m_parent);
|
||||
progress_dialog_deps.setSkipButton(true, tr("Abort"));
|
||||
progress_dialog_deps.setWindowTitle(tr("Checking for dependencies..."));
|
||||
auto dret = progress_dialog_deps.execWithTask(depTask.get());
|
||||
|
||||
// If the dialog was skipped / some download error happened
|
||||
if (dret == QDialog::DialogCode::Rejected) {
|
||||
m_aborted = true;
|
||||
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
|
||||
static FlameAPI api;
|
||||
|
||||
auto dependencyExtraInfo = depTask->getExtraInfo();
|
||||
|
||||
for (const auto& dep : depTask->getDependecies()) {
|
||||
auto changelog = dep->version.changelog;
|
||||
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
|
||||
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
|
||||
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resource_model);
|
||||
auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString());
|
||||
CheckUpdateTask::Update updatable = {
|
||||
dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type,
|
||||
changelog, dep->pack->provider, download_task, !extraInfo.maybe_installed
|
||||
};
|
||||
|
||||
appendResource(updatable, extraInfo.required_by);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
});
|
||||
|
||||
ProgressDialog progress_dialog_deps(m_parent);
|
||||
progress_dialog_deps.setSkipButton(true, tr("Abort"));
|
||||
progress_dialog_deps.setWindowTitle(tr("Checking for dependencies..."));
|
||||
auto dret = progress_dialog_deps.execWithTask(depTask.get());
|
||||
|
||||
// If the dialog was skipped / some download error happened
|
||||
if (dret == QDialog::DialogCode::Rejected) {
|
||||
m_aborted = true;
|
||||
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
static FlameAPI api;
|
||||
|
||||
auto dependencyExtraInfo = depTask->getExtraInfo();
|
||||
|
||||
for (auto dep : depTask->getDependecies()) {
|
||||
auto changelog = dep->version.changelog;
|
||||
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
|
||||
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
|
||||
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_mod_model);
|
||||
auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString());
|
||||
CheckUpdateTask::UpdatableMod updatable = { dep->pack->name,
|
||||
dep->version.hash,
|
||||
"",
|
||||
dep->version.version,
|
||||
dep->version.version_type,
|
||||
changelog,
|
||||
dep->pack->provider,
|
||||
download_task,
|
||||
!extraInfo.maybe_installed };
|
||||
|
||||
appendMod(updatable, extraInfo.required_by);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no mod to be updated
|
||||
// If there's no resource to be updated
|
||||
if (ui->modTreeWidget->topLevelItemCount() == 0) {
|
||||
m_no_updates = true;
|
||||
} else {
|
||||
@ -261,7 +265,7 @@ void ModUpdateDialog::checkCandidates()
|
||||
}
|
||||
|
||||
// Part 1: Ensure we have a valid metadata
|
||||
auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
auto ResourceUpdateDialog::ensureMetadata() -> bool
|
||||
{
|
||||
auto index_dir = indexDir();
|
||||
|
||||
@ -269,27 +273,27 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
|
||||
// A better use of data structures here could remove the need for this QHash
|
||||
QHash<QString, bool> should_try_others;
|
||||
QList<Mod*> modrinth_tmp;
|
||||
QList<Mod*> flame_tmp;
|
||||
QList<Resource*> modrinth_tmp;
|
||||
QList<Resource*> flame_tmp;
|
||||
|
||||
bool confirm_rest = false;
|
||||
bool try_others_rest = false;
|
||||
bool skip_rest = false;
|
||||
ModPlatform::ResourceProvider provider_rest = ModPlatform::ResourceProvider::MODRINTH;
|
||||
|
||||
auto addToTmp = [&modrinth_tmp, &flame_tmp](Mod* m, ModPlatform::ResourceProvider p) {
|
||||
auto addToTmp = [&modrinth_tmp, &flame_tmp](Resource* resource, ModPlatform::ResourceProvider p) {
|
||||
switch (p) {
|
||||
case ModPlatform::ResourceProvider::MODRINTH:
|
||||
modrinth_tmp.push_back(m);
|
||||
modrinth_tmp.push_back(resource);
|
||||
break;
|
||||
case ModPlatform::ResourceProvider::FLAME:
|
||||
flame_tmp.push_back(m);
|
||||
flame_tmp.push_back(resource);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for (auto candidate : m_candidates) {
|
||||
if (candidate->status() != ModStatus::NoMetadata) {
|
||||
if (candidate->status() != ResourceStatus::NO_METADATA) {
|
||||
onMetadataEnsured(candidate);
|
||||
continue;
|
||||
}
|
||||
@ -308,7 +312,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
}
|
||||
|
||||
ChooseProviderDialog chooser(this);
|
||||
chooser.setDescription(tr("The mod '%1' does not have a metadata yet. We need to generate it in order to track relevant "
|
||||
chooser.setDescription(tr("The resource '%1' does not have a metadata yet. We need to generate it in order to track relevant "
|
||||
"information on how to update this mod. "
|
||||
"To do this, please select a mod provider which we can use to check for updates for this mod.")
|
||||
.arg(candidate->name()));
|
||||
@ -332,8 +336,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
|
||||
if (!modrinth_tmp.empty()) {
|
||||
auto modrinth_task = makeShared<EnsureMetadataTask>(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) {
|
||||
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH);
|
||||
});
|
||||
connect(modrinth_task.get(), &EnsureMetadataTask::failed,
|
||||
@ -347,8 +351,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
|
||||
if (!flame_tmp.empty()) {
|
||||
auto flame_task = makeShared<EnsureMetadataTask>(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME);
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) {
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) {
|
||||
onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME);
|
||||
});
|
||||
connect(flame_task.get(), &EnsureMetadataTask::failed,
|
||||
@ -370,18 +374,18 @@ auto ModUpdateDialog::ensureMetadata() -> bool
|
||||
return (ret_metadata != QDialog::DialogCode::Rejected);
|
||||
}
|
||||
|
||||
void ModUpdateDialog::onMetadataEnsured(Mod* mod)
|
||||
void ResourceUpdateDialog::onMetadataEnsured(Resource* resource)
|
||||
{
|
||||
// When the mod is a folder, for instance
|
||||
if (!mod->metadata())
|
||||
if (!resource->metadata())
|
||||
return;
|
||||
|
||||
switch (mod->metadata()->provider) {
|
||||
switch (resource->metadata()->provider) {
|
||||
case ModPlatform::ResourceProvider::MODRINTH:
|
||||
m_modrinth_to_update.push_back(mod);
|
||||
m_modrinth_to_update.push_back(resource);
|
||||
break;
|
||||
case ModPlatform::ResourceProvider::FLAME:
|
||||
m_flame_to_update.push_back(mod);
|
||||
m_flame_to_update.push_back(resource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -398,26 +402,26 @@ ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p)
|
||||
return ModPlatform::ResourceProvider::FLAME;
|
||||
}
|
||||
|
||||
void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::ResourceProvider first_choice)
|
||||
void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool try_others, ModPlatform::ResourceProvider first_choice)
|
||||
{
|
||||
if (try_others) {
|
||||
auto index_dir = indexDir();
|
||||
|
||||
auto task = makeShared<EnsureMetadataTask>(mod, index_dir, next(first_choice));
|
||||
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); });
|
||||
auto task = makeShared<EnsureMetadataTask>(resource, index_dir, next(first_choice));
|
||||
connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); });
|
||||
connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Resource* candidate) { onMetadataFailed(candidate, false); });
|
||||
connect(task.get(), &EnsureMetadataTask::failed,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
[this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
|
||||
m_second_try_metadata->addTask(task);
|
||||
} else {
|
||||
QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") };
|
||||
|
||||
m_failed_metadata.append({ mod, reason });
|
||||
m_failed_metadata.append({ resource, reason });
|
||||
}
|
||||
}
|
||||
|
||||
void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy)
|
||||
void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, QStringList requiredBy)
|
||||
{
|
||||
auto item_top = new QTreeWidgetItem(ui->modTreeWidget);
|
||||
item_top->setCheckState(0, info.enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||
@ -431,7 +435,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
||||
provider_item->setText(0, tr("Provider: %1").arg(ModPlatform::ProviderCapabilities::readableName(info.provider)));
|
||||
|
||||
auto old_version_item = new QTreeWidgetItem(item_top);
|
||||
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version));
|
||||
old_version_item->setText(0, tr("Old version: %1").arg(info.old_version));
|
||||
|
||||
auto new_version_item = new QTreeWidgetItem(item_top);
|
||||
new_version_item->setText(0, tr("New version: %1").arg(info.new_version));
|
||||
@ -485,7 +489,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri
|
||||
ui->modTreeWidget->addTopLevelItem(item_top);
|
||||
}
|
||||
|
||||
auto ModUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr>
|
||||
auto ResourceUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr>
|
||||
{
|
||||
QList<ResourceDownloadTask::Ptr> list;
|
||||
|
@ -13,22 +13,22 @@ class ModrinthCheckUpdate;
|
||||
class FlameCheckUpdate;
|
||||
class ConcurrentTask;
|
||||
|
||||
class ModUpdateDialog final : public ReviewMessageBox {
|
||||
class ResourceUpdateDialog final : public ReviewMessageBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, std::shared_ptr<ModFolderModel> mod_model, QList<Mod*>& search_for);
|
||||
explicit ModUpdateDialog(QWidget* parent,
|
||||
BaseInstance* instance,
|
||||
std::shared_ptr<ModFolderModel> mod_model,
|
||||
QList<Mod*>& search_for,
|
||||
bool includeDeps);
|
||||
explicit ResourceUpdateDialog(QWidget* parent,
|
||||
BaseInstance* instance,
|
||||
std::shared_ptr<ResourceFolderModel> resource_model,
|
||||
QList<Resource*>& search_for,
|
||||
bool include_deps,
|
||||
bool filter_loaders);
|
||||
|
||||
void checkCandidates();
|
||||
|
||||
void appendMod(const CheckUpdateTask::UpdatableMod& info, QStringList requiredBy = {});
|
||||
void appendResource(const CheckUpdateTask::Update& info, QStringList requiredBy = {});
|
||||
|
||||
const QList<ResourceDownloadTask::Ptr> getTasks();
|
||||
auto indexDir() const -> QDir { return m_mod_model->indexDir(); }
|
||||
auto indexDir() const -> QDir { return m_resource_model->indexDir(); }
|
||||
|
||||
auto noUpdates() const -> bool { return m_no_updates; };
|
||||
auto aborted() const -> bool { return m_aborted; };
|
||||
@ -37,8 +37,8 @@ class ModUpdateDialog final : public ReviewMessageBox {
|
||||
auto ensureMetadata() -> bool;
|
||||
|
||||
private slots:
|
||||
void onMetadataEnsured(Mod*);
|
||||
void onMetadataFailed(Mod*,
|
||||
void onMetadataEnsured(Resource* resource);
|
||||
void onMetadataFailed(Resource* resource,
|
||||
bool try_others = false,
|
||||
ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH);
|
||||
|
||||
@ -48,15 +48,15 @@ class ModUpdateDialog final : public ReviewMessageBox {
|
||||
shared_qobject_ptr<ModrinthCheckUpdate> m_modrinth_check_task;
|
||||
shared_qobject_ptr<FlameCheckUpdate> m_flame_check_task;
|
||||
|
||||
const std::shared_ptr<ModFolderModel> m_mod_model;
|
||||
const std::shared_ptr<ResourceFolderModel> m_resource_model;
|
||||
|
||||
QList<Mod*>& m_candidates;
|
||||
QList<Mod*> m_modrinth_to_update;
|
||||
QList<Mod*> m_flame_to_update;
|
||||
QList<Resource*>& m_candidates;
|
||||
QList<Resource*> m_modrinth_to_update;
|
||||
QList<Resource*> m_flame_to_update;
|
||||
|
||||
ConcurrentTask::Ptr m_second_try_metadata;
|
||||
QList<std::tuple<Mod*, QString>> m_failed_metadata;
|
||||
QList<std::tuple<Mod*, QString, QUrl>> m_failed_check_update;
|
||||
QList<std::tuple<Resource*, QString>> m_failed_metadata;
|
||||
QList<std::tuple<Resource*, QString, QUrl>> m_failed_check_update;
|
||||
|
||||
QHash<QString, ResourceDownloadTask::Ptr> m_tasks;
|
||||
BaseInstance* m_instance;
|
||||
@ -64,4 +64,5 @@ class ModUpdateDialog final : public ReviewMessageBox {
|
||||
bool m_no_updates = false;
|
||||
bool m_aborted = false;
|
||||
bool m_include_deps = false;
|
||||
bool m_filter_loaders = false;
|
||||
};
|
@ -132,9 +132,9 @@ class InstallJavaPage : public QWidget, public BasePage {
|
||||
m_recommended_majors = majors;
|
||||
recommendedFilterChanged();
|
||||
}
|
||||
void setRecomend(bool recomend)
|
||||
void setRecommend(bool recommend)
|
||||
{
|
||||
m_recommend = recomend;
|
||||
m_recommend = recommend;
|
||||
recommendedFilterChanged();
|
||||
}
|
||||
void recommendedFilterChanged()
|
||||
@ -202,7 +202,7 @@ InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget
|
||||
recommendedCheckBox->setCheckState(Qt::CheckState::Checked);
|
||||
connect(recommendedCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
for (BasePage* page : container->getPages()) {
|
||||
pageCast(page)->setRecomend(state == Qt::Checked);
|
||||
pageCast(page)->setRecommend(state == Qt::Checked);
|
||||
}
|
||||
});
|
||||
|
||||
@ -261,7 +261,7 @@ InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget
|
||||
container->selectPage(page->id());
|
||||
|
||||
auto cast = pageCast(page);
|
||||
cast->setRecomend(true);
|
||||
cast->setRecommend(true);
|
||||
connect(cast, &InstallJavaPage::selectionChanged, this, [this, cast] { validate(cast); });
|
||||
if (!recommendedJavas.isEmpty()) {
|
||||
cast->setRecommendedMajors(recommendedJavas);
|
||||
@ -344,4 +344,4 @@ void InstallDialog::done(int result)
|
||||
|
||||
} // namespace Java
|
||||
|
||||
#include "InstallJavaDialog.moc"
|
||||
#include "InstallJavaDialog.moc"
|
||||
|
@ -74,6 +74,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
||||
connect(ui->actionRemoveItem, &QAction::triggered, this, &ExternalResourcesPage::removeItem);
|
||||
connect(ui->actionEnableItem, &QAction::triggered, this, &ExternalResourcesPage::enableItem);
|
||||
connect(ui->actionDisableItem, &QAction::triggered, this, &ExternalResourcesPage::disableItem);
|
||||
connect(ui->actionViewHomepage, &QAction::triggered, this, &ExternalResourcesPage::viewHomepage);
|
||||
connect(ui->actionViewConfigs, &QAction::triggered, this, &ExternalResourcesPage::viewConfigs);
|
||||
connect(ui->actionViewFolder, &QAction::triggered, this, &ExternalResourcesPage::viewFolder);
|
||||
|
||||
@ -81,16 +82,28 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
||||
connect(ui->treeView, &ModListView::activated, this, &ExternalResourcesPage::itemActivated);
|
||||
|
||||
auto selection_model = ui->treeView->selectionModel();
|
||||
connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current);
|
||||
|
||||
connect(selection_model, &QItemSelectionModel::currentChanged, this, [this](const QModelIndex& current, const QModelIndex& previous) {
|
||||
if (!current.isValid()) {
|
||||
ui->frame->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
updateFrame(current, previous);
|
||||
});
|
||||
|
||||
auto updateExtra = [this]() {
|
||||
if (updateExtraInfo)
|
||||
updateExtraInfo(id(), extraHeaderInfoString());
|
||||
};
|
||||
|
||||
connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra);
|
||||
connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra);
|
||||
connect(model.get(), &ResourceFolderModel::parseFinished, this, updateExtra);
|
||||
|
||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
|
||||
connect(selection_model, &QItemSelectionModel::selectionChanged, this, [this] { updateActions(); });
|
||||
connect(m_model.get(), &ResourceFolderModel::rowsInserted, this, [this] { updateActions(); });
|
||||
connect(m_model.get(), &ResourceFolderModel::rowsRemoved, this, [this] { updateActions(); });
|
||||
|
||||
auto viewHeader = ui->treeView->header();
|
||||
viewHeader->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
@ -289,6 +302,16 @@ void ExternalResourcesPage::disableItem()
|
||||
m_model->setResourceEnabled(selection.indexes(), EnableAction::DISABLE);
|
||||
}
|
||||
|
||||
void ExternalResourcesPage::viewHomepage()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
for (auto resource : m_model->selectedResources(selection)) {
|
||||
auto url = resource->homepage();
|
||||
if (!url.isEmpty())
|
||||
DesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalResourcesPage::viewConfigs()
|
||||
{
|
||||
DesktopServices::openPath(m_instance->instanceConfigFolder(), true);
|
||||
@ -299,23 +322,32 @@ void ExternalResourcesPage::viewFolder()
|
||||
DesktopServices::openPath(m_model->dir().absolutePath(), true);
|
||||
}
|
||||
|
||||
bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous)
|
||||
void ExternalResourcesPage::updateActions()
|
||||
{
|
||||
if (!current.isValid()) {
|
||||
ui->frame->clear();
|
||||
return false;
|
||||
}
|
||||
const bool hasSelection = ui->treeView->selectionModel()->hasSelection();
|
||||
const QModelIndexList selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
const QList<Resource*> selectedResources = m_model->selectedResources(selection);
|
||||
|
||||
return onSelectionChanged(current, previous);
|
||||
ui->actionUpdateItem->setEnabled(!m_model->empty());
|
||||
ui->actionResetItemMetadata->setEnabled(hasSelection);
|
||||
|
||||
ui->actionChangeVersion->setEnabled(selectedResources.size() == 1 && selectedResources[0]->metadata() != nullptr);
|
||||
|
||||
ui->actionRemoveItem->setEnabled(hasSelection);
|
||||
ui->actionEnableItem->setEnabled(hasSelection);
|
||||
ui->actionDisableItem->setEnabled(hasSelection);
|
||||
|
||||
ui->actionViewHomepage->setEnabled(hasSelection && std::any_of(selectedResources.begin(), selectedResources.end(),
|
||||
[](Resource* resource) { return !resource->homepage().isEmpty(); }));
|
||||
ui->actionExportMetadata->setEnabled(!m_model->empty());
|
||||
}
|
||||
|
||||
bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
void ExternalResourcesPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
{
|
||||
auto sourceCurrent = m_filterModel->mapToSource(current);
|
||||
int row = sourceCurrent.row();
|
||||
Resource const& resource = m_model->at(row);
|
||||
ui->frame->updateWithResource(resource);
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ExternalResourcesPage::extraHeaderInfoString()
|
||||
|
@ -42,9 +42,8 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
|
||||
QMenu* createPopupMenu() override;
|
||||
|
||||
public slots:
|
||||
bool current(const QModelIndex& current, const QModelIndex& previous);
|
||||
|
||||
virtual bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||
virtual void updateActions();
|
||||
virtual void updateFrame(const QModelIndex& current, const QModelIndex& previous);
|
||||
|
||||
protected slots:
|
||||
void itemActivated(const QModelIndex& index);
|
||||
@ -57,6 +56,8 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
|
||||
virtual void enableItem();
|
||||
virtual void disableItem();
|
||||
|
||||
virtual void viewHomepage();
|
||||
|
||||
virtual void viewFolder();
|
||||
virtual void viewConfigs();
|
||||
|
||||
|
@ -60,7 +60,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DropOnly</enum>
|
||||
<enum>QAbstractItemView::NoDragDrop</enum>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
@ -74,7 +74,7 @@
|
||||
<string>Actions</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextOnly</enum>
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
<property name="useDefaultAction" stdset="0">
|
||||
<bool>true</bool>
|
||||
@ -90,39 +90,50 @@
|
||||
<addaction name="actionRemoveItem"/>
|
||||
<addaction name="actionEnableItem"/>
|
||||
<addaction name="actionDisableItem"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionViewHomepage"/>
|
||||
<addaction name="actionViewConfigs"/>
|
||||
<addaction name="actionViewFolder"/>
|
||||
</widget>
|
||||
<action name="actionAddItem">
|
||||
<property name="text">
|
||||
<string>&Add</string>
|
||||
<string>&Add File</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add</string>
|
||||
<string>Add a locally downloaded file.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemoveItem">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Remove</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove selected item</string>
|
||||
<string>Remove all selected items.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEnableItem">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Enable</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enable selected item</string>
|
||||
<string>Enable all selected items.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDisableItem">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Disable</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Disable selected item</string>
|
||||
<string>Disable all selected items.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewConfigs">
|
||||
@ -137,6 +148,9 @@
|
||||
<property name="text">
|
||||
<string>View &Folder</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open the folder in the system file manager.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDownloadItem">
|
||||
<property name="enabled">
|
||||
@ -146,40 +160,70 @@
|
||||
<string>&Download</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Download a new resource</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionVisitItemPage">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Visit mod's page</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Go to mods home page</string>
|
||||
<string>Download resources from online mod platforms.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUpdateItem">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check for &Updates</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Try to check or update all selected resources (all resources if none are selected)</string>
|
||||
<string>Try to check or update all selected resources (all resources if none are selected).</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionResetItemMetadata">
|
||||
<property name="text">
|
||||
<string>Reset Update Metadata</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionVerifyItemDependencies">
|
||||
<property name="text">
|
||||
<string>Verify Dependencies</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportMetadata">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export modlist</string>
|
||||
<string>Export List</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Export mod's metadata to text</string>
|
||||
<string>Export resource's metadata to text.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionChangeVersion">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change Version</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Change a resource's version.</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewHomepage">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Homepage</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View the homepages of all selected items.</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
@ -53,8 +53,8 @@
|
||||
|
||||
#include "ui/GuiUtil.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ModUpdateDialog.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
#include "ui/dialogs/ResourceUpdateDialog.h"
|
||||
|
||||
#include "DesktopServices.h"
|
||||
|
||||
@ -71,98 +71,47 @@
|
||||
#include "tasks/Task.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
|
||||
ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent)
|
||||
: ExternalResourcesPage(inst, mods, parent), m_model(mods)
|
||||
ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> model, QWidget* parent)
|
||||
: ExternalResourcesPage(inst, model, parent), m_model(model)
|
||||
{
|
||||
// This is structured like that so that these changes
|
||||
// do not affect the Resource pack and Shader pack tabs
|
||||
{
|
||||
ui->actionDownloadItem->setText(tr("Download mods"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms"));
|
||||
ui->actionDownloadItem->setEnabled(true);
|
||||
ui->actionAddItem->setText(tr("Add file"));
|
||||
ui->actionAddItem->setToolTip(tr("Add a locally downloaded file"));
|
||||
ui->actionDownloadItem->setText(tr("Download Mods"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms"));
|
||||
ui->actionDownloadItem->setEnabled(true);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem);
|
||||
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::downloadMods);
|
||||
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods);
|
||||
ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)"));
|
||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem);
|
||||
|
||||
// update menu
|
||||
auto updateMenu = ui->actionUpdateItem->menu();
|
||||
if (updateMenu) {
|
||||
updateMenu->clear();
|
||||
} else {
|
||||
updateMenu = new QMenu(this);
|
||||
}
|
||||
auto updateMenu = new QMenu(this);
|
||||
|
||||
auto update = updateMenu->addAction(tr("Check for Updates"));
|
||||
update->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)"));
|
||||
connect(update, &QAction::triggered, this, &ModFolderPage::updateMods);
|
||||
auto update = updateMenu->addAction(tr("Check for Updates"));
|
||||
connect(update, &QAction::triggered, this, &ModFolderPage::updateMods);
|
||||
|
||||
auto updateWithDeps = updateMenu->addAction(tr("Verify Dependencies"));
|
||||
updateWithDeps->setToolTip(
|
||||
tr("Try to update and check for missing dependencies all selected mods (all mods if none are selected)"));
|
||||
connect(updateWithDeps, &QAction::triggered, this, [this] { updateMods(true); });
|
||||
updateMenu->addAction(ui->actionVerifyItemDependencies);
|
||||
connect(ui->actionVerifyItemDependencies, &QAction::triggered, this, [this] { updateMods(true); });
|
||||
|
||||
auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled");
|
||||
updateWithDeps->setVisible(!depsDisabled->get().toBool());
|
||||
connect(depsDisabled.get(), &Setting::SettingChanged, this,
|
||||
[updateWithDeps](const Setting& setting, QVariant value) { updateWithDeps->setVisible(!value.toBool()); });
|
||||
auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled");
|
||||
ui->actionVerifyItemDependencies->setVisible(!depsDisabled->get().toBool());
|
||||
connect(depsDisabled.get(), &Setting::SettingChanged, this,
|
||||
[this](const Setting& setting, const QVariant& value) { ui->actionVerifyItemDependencies->setVisible(!value.toBool()); });
|
||||
|
||||
auto actionRemoveItemMetadata = updateMenu->addAction(tr("Reset update metadata"));
|
||||
actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata"));
|
||||
connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata);
|
||||
actionRemoveItemMetadata->setEnabled(false);
|
||||
updateMenu->addAction(ui->actionResetItemMetadata);
|
||||
connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata);
|
||||
|
||||
ui->actionUpdateItem->setMenu(updateMenu);
|
||||
ui->actionUpdateItem->setMenu(updateMenu);
|
||||
|
||||
ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)"));
|
||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem);
|
||||
ui->actionChangeVersion->setToolTip(tr("Change a mod's version."));
|
||||
connect(ui->actionChangeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion);
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion);
|
||||
|
||||
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
|
||||
ui->actionsToolbar->addAction(ui->actionVisitItemPage);
|
||||
connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages);
|
||||
ui->actionViewHomepage->setToolTip(tr("View the homepages of all selected mods."));
|
||||
|
||||
auto changeVersion = new QAction(tr("Change Version"), this);
|
||||
changeVersion->setToolTip(tr("Change mod version"));
|
||||
changeVersion->setEnabled(false);
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, changeVersion);
|
||||
connect(changeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion);
|
||||
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionVisitItemPage, ui->actionExportMetadata);
|
||||
connect(ui->actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata);
|
||||
|
||||
auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); };
|
||||
|
||||
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
[this, check_allow_update, actionRemoveItemMetadata, changeVersion] {
|
||||
ui->actionUpdateItem->setEnabled(check_allow_update());
|
||||
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto mods_list = m_model->selectedMods(selection);
|
||||
auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(),
|
||||
[](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; });
|
||||
if (selected <= 1) {
|
||||
ui->actionVisitItemPage->setText(tr("Visit mod's page"));
|
||||
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
|
||||
} else {
|
||||
ui->actionVisitItemPage->setText(tr("Visit mods' pages"));
|
||||
ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods"));
|
||||
}
|
||||
|
||||
changeVersion->setEnabled(mods_list.length() == 1 && mods_list[0]->metadata() != nullptr);
|
||||
ui->actionVisitItemPage->setEnabled(selected != 0);
|
||||
actionRemoveItemMetadata->setEnabled(selected != 0);
|
||||
});
|
||||
|
||||
auto updateButtons = [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); };
|
||||
connect(mods.get(), &ModFolderModel::rowsInserted, this, updateButtons);
|
||||
|
||||
connect(mods.get(), &ModFolderModel::rowsRemoved, this, updateButtons);
|
||||
|
||||
connect(mods.get(), &ModFolderModel::updateFinished, this, updateButtons);
|
||||
}
|
||||
ui->actionExportMetadata->setToolTip(tr("Export mod's metadata to text."));
|
||||
connect(ui->actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata);
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionViewHomepage, ui->actionExportMetadata);
|
||||
}
|
||||
|
||||
bool ModFolderPage::shouldDisplay() const
|
||||
@ -170,15 +119,12 @@ bool ModFolderPage::shouldDisplay() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
void ModFolderPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
{
|
||||
auto sourceCurrent = m_filterModel->mapToSource(current);
|
||||
int row = sourceCurrent.row();
|
||||
Mod const* m = m_model->at(row);
|
||||
if (m)
|
||||
ui->frame->updateWithMod(*m);
|
||||
|
||||
return true;
|
||||
const Mod& mod = m_model->at(row);
|
||||
ui->frame->updateWithMod(mod);
|
||||
}
|
||||
|
||||
void ModFolderPage::removeItems(const QItemSelection& selection)
|
||||
@ -193,10 +139,10 @@ void ModFolderPage::removeItems(const QItemSelection& selection)
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
m_model->deleteMods(selection.indexes());
|
||||
m_model->deleteResources(selection.indexes());
|
||||
}
|
||||
|
||||
void ModFolderPage::installMods()
|
||||
void ModFolderPage::downloadMods()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
@ -209,7 +155,7 @@ void ModFolderPage::installMods()
|
||||
|
||||
ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance);
|
||||
if (mdownload.exec()) {
|
||||
auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
auto tasks = new ConcurrentTask(this, tr("Download Mods"), APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
@ -266,12 +212,12 @@ void ModFolderPage::updateMods(bool includeDeps)
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
|
||||
auto mods_list = m_model->selectedMods(selection);
|
||||
auto mods_list = m_model->selectedResources(selection);
|
||||
bool use_all = mods_list.empty();
|
||||
if (use_all)
|
||||
mods_list = m_model->allMods();
|
||||
mods_list = m_model->allResources();
|
||||
|
||||
ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps);
|
||||
ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps, true);
|
||||
update_dialog.checkCandidates();
|
||||
|
||||
if (update_dialog.aborted()) {
|
||||
@ -321,6 +267,90 @@ void ModFolderPage::updateMods(bool includeDeps)
|
||||
}
|
||||
}
|
||||
|
||||
void ModFolderPage::deleteModMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectionCount = m_model->selectedMods(selection).length();
|
||||
if (selectionCount == 0)
|
||||
return;
|
||||
if (selectionCount > 1) {
|
||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
||||
tr("You are about to remove the metadata for %1 mods.\n"
|
||||
"Are you sure?")
|
||||
.arg(selectionCount),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->deleteMetadata(selection);
|
||||
}
|
||||
|
||||
void ModFolderPage::changeModVersion()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
auto profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile();
|
||||
if (!profile->getModLoaders().has_value()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!"));
|
||||
return;
|
||||
}
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto mods_list = m_model->selectedMods(selection);
|
||||
if (mods_list.length() != 1 || mods_list[0]->metadata() == nullptr)
|
||||
return;
|
||||
|
||||
ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance);
|
||||
mdownload.setResourceMetadata((*mods_list.begin())->metadata());
|
||||
if (mdownload.exec()) {
|
||||
auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count())
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto& task : mdownload.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFolderPage::exportModMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectedMods = m_model->selectedMods(selection);
|
||||
if (selectedMods.length() == 0)
|
||||
selectedMods = m_model->allMods();
|
||||
|
||||
std::sort(selectedMods.begin(), selectedMods.end(), [](const Mod* a, const Mod* b) { return a->name() < b->name(); });
|
||||
ExportToModListDialog dlg(m_instance->name(), selectedMods, this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
CoreModFolderPage::CoreModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent)
|
||||
: ModFolderPage(inst, mods, parent)
|
||||
{
|
||||
@ -369,97 +399,3 @@ bool NilModFolderPage::shouldDisplay() const
|
||||
{
|
||||
return m_model->dir().exists();
|
||||
}
|
||||
|
||||
void ModFolderPage::visitModPages()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
for (auto mod : m_model->selectedMods(selection)) {
|
||||
auto url = mod->metaurl();
|
||||
if (!url.isEmpty())
|
||||
DesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void ModFolderPage::deleteModMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectionCount = m_model->selectedMods(selection).length();
|
||||
if (selectionCount == 0)
|
||||
return;
|
||||
if (selectionCount > 1) {
|
||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
||||
tr("You are about to remove the metadata for %1 mods.\n"
|
||||
"Are you sure?")
|
||||
.arg(selectionCount),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->deleteModsMetadata(selection);
|
||||
}
|
||||
|
||||
void ModFolderPage::changeModVersion()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
auto profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile();
|
||||
if (!profile->getModLoaders().has_value()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Please install a mod loader first!"));
|
||||
return;
|
||||
}
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto mods_list = m_model->selectedMods(selection);
|
||||
if (mods_list.length() != 1 || mods_list[0]->metadata() == nullptr)
|
||||
return;
|
||||
|
||||
ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance);
|
||||
mdownload.setModMetadata((*mods_list.begin())->metadata());
|
||||
if (mdownload.exec()) {
|
||||
auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count())
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto& task : mdownload.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFolderPage::exportModMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectedMods = m_model->selectedMods(selection);
|
||||
if (selectedMods.length() == 0)
|
||||
selectedMods = m_model->allMods();
|
||||
|
||||
std::sort(selectedMods.begin(), selectedMods.end(), [](const Mod* a, const Mod* b) { return a->name() < b->name(); });
|
||||
ExportToModListDialog dlg(m_instance->name(), selectedMods, this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class ModFolderPage : public ExternalResourcesPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent = nullptr);
|
||||
explicit ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> model, QWidget* parent = nullptr);
|
||||
virtual ~ModFolderPage() = default;
|
||||
|
||||
void setFilter(const QString& filter) { m_fileSelectionFilter = filter; }
|
||||
@ -57,16 +57,15 @@ class ModFolderPage : public ExternalResourcesPage {
|
||||
virtual bool shouldDisplay() const override;
|
||||
|
||||
public slots:
|
||||
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
|
||||
void updateFrame(const QModelIndex& current, const QModelIndex& previous) override;
|
||||
|
||||
private slots:
|
||||
void removeItems(const QItemSelection& selection) override;
|
||||
|
||||
void downloadMods();
|
||||
void updateMods(bool includeDeps = false);
|
||||
void deleteModMetadata();
|
||||
void exportModMetadata();
|
||||
|
||||
void installMods();
|
||||
void updateMods(bool includeDeps = false);
|
||||
void visitModPages();
|
||||
void changeModVersion();
|
||||
|
||||
protected:
|
||||
|
@ -42,35 +42,51 @@
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
#include "ui/dialogs/ResourceUpdateDialog.h"
|
||||
|
||||
ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr<ResourcePackFolderModel> model, QWidget* parent)
|
||||
: ExternalResourcesPage(instance, model, parent)
|
||||
: ExternalResourcesPage(instance, model, parent), m_model(model)
|
||||
{
|
||||
ui->actionDownloadItem->setText(tr("Download packs"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download resource packs from online platforms"));
|
||||
ui->actionDownloadItem->setText(tr("Download Packs"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download resource packs from online mod platforms"));
|
||||
ui->actionDownloadItem->setEnabled(true);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadRPs);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem);
|
||||
|
||||
ui->actionViewConfigs->setVisible(false);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadResourcePacks);
|
||||
|
||||
ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected resource packs (all resource packs if none are selected)"));
|
||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &ResourcePackPage::updateResourcePacks);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem);
|
||||
|
||||
auto updateMenu = new QMenu(this);
|
||||
|
||||
auto update = updateMenu->addAction(ui->actionUpdateItem->text());
|
||||
connect(update, &QAction::triggered, this, &ResourcePackPage::updateResourcePacks);
|
||||
|
||||
updateMenu->addAction(ui->actionResetItemMetadata);
|
||||
connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ResourcePackPage::deleteResourcePackMetadata);
|
||||
|
||||
ui->actionUpdateItem->setMenu(updateMenu);
|
||||
|
||||
ui->actionChangeVersion->setToolTip(tr("Change a mod's version."));
|
||||
connect(ui->actionChangeVersion, &QAction::triggered, this, &ResourcePackPage::changeResourcePackVersion);
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion);
|
||||
}
|
||||
|
||||
bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
void ResourcePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
{
|
||||
auto sourceCurrent = m_filterModel->mapToSource(current);
|
||||
int row = sourceCurrent.row();
|
||||
auto& rp = static_cast<ResourcePack&>(m_model->at(row));
|
||||
ui->frame->updateWithResourcePack(rp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourcePackPage::downloadRPs()
|
||||
void ResourcePackPage::downloadResourcePacks()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
ResourceDownload::ResourcePackDownloadDialog mdownload(this, std::static_pointer_cast<ResourcePackFolderModel>(m_model), m_instance);
|
||||
ResourceDownload::ResourcePackDownloadDialog mdownload(this, m_model, m_instance);
|
||||
if (mdownload.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Resource Pack", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
@ -101,3 +117,157 @@ void ResourcePackPage::downloadRPs()
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourcePackPage::updateResourcePacks()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
auto profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile();
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Resource pack updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||
auto response = CustomMessageBox::selectable(
|
||||
this, tr("Confirm Update"),
|
||||
tr("Updating resource packs while the game is running may cause pack duplication and game crashes.\n"
|
||||
"The old files may not be deleted as they are in use.\n"
|
||||
"Are you sure you want to do this?"),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
|
||||
auto mods_list = m_model->selectedResources(selection);
|
||||
bool use_all = mods_list.empty();
|
||||
if (use_all)
|
||||
mods_list = m_model->allResources();
|
||||
|
||||
ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false);
|
||||
update_dialog.checkCandidates();
|
||||
|
||||
if (update_dialog.aborted()) {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("The resource pack updater was aborted!"), QMessageBox::Warning)->show();
|
||||
return;
|
||||
}
|
||||
if (update_dialog.noUpdates()) {
|
||||
QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) };
|
||||
if (mods_list.size() > 1) {
|
||||
if (use_all) {
|
||||
message = tr("All resource packs are up-to-date! :)");
|
||||
} else {
|
||||
message = tr("All selected resource packs are up-to-date! :)");
|
||||
}
|
||||
}
|
||||
CustomMessageBox::selectable(this, tr("Update checker"), message)->exec();
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_dialog.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
}
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto task : update_dialog.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourcePackPage::deleteResourcePackMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectionCount = m_model->selectedResourcePacks(selection).length();
|
||||
if (selectionCount == 0)
|
||||
return;
|
||||
if (selectionCount > 1) {
|
||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
||||
tr("You are about to remove the metadata for %1 resource packs.\n"
|
||||
"Are you sure?")
|
||||
.arg(selectionCount),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->deleteMetadata(selection);
|
||||
}
|
||||
|
||||
void ResourcePackPage::changeResourcePackVersion()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Resource pack updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows();
|
||||
|
||||
if (rows.count() != 1)
|
||||
return;
|
||||
|
||||
Resource &resource = m_model->at(m_filterModel->mapToSource(rows[0]).row());
|
||||
|
||||
if (resource.metadata() == nullptr)
|
||||
return;
|
||||
|
||||
ResourceDownload::ResourcePackDownloadDialog mdownload(this, m_model, m_instance);
|
||||
mdownload.setResourceMetadata(resource.metadata());
|
||||
if (mdownload.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count())
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto& task : mdownload.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
@ -58,6 +58,14 @@ class ResourcePackPage : public ExternalResourcesPage {
|
||||
}
|
||||
|
||||
public slots:
|
||||
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
|
||||
void downloadRPs();
|
||||
void updateFrame(const QModelIndex& current, const QModelIndex& previous) override;
|
||||
|
||||
private slots:
|
||||
void downloadResourcePacks();
|
||||
void updateResourcePacks();
|
||||
void deleteResourcePackMetadata();
|
||||
void changeResourcePackVersion();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<ResourcePackFolderModel> m_model;
|
||||
};
|
||||
|
@ -45,27 +45,198 @@
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
#include "ui/dialogs/ResourceUpdateDialog.h"
|
||||
|
||||
ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr<ShaderPackFolderModel> model, QWidget* parent)
|
||||
: ExternalResourcesPage(instance, model, parent)
|
||||
: ExternalResourcesPage(instance, model, parent), m_model(model)
|
||||
{
|
||||
ui->actionDownloadItem->setText(tr("Download shaders"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download shaders from online platforms"));
|
||||
ui->actionDownloadItem->setText(tr("Download Packs"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download shader packs from online mod platforms"));
|
||||
ui->actionDownloadItem->setEnabled(true);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaders);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem);
|
||||
|
||||
ui->actionViewConfigs->setVisible(false);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaderPack);
|
||||
|
||||
ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected shader packs (all shader packs if none are selected)"));
|
||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &ShaderPackPage::updateShaderPacks);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem);
|
||||
|
||||
auto updateMenu = new QMenu(this);
|
||||
|
||||
auto update = updateMenu->addAction(ui->actionUpdateItem->text());
|
||||
connect(update, &QAction::triggered, this, &ShaderPackPage::updateShaderPacks);
|
||||
|
||||
updateMenu->addAction(ui->actionResetItemMetadata);
|
||||
connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ShaderPackPage::deleteShaderPackMetadata);
|
||||
|
||||
ui->actionUpdateItem->setMenu(updateMenu);
|
||||
|
||||
ui->actionChangeVersion->setToolTip(tr("Change a shader pack's version."));
|
||||
connect(ui->actionChangeVersion, &QAction::triggered, this, &ShaderPackPage::changeShaderPackVersion);
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion);
|
||||
}
|
||||
|
||||
void ShaderPackPage::downloadShaders()
|
||||
void ShaderPackPage::downloadShaderPack()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
ResourceDownload::ShaderPackDownloadDialog mdownload(this, std::static_pointer_cast<ShaderPackFolderModel>(m_model), m_instance);
|
||||
ResourceDownload::ShaderPackDownloadDialog mdownload(this, m_model, m_instance);
|
||||
if (mdownload.exec()) {
|
||||
auto tasks = new ConcurrentTask(this, "Download Shaders", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
auto tasks = new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count())
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto& task : mdownload.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderPackPage::updateShaderPacks()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
auto profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile();
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Shader pack updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||
auto response =
|
||||
CustomMessageBox::selectable(this, tr("Confirm Update"),
|
||||
tr("Updating shader packs while the game is running may pack duplication and game crashes.\n"
|
||||
"The old files may not be deleted as they are in use.\n"
|
||||
"Are you sure you want to do this?"),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
|
||||
auto mods_list = m_model->selectedResources(selection);
|
||||
bool use_all = mods_list.empty();
|
||||
if (use_all)
|
||||
mods_list = m_model->allResources();
|
||||
|
||||
ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false);
|
||||
update_dialog.checkCandidates();
|
||||
|
||||
if (update_dialog.aborted()) {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("The shader pack updater was aborted!"), QMessageBox::Warning)->show();
|
||||
return;
|
||||
}
|
||||
if (update_dialog.noUpdates()) {
|
||||
QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) };
|
||||
if (mods_list.size() > 1) {
|
||||
if (use_all) {
|
||||
message = tr("All shader packs are up-to-date! :)");
|
||||
} else {
|
||||
message = tr("All selected shader packs are up-to-date! :)");
|
||||
}
|
||||
}
|
||||
CustomMessageBox::selectable(this, tr("Update checker"), message)->exec();
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_dialog.exec()) {
|
||||
auto tasks = new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
}
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto task : update_dialog.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderPackPage::deleteShaderPackMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectionCount = m_model->selectedShaderPacks(selection).length();
|
||||
if (selectionCount == 0)
|
||||
return;
|
||||
if (selectionCount > 1) {
|
||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
||||
tr("You are about to remove the metadata for %1 shader packs.\n"
|
||||
"Are you sure?")
|
||||
.arg(selectionCount),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->deleteMetadata(selection);
|
||||
}
|
||||
|
||||
void ShaderPackPage::changeShaderPackVersion()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Shader pack updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows();
|
||||
|
||||
if (rows.count() != 1)
|
||||
return;
|
||||
|
||||
Resource &resource = m_model->at(m_filterModel->mapToSource(rows[0]).row());
|
||||
|
||||
if (resource.metadata() == nullptr)
|
||||
return;
|
||||
|
||||
ResourceDownload::ShaderPackDownloadDialog mdownload(this, m_model, m_instance);
|
||||
mdownload.setResourceMetadata(resource.metadata());
|
||||
if (mdownload.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
|
@ -53,5 +53,11 @@ class ShaderPackPage : public ExternalResourcesPage {
|
||||
bool shouldDisplay() const override { return true; }
|
||||
|
||||
public slots:
|
||||
void downloadShaders();
|
||||
void downloadShaderPack();
|
||||
void updateShaderPacks();
|
||||
void deleteShaderPackMetadata();
|
||||
void changeShaderPackVersion();
|
||||
|
||||
private:
|
||||
std::shared_ptr<ShaderPackFolderModel> m_model;
|
||||
};
|
||||
|
@ -44,35 +44,206 @@
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
#include "ui/dialogs/ResourceUpdateDialog.h"
|
||||
|
||||
TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptr<TexturePackFolderModel> model, QWidget* parent)
|
||||
: ExternalResourcesPage(instance, model, parent)
|
||||
: ExternalResourcesPage(instance, model, parent), m_model(model)
|
||||
{
|
||||
ui->actionDownloadItem->setText(tr("Download packs"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download texture packs from online platforms"));
|
||||
ui->actionDownloadItem->setText(tr("Download Packs"));
|
||||
ui->actionDownloadItem->setToolTip(tr("Download texture packs from online mod platforms"));
|
||||
ui->actionDownloadItem->setEnabled(true);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTPs);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem);
|
||||
|
||||
ui->actionViewConfigs->setVisible(false);
|
||||
connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTexturePacks);
|
||||
|
||||
ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected texture packs (all texture packs if none are selected)"));
|
||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &TexturePackPage::updateTexturePacks);
|
||||
ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem);
|
||||
|
||||
auto updateMenu = new QMenu(this);
|
||||
|
||||
auto update = updateMenu->addAction(ui->actionUpdateItem->text());
|
||||
connect(update, &QAction::triggered, this, &TexturePackPage::updateTexturePacks);
|
||||
|
||||
updateMenu->addAction(ui->actionResetItemMetadata);
|
||||
connect(ui->actionResetItemMetadata, &QAction::triggered, this, &TexturePackPage::deleteTexturePackMetadata);
|
||||
|
||||
ui->actionUpdateItem->setMenu(updateMenu);
|
||||
|
||||
ui->actionChangeVersion->setToolTip(tr("Change a texture pack's version."));
|
||||
connect(ui->actionChangeVersion, &QAction::triggered, this, &TexturePackPage::changeTexturePackVersion);
|
||||
ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion);
|
||||
|
||||
ui->actionViewHomepage->setToolTip(tr("View the homepages of all selected texture packs."));
|
||||
}
|
||||
|
||||
bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
void TexturePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||
{
|
||||
auto sourceCurrent = m_filterModel->mapToSource(current);
|
||||
int row = sourceCurrent.row();
|
||||
auto& rp = static_cast<TexturePack&>(m_model->at(row));
|
||||
ui->frame->updateWithTexturePack(rp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TexturePackPage::downloadTPs()
|
||||
void TexturePackPage::downloadTexturePacks()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
ResourceDownload::TexturePackDownloadDialog mdownload(this, std::static_pointer_cast<TexturePackFolderModel>(m_model), m_instance);
|
||||
ResourceDownload::TexturePackDownloadDialog mdownload(this, m_model, m_instance);
|
||||
if (mdownload.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count())
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto& task : mdownload.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void TexturePackPage::updateTexturePacks()
|
||||
{
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
auto profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile();
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Texture pack updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||
auto response = CustomMessageBox::selectable(
|
||||
this, tr("Confirm Update"),
|
||||
tr("Updating texture packs while the game is running may cause pack duplication and game crashes.\n"
|
||||
"The old files may not be deleted as they are in use.\n"
|
||||
"Are you sure you want to do this?"),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
|
||||
auto mods_list = m_model->selectedResources(selection);
|
||||
bool use_all = mods_list.empty();
|
||||
if (use_all)
|
||||
mods_list = m_model->allResources();
|
||||
|
||||
ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false);
|
||||
update_dialog.checkCandidates();
|
||||
|
||||
if (update_dialog.aborted()) {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("The texture pack updater was aborted!"), QMessageBox::Warning)->show();
|
||||
return;
|
||||
}
|
||||
if (update_dialog.noUpdates()) {
|
||||
QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) };
|
||||
if (mods_list.size() > 1) {
|
||||
if (use_all) {
|
||||
message = tr("All texture packs are up-to-date! :)");
|
||||
} else {
|
||||
message = tr("All selected texture packs are up-to-date! :)");
|
||||
}
|
||||
}
|
||||
CustomMessageBox::selectable(this, tr("Update checker"), message)->exec();
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_dialog.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
connect(tasks, &Task::failed, [this, tasks](QString reason) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::aborted, [this, tasks]() {
|
||||
CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
|
||||
tasks->deleteLater();
|
||||
});
|
||||
connect(tasks, &Task::succeeded, [this, tasks]() {
|
||||
QStringList warnings = tasks->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
}
|
||||
tasks->deleteLater();
|
||||
});
|
||||
|
||||
for (auto task : update_dialog.getTasks()) {
|
||||
tasks->addTask(task);
|
||||
}
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(tasks);
|
||||
|
||||
m_model->update();
|
||||
}
|
||||
}
|
||||
|
||||
void TexturePackPage::deleteTexturePackMetadata()
|
||||
{
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
auto selectionCount = m_model->selectedTexturePacks(selection).length();
|
||||
if (selectionCount == 0)
|
||||
return;
|
||||
if (selectionCount > 1) {
|
||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
||||
tr("You are about to remove the metadata for %1 texture packs.\n"
|
||||
"Are you sure?")
|
||||
.arg(selectionCount),
|
||||
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (response != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->deleteMetadata(selection);
|
||||
}
|
||||
|
||||
void TexturePackPage::changeTexturePackVersion() {
|
||||
if (m_instance->typeName() != "Minecraft")
|
||||
return; // this is a null instance or a legacy instance
|
||||
|
||||
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("Texture pack updates are unavailable when metadata is disabled!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows();
|
||||
|
||||
if (rows.count() != 1)
|
||||
return;
|
||||
|
||||
Resource &resource = m_model->at(m_filterModel->mapToSource(rows[0]).row());
|
||||
|
||||
if (resource.metadata() == nullptr)
|
||||
return;
|
||||
|
||||
ResourceDownload::TexturePackDownloadDialog mdownload(this, m_model, m_instance);
|
||||
mdownload.setResourceMetadata(resource.metadata());
|
||||
if (mdownload.exec()) {
|
||||
auto tasks =
|
||||
new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
|
||||
|
@ -55,6 +55,12 @@ class TexturePackPage : public ExternalResourcesPage {
|
||||
virtual bool shouldDisplay() const override { return m_instance->traits().contains("texturepacks"); }
|
||||
|
||||
public slots:
|
||||
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
|
||||
void downloadTPs();
|
||||
void updateFrame(const QModelIndex& current, const QModelIndex& previous) override;
|
||||
void downloadTexturePacks();
|
||||
void updateTexturePacks();
|
||||
void deleteTexturePackMetadata();
|
||||
void changeTexturePackVersion();
|
||||
|
||||
private:
|
||||
std::shared_ptr<TexturePackFolderModel> m_model;
|
||||
};
|
||||
|
@ -42,12 +42,13 @@
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui_ResourcePage.h"
|
||||
|
||||
#include <StringUtils.h>
|
||||
#include <QDesktopServices>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "Markdown.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include "Application.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
#include "ui/pages/modplatform/ResourceModel.h"
|
||||
#include "ui/widgets/ProjectItem.h"
|
||||
@ -349,7 +350,8 @@ void ResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
|
||||
ModPlatform::IndexedVersion& ver,
|
||||
const std::shared_ptr<ResourceFolderModel> base_model)
|
||||
{
|
||||
m_model->addPack(pack, ver, base_model);
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
m_model->addPack(pack, ver, base_model, is_indexed);
|
||||
}
|
||||
|
||||
void ResourcePage::removeResourceFromPage(const QString& name)
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "ShaderPackModel.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
@ -48,10 +49,11 @@ void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pac
|
||||
ModPlatform::IndexedVersion& version,
|
||||
const std::shared_ptr<ResourceFolderModel> base_model)
|
||||
{
|
||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||
QString custom_target_folder;
|
||||
if (version.loaders & ModPlatform::Cauldron)
|
||||
custom_target_folder = QStringLiteral("resourcepacks");
|
||||
m_model->addPack(pack, version, base_model, false, custom_target_folder);
|
||||
m_model->addPack(pack, version, base_model, is_indexed, custom_target_folder);
|
||||
}
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
@ -84,7 +84,7 @@ void InfoFrame::updateWithMod(Mod const& m)
|
||||
|
||||
QString text = "";
|
||||
QString name = "";
|
||||
QString link = m.metaurl();
|
||||
QString link = m.homepage();
|
||||
if (m.name().isEmpty())
|
||||
name = m.internal_id();
|
||||
else
|
||||
@ -93,7 +93,7 @@ void InfoFrame::updateWithMod(Mod const& m)
|
||||
if (link.isEmpty())
|
||||
text = name;
|
||||
else {
|
||||
text = "<a href=\"" + link + "\">" + name + "</a>";
|
||||
text = "<a href=\"" + QUrl(link).toEncoded() + "\">" + name + "</a>";
|
||||
}
|
||||
if (!m.authors().isEmpty())
|
||||
text += " by " + m.authors().join(", ");
|
||||
@ -145,7 +145,13 @@ void InfoFrame::updateWithMod(Mod const& m)
|
||||
|
||||
void InfoFrame::updateWithResource(const Resource& resource)
|
||||
{
|
||||
setName(resource.name());
|
||||
const QString homepage = resource.homepage();
|
||||
|
||||
if (!homepage.isEmpty())
|
||||
setName("<a href=\"" + homepage + "\">" + resource.name() + "</a>");
|
||||
else
|
||||
setName(resource.name());
|
||||
|
||||
setImage();
|
||||
}
|
||||
|
||||
@ -209,14 +215,28 @@ QString InfoFrame::renderColorCodes(QString input)
|
||||
|
||||
void InfoFrame::updateWithResourcePack(ResourcePack& resource_pack)
|
||||
{
|
||||
setName(renderColorCodes(resource_pack.name()));
|
||||
QString name = renderColorCodes(resource_pack.name());
|
||||
|
||||
const QString homepage = resource_pack.homepage();
|
||||
if (!homepage.isEmpty()) {
|
||||
name = "<a href=\"" + homepage + "\">" + name + "</a>";
|
||||
}
|
||||
|
||||
setName(name);
|
||||
setDescription(renderColorCodes(resource_pack.description()));
|
||||
setImage(resource_pack.image({ 64, 64 }));
|
||||
}
|
||||
|
||||
void InfoFrame::updateWithTexturePack(TexturePack& texture_pack)
|
||||
{
|
||||
setName(renderColorCodes(texture_pack.name()));
|
||||
QString name = renderColorCodes(texture_pack.name());
|
||||
|
||||
const QString homepage = texture_pack.homepage();
|
||||
if (!homepage.isEmpty()) {
|
||||
name = "<a href=\"" + homepage + "\">" + name + "</a>";
|
||||
}
|
||||
|
||||
setName(name);
|
||||
setDescription(renderColorCodes(texture_pack.description()));
|
||||
setImage(texture_pack.image({ 64, 64 }));
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<launchable type="desktop-id">org.prismlauncher.PrismLauncher.desktop</launchable>
|
||||
<name>Prism Launcher</name>
|
||||
<developer_name>Prism Launcher Contributors</developer_name>
|
||||
<summary>A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once</summary>
|
||||
<summary>Custom Minecraft Launcher to easily manage multiple Minecraft installations at once</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0-only</project_license>
|
||||
<url type="homepage">https://prismlauncher.org/</url>
|
||||
|
@ -87,7 +87,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
|
||||
QEventLoop loop;
|
||||
|
||||
ModFolderModel m(tempDir.path(), nullptr, true);
|
||||
ModFolderModel m(tempDir.path(), nullptr, true, true);
|
||||
|
||||
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
|
||||
|
||||
@ -96,7 +96,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
expire_timer.setSingleShot(true);
|
||||
expire_timer.start(4000);
|
||||
|
||||
m.installMod(folder);
|
||||
m.installResource(folder);
|
||||
|
||||
loop.exec();
|
||||
|
||||
@ -111,7 +111,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
QString folder = source + '/';
|
||||
QTemporaryDir tempDir;
|
||||
QEventLoop loop;
|
||||
ModFolderModel m(tempDir.path(), nullptr, true);
|
||||
ModFolderModel m(tempDir.path(), nullptr, true, true);
|
||||
|
||||
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
|
||||
|
||||
@ -120,7 +120,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
expire_timer.setSingleShot(true);
|
||||
expire_timer.start(4000);
|
||||
|
||||
m.installMod(folder);
|
||||
m.installResource(folder);
|
||||
|
||||
loop.exec();
|
||||
|
||||
@ -134,7 +134,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
void test_addFromWatch()
|
||||
{
|
||||
QString source = QFINDTESTDATA("testdata/ResourceFolderModel");
|
||||
ModFolderModel model(source, nullptr);
|
||||
ModFolderModel model(source, nullptr, false, true);
|
||||
|
||||
QCOMPARE(model.size(), 0);
|
||||
|
||||
@ -154,7 +154,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
|
||||
|
||||
QTemporaryDir tmp;
|
||||
ResourceFolderModel model(QDir(tmp.path()), nullptr);
|
||||
ResourceFolderModel model(QDir(tmp.path()), nullptr, false, false);
|
||||
|
||||
QCOMPARE(model.size(), 0);
|
||||
|
||||
@ -199,7 +199,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
|
||||
|
||||
QTemporaryDir tmp;
|
||||
ResourceFolderModel model(tmp.path(), nullptr);
|
||||
ResourceFolderModel model(tmp.path(), nullptr, false, false);
|
||||
|
||||
QCOMPARE(model.size(), 0);
|
||||
|
||||
@ -210,7 +210,7 @@ class ResourceFolderModelTest : public QObject {
|
||||
EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY)
|
||||
}
|
||||
|
||||
for (auto res : model.all())
|
||||
for (auto res : model.allResources())
|
||||
qDebug() << res->name();
|
||||
|
||||
QCOMPARE(model.size(), 2);
|
||||
|
Loading…
x
Reference in New Issue
Block a user