mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-07-06 05:16:59 +03:00
Merge cbe143c266 into d2fa7cf7f7
This commit is contained in:
commit
74af7592c8
30 changed files with 1193 additions and 1181 deletions
|
|
@ -46,13 +46,10 @@
|
|||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "settings/OverrideSetting.h"
|
||||
#include "settings/Setting.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "Commandline.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
int getConsoleMaxLines(SettingsObject* settings)
|
||||
{
|
||||
|
|
@ -71,20 +68,18 @@ bool shouldStopOnConsoleOverflow(SettingsObject* settings)
|
|||
return settings->get("ConsoleOverflowStop").toBool();
|
||||
}
|
||||
|
||||
BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, const QString& rootDir) : QObject()
|
||||
BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, QString rootDir)
|
||||
: m_rootDir(std::move(rootDir)), m_settings(std::move(settings)), m_global_settings(globalSettings)
|
||||
{
|
||||
m_settings = std::move(settings);
|
||||
m_global_settings = globalSettings;
|
||||
m_rootDir = rootDir;
|
||||
|
||||
m_settings->registerSetting("name", "Unnamed Instance");
|
||||
m_settings->registerSetting("iconKey", "default");
|
||||
m_settings->registerSetting("notes", "");
|
||||
|
||||
m_settings->registerSetting("lastLaunchTime", 0);
|
||||
m_settings->registerSetting("totalTimePlayed", 0);
|
||||
if (m_settings->get("totalTimePlayed").toLongLong() < 0)
|
||||
if (m_settings->get("totalTimePlayed").toLongLong() < 0) {
|
||||
m_settings->reset("totalTimePlayed");
|
||||
}
|
||||
m_settings->registerSetting("lastTimePlayed", 0);
|
||||
|
||||
m_settings->registerSetting("linkedInstances", "[]");
|
||||
|
|
@ -97,8 +92,9 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<Setti
|
|||
|
||||
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
|
||||
// a locally stored instance
|
||||
if (!m_settings->getSetting("InstanceType"))
|
||||
if (!m_settings->getSetting("InstanceType")) {
|
||||
m_settings->registerSetting("InstanceType", "");
|
||||
}
|
||||
|
||||
// Custom Commands
|
||||
auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false);
|
||||
|
|
@ -128,8 +124,6 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr<Setti
|
|||
m_settings->registerSetting("Profiler", "");
|
||||
}
|
||||
|
||||
BaseInstance::~BaseInstance() {}
|
||||
|
||||
QString BaseInstance::getPreLaunchCommand()
|
||||
{
|
||||
return settings()->get("PreLaunchCommand").toString();
|
||||
|
|
@ -189,22 +183,6 @@ void BaseInstance::setManagedPack(const QString& type,
|
|||
m_settings->set("ManagedPackVersionName", version);
|
||||
}
|
||||
|
||||
void BaseInstance::copyManagedPack(BaseInstance& other)
|
||||
{
|
||||
m_settings->set("ManagedPack", other.isManagedPack());
|
||||
m_settings->set("ManagedPackType", other.getManagedPackType());
|
||||
m_settings->set("ManagedPackID", other.getManagedPackID());
|
||||
m_settings->set("ManagedPackName", other.getManagedPackName());
|
||||
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
|
||||
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
|
||||
|
||||
if (APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() && m_settings->get("AutomaticJava").toBool() &&
|
||||
m_settings->get("OverrideJavaLocation").toBool()) {
|
||||
m_settings->set("OverrideJavaLocation", false);
|
||||
m_settings->set("JavaPath", "");
|
||||
}
|
||||
}
|
||||
|
||||
QStringList BaseInstance::getLinkedInstances() const
|
||||
{
|
||||
auto setting = m_settings->get("linkedInstances").toString();
|
||||
|
|
@ -226,7 +204,7 @@ void BaseInstance::addLinkedInstanceId(const QString& id)
|
|||
bool BaseInstance::removeLinkedInstanceId(const QString& id)
|
||||
{
|
||||
auto linkedInstances = getLinkedInstances();
|
||||
int numRemoved = linkedInstances.removeAll(id);
|
||||
auto numRemoved = linkedInstances.removeAll(id);
|
||||
setLinkedInstances(linkedInstances);
|
||||
return numRemoved > 0;
|
||||
}
|
||||
|
|
@ -237,7 +215,7 @@ bool BaseInstance::isLinkedToInstanceId(const QString& id) const
|
|||
return linkedInstances.contains(id);
|
||||
}
|
||||
|
||||
void BaseInstance::iconUpdated(QString key)
|
||||
void BaseInstance::iconUpdated(const QString& key)
|
||||
{
|
||||
if (iconKey() == key) {
|
||||
emit propertiesChanged(this);
|
||||
|
|
@ -276,8 +254,9 @@ bool BaseInstance::isRunning() const
|
|||
|
||||
void BaseInstance::setRunning(bool running)
|
||||
{
|
||||
if (running == m_isRunning)
|
||||
if (running == m_isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_isRunning = running;
|
||||
|
||||
|
|
@ -368,7 +347,7 @@ void BaseInstance::setLastLaunch(qint64 val)
|
|||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
void BaseInstance::setNotes(QString val)
|
||||
void BaseInstance::setNotes(const QString& val)
|
||||
{
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("notes", val);
|
||||
|
|
@ -379,7 +358,7 @@ QString BaseInstance::notes() const
|
|||
return m_settings->get("notes").toString();
|
||||
}
|
||||
|
||||
void BaseInstance::setIconKey(QString val)
|
||||
void BaseInstance::setIconKey(const QString& val)
|
||||
{
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("iconKey", val);
|
||||
|
|
@ -391,7 +370,7 @@ QString BaseInstance::iconKey() const
|
|||
return m_settings->get("iconKey").toString();
|
||||
}
|
||||
|
||||
void BaseInstance::setName(QString val)
|
||||
void BaseInstance::setName(const QString& val)
|
||||
{
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("name", val);
|
||||
|
|
@ -430,19 +409,23 @@ QList<ShortcutData> BaseInstance::shortcuts() const
|
|||
auto data = m_settings->get("shortcuts").toString().toUtf8();
|
||||
QJsonParseError parseError;
|
||||
auto document = QJsonDocument::fromJson(data, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError || !document.isArray())
|
||||
if (parseError.error != QJsonParseError::NoError || !document.isArray()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<ShortcutData> results;
|
||||
for (const auto& elem : document.array()) {
|
||||
if (!elem.isObject())
|
||||
if (!elem.isObject()) {
|
||||
continue;
|
||||
}
|
||||
auto dict = elem.toObject();
|
||||
if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target"))
|
||||
if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target")) {
|
||||
continue;
|
||||
}
|
||||
int value = dict["target"].toInt(-1);
|
||||
if (!dict["name"].isString() || !dict["filePath"].isString() || value < 0 || value >= 3)
|
||||
if (!dict.value("name").isString() || !dict.value("filePath").isString() || value < 0 || value >= 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString shortcutName = dict["name"].toString();
|
||||
QString filePath = dict["filePath"].toString();
|
||||
|
|
@ -481,7 +464,7 @@ void BaseInstance::updateRuntimeContext()
|
|||
// NOOP
|
||||
}
|
||||
|
||||
bool BaseInstance::isLegacy()
|
||||
bool BaseInstance::isLegacy() const
|
||||
{
|
||||
return traits().contains("legacyLaunch") || traits().contains("alphaLaunch");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,16 +45,12 @@
|
|||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QSet>
|
||||
#include <cstdint>
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#include "BaseVersionList.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "settings/INIFile.h"
|
||||
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "RuntimeContext.h"
|
||||
#include "minecraft/launch/MinecraftTarget.h"
|
||||
|
|
@ -65,7 +61,7 @@ class LaunchTask;
|
|||
class BaseInstance;
|
||||
|
||||
/// Shortcut saving target representations
|
||||
enum class ShortcutTarget { Desktop, Applications, Other };
|
||||
enum class ShortcutTarget : std::uint8_t { Desktop, Applications, Other };
|
||||
|
||||
/// Shortcut data representation
|
||||
struct ShortcutData {
|
||||
|
|
@ -90,17 +86,17 @@ class BaseInstance : public QObject {
|
|||
Q_OBJECT
|
||||
protected:
|
||||
/// no-touchy!
|
||||
BaseInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, const QString& rootDir);
|
||||
BaseInstance(SettingsObject* globalSettings, std::unique_ptr<SettingsObject> settings, QString rootDir);
|
||||
|
||||
public: /* types */
|
||||
enum class Status {
|
||||
enum class Status : std::uint8_t {
|
||||
Present,
|
||||
Gone // either nuked or invalidated
|
||||
};
|
||||
|
||||
public:
|
||||
/// virtual destructor to make sure the destruction is COMPLETE
|
||||
virtual ~BaseInstance();
|
||||
~BaseInstance() override = default;
|
||||
|
||||
virtual void saveNow() = 0;
|
||||
|
||||
|
|
@ -136,7 +132,7 @@ class BaseInstance : public QObject {
|
|||
virtual QString modsRoot() const = 0;
|
||||
|
||||
QString name() const;
|
||||
void setName(QString val);
|
||||
void setName(const QString& val);
|
||||
|
||||
/// Sync name and rename instance dir accordingly; returns true if successful
|
||||
bool syncInstanceDirName(const QString& newRoot) const;
|
||||
|
|
@ -150,10 +146,10 @@ class BaseInstance : public QObject {
|
|||
QString windowTitle() const;
|
||||
|
||||
QString iconKey() const;
|
||||
void setIconKey(QString val);
|
||||
void setIconKey(const QString& val);
|
||||
|
||||
QString notes() const;
|
||||
void setNotes(QString val);
|
||||
void setNotes(const QString& val);
|
||||
|
||||
QString getPreLaunchCommand();
|
||||
QString getPostExitCommand();
|
||||
|
|
@ -166,7 +162,6 @@ class BaseInstance : public QObject {
|
|||
QString getManagedPackVersionID() const;
|
||||
QString getManagedPackVersionName() const;
|
||||
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
|
||||
void copyManagedPack(BaseInstance& other);
|
||||
|
||||
virtual QStringList extraArguments();
|
||||
|
||||
|
|
@ -278,7 +273,7 @@ class BaseInstance : public QObject {
|
|||
bool removeLinkedInstanceId(const QString& id);
|
||||
bool isLinkedToInstanceId(const QString& id) const;
|
||||
|
||||
bool isLegacy();
|
||||
bool isLegacy() const;
|
||||
|
||||
protected:
|
||||
void changeStatus(Status newStatus);
|
||||
|
|
@ -303,7 +298,7 @@ class BaseInstance : public QObject {
|
|||
void statusChanged(Status from, Status to);
|
||||
|
||||
protected slots:
|
||||
void iconUpdated(QString key);
|
||||
void iconUpdated(const QString& key);
|
||||
|
||||
protected: /* data */
|
||||
QString m_rootDir;
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ set(CORE_SOURCES
|
|||
PSaveFile.h
|
||||
|
||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||
InstanceCreationTask.h
|
||||
InstanceCreationTask.cpp
|
||||
InstanceCopyPrefs.h
|
||||
InstanceCopyPrefs.cpp
|
||||
InstanceCopyTask.h
|
||||
|
|
|
|||
|
|
@ -1,142 +0,0 @@
|
|||
#include "InstanceCreationTask.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "minecraft/MinecraftLoadAndCheck.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
bool InstanceCreationTask::abort()
|
||||
{
|
||||
if (!canAbort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_abort = true;
|
||||
if (m_gameFilesTask) {
|
||||
return m_gameFilesTask->abort();
|
||||
}
|
||||
|
||||
return InstanceTask::abort();
|
||||
}
|
||||
|
||||
void InstanceCreationTask::executeTask()
|
||||
{
|
||||
setAbortable(true);
|
||||
|
||||
if (updateInstance()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
// When the user aborted in the update stage.
|
||||
if (m_abort) {
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
|
||||
m_instance = createInstance();
|
||||
if (!m_instance) {
|
||||
if (m_abort) {
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << "Instance creation failed!";
|
||||
if (!m_error_message.isEmpty()) {
|
||||
qWarning() << "Reason:" << m_error_message;
|
||||
emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message));
|
||||
} else {
|
||||
emitFailed(tr("Error while creating new instance."));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is set, it means we're updating an instance. So, we now need to remove the
|
||||
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
|
||||
// put the instance in an invalid state.
|
||||
if (shouldOverride()) {
|
||||
bool deleteFailed = false;
|
||||
|
||||
setAbortable(false);
|
||||
setStatus(tr("Removing old conflicting files..."));
|
||||
qDebug() << "Removing old files";
|
||||
|
||||
for (const QString& path : m_filesToRemove) {
|
||||
if (!QFile::exists(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "Removing" << path;
|
||||
|
||||
if (!QFile::remove(path)) {
|
||||
qCritical() << "Could not remove" << path;
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFailed) {
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_abort) {
|
||||
if (!APPLICATION->settings()->get("DownloadGameFilesDuringInstanceCreation").toBool()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
setAbortable(true);
|
||||
setAbortButtonText(tr("Skip"));
|
||||
qDebug() << "Downloading game files";
|
||||
|
||||
auto updateTasks = m_instance->createUpdateTask();
|
||||
if (updateTasks.isEmpty()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto task = makeShared<SequentialTask>();
|
||||
task->addTask(makeShared<MinecraftLoadAndCheck>(m_instance.get(), Net::Mode::Online));
|
||||
for (const auto& t : updateTasks) {
|
||||
task->addTask(t);
|
||||
}
|
||||
connect(task.get(), &Task::finished, this, [this, task] {
|
||||
if (task->wasSuccessful() || m_abort) {
|
||||
emitSucceeded();
|
||||
} else {
|
||||
emitFailed(tr("Could not download game files: %1").arg(task->failReason()));
|
||||
}
|
||||
});
|
||||
propagateFromOther(task.get());
|
||||
setDetails(tr("Downloading game files"));
|
||||
|
||||
m_gameFilesTask = task;
|
||||
m_gameFilesTask->start();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceCreationTask::scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (path.startsWith("saves/")) {
|
||||
if (m_shouldDeleteSaves == ShouldDeleteSaves::NotAsked) {
|
||||
m_shouldDeleteSaves = askIfShouldDeleteSaves(parent);
|
||||
}
|
||||
if (m_shouldDeleteSaves == ShouldDeleteSaves::No) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
qDebug() << "Scheduling" << path << "for removal";
|
||||
m_filesToRemove.append(dir.absoluteFilePath(path));
|
||||
if (checkDisabled) {
|
||||
if (path.endsWith(".disabled")) { // remove it if it was enabled/disabled by user
|
||||
m_filesToRemove.append(dir.absoluteFilePath(path.chopped(9)));
|
||||
} else {
|
||||
m_filesToRemove.append(dir.absoluteFilePath(path + ".disabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
class InstanceCreationTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstanceCreationTask() = default;
|
||||
virtual ~InstanceCreationTask() = default;
|
||||
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
void executeTask() final override;
|
||||
|
||||
/**
|
||||
* Tries to update an already existing instance.
|
||||
*
|
||||
* This can be implemented by subclasses to provide a way of updating an already existing
|
||||
* instance, according to that implementation's concept of 'identity' (i.e. instances that
|
||||
* are updates / downgrades of one another).
|
||||
*
|
||||
* If this returns true, createInstance() will not run, so you should do all update steps in here.
|
||||
* Otherwise, createInstance() is run as normal.
|
||||
*/
|
||||
virtual bool updateInstance() { return false; };
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* Returns the instance if it was created or nullptr otherwise.
|
||||
*/
|
||||
virtual std::unique_ptr<MinecraftInstance> createInstance() { return nullptr; }
|
||||
|
||||
QString getError() const { return m_error_message; }
|
||||
|
||||
protected:
|
||||
void setError(const QString& message) { m_error_message = message; };
|
||||
void scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled = false);
|
||||
|
||||
protected:
|
||||
bool m_abort = false;
|
||||
|
||||
QStringList m_filesToRemove;
|
||||
ShouldDeleteSaves m_shouldDeleteSaves;
|
||||
|
||||
private:
|
||||
QString m_error_message;
|
||||
std::unique_ptr<MinecraftInstance> m_instance;
|
||||
Task::Ptr m_gameFilesTask;
|
||||
};
|
||||
|
|
@ -58,19 +58,22 @@
|
|||
#include <QFileInfo>
|
||||
#include <QtConcurrentRun>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
|
||||
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
|
||||
InstanceImportTask::InstanceImportTask(QUrl sourceUrl, QWidget* parent, QMap<QString, QString> extraInfo)
|
||||
: m_sourceUrl(std::move(sourceUrl)), m_extra_info(std::move(extraInfo)), m_parent(parent)
|
||||
{}
|
||||
|
||||
bool InstanceImportTask::abort()
|
||||
{
|
||||
if (!canAbort())
|
||||
if (!canAbort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wasAborted = false;
|
||||
if (m_task)
|
||||
if (m_task) {
|
||||
wasAborted = m_task->abort();
|
||||
}
|
||||
return wasAborted;
|
||||
}
|
||||
|
||||
|
|
@ -107,16 +110,20 @@ void InstanceImportTask::downloadFromUrl()
|
|||
m_task.reset(filesNetJob);
|
||||
filesNetJob->start();
|
||||
}
|
||||
namespace {
|
||||
|
||||
QString cleanPath(QString path)
|
||||
QString cleanPath(const QString& path)
|
||||
{
|
||||
if (path == ".")
|
||||
if (path == ".") {
|
||||
return QString();
|
||||
}
|
||||
QString result = path;
|
||||
if (result.startsWith("./"))
|
||||
if (result.startsWith("./")) {
|
||||
result = result.mid(2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void InstanceImportTask::processZipPack()
|
||||
{
|
||||
|
|
@ -187,7 +194,7 @@ void InstanceImportTask::processZipPack()
|
|||
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
emitFailed(std::move(reason));
|
||||
});
|
||||
connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
|
||||
|
|
@ -196,7 +203,7 @@ void InstanceImportTask::processZipPack()
|
|||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
progressStep->status = std::move(status);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(zipTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });
|
||||
|
|
@ -251,16 +258,20 @@ void InstanceImportTask::extractFinished()
|
|||
}
|
||||
}
|
||||
|
||||
bool installIcon(QString root, QString instIconKey)
|
||||
namespace {
|
||||
|
||||
bool installIcon(const QString& root, const QString& instIconKey)
|
||||
{
|
||||
auto importIconPath = IconUtils::findBestIconIn(root, instIconKey);
|
||||
if (importIconPath.isNull() || !QFile::exists(importIconPath))
|
||||
if (importIconPath.isNull() || !QFile::exists(importIconPath)) {
|
||||
importIconPath = IconUtils::findBestIconIn(root, "icon.png");
|
||||
if (importIconPath.isNull() || !QFile::exists(importIconPath))
|
||||
}
|
||||
if (importIconPath.isNull() || !QFile::exists(importIconPath)) {
|
||||
importIconPath = IconUtils::findBestIconIn(FS::PathCombine(root, "overrides"), "icon.png");
|
||||
}
|
||||
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
|
||||
// import icon
|
||||
auto iconList = APPLICATION->icons();
|
||||
auto* iconList = APPLICATION->icons();
|
||||
if (iconList->iconFileExists(instIconKey)) {
|
||||
iconList->deleteIcon(instIconKey);
|
||||
}
|
||||
|
|
@ -269,32 +280,35 @@ bool installIcon(QString root, QString instIconKey)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void InstanceImportTask::processFlame()
|
||||
{
|
||||
shared_qobject_ptr<FlameCreationTask> inst_creation_task = nullptr;
|
||||
shared_qobject_ptr<FlameCreationTask> instCreationTask = nullptr;
|
||||
if (!m_extra_info.isEmpty()) {
|
||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||
auto pack_id = pack_id_it.value();
|
||||
auto packIdIt = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(packIdIt != m_extra_info.constEnd());
|
||||
const auto& packId = packIdIt.value();
|
||||
|
||||
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||
Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
|
||||
auto pack_version_id = pack_version_id_it.value();
|
||||
auto packVersionIdIt = m_extra_info.constFind("pack_version_id");
|
||||
Q_ASSERT(packVersionIdIt != m_extra_info.constEnd());
|
||||
const auto& packVersionId = packVersionIdIt.value();
|
||||
|
||||
QString original_instance_id;
|
||||
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||
if (original_instance_id_it != m_extra_info.constEnd())
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
QString originalInstanceId;
|
||||
auto originalInstanceIdIt = m_extra_info.constFind("original_instance_id");
|
||||
if (originalInstanceIdIt != m_extra_info.constEnd()) {
|
||||
originalInstanceId = originalInstanceIdIt.value();
|
||||
}
|
||||
|
||||
inst_creation_task =
|
||||
makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
instCreationTask =
|
||||
makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, packId, packVersionId, originalInstanceId);
|
||||
} else {
|
||||
// FIXME: Find a way to get IDs in directly imported ZIPs
|
||||
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
|
||||
instCreationTask = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
|
||||
}
|
||||
|
||||
inst_creation_task->setName(*this);
|
||||
instCreationTask->setName(modifiedName());
|
||||
instCreationTask->setOriginalName(originalName(), version());
|
||||
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
||||
if (m_instIcon == "default") {
|
||||
auto iconKey = QString("Flame_%1_Icon").arg(name());
|
||||
|
|
@ -303,30 +317,30 @@ void InstanceImportTask::processFlame()
|
|||
m_instIcon = iconKey;
|
||||
}
|
||||
}
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
instCreationTask->setIcon(m_instIcon);
|
||||
instCreationTask->setGroup(m_instGroup);
|
||||
instCreationTask->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
auto weak = inst_creation_task.toWeakRef();
|
||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
|
||||
auto weak = instCreationTask.toWeakRef();
|
||||
connect(instCreationTask.get(), &Task::succeeded, this, [this, weak] {
|
||||
if (auto sp = weak.lock()) {
|
||||
setOverride(sp->shouldOverride(), sp->originalInstanceID());
|
||||
}
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
connect(instCreationTask.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(instCreationTask.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(instCreationTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(instCreationTask.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(instCreationTask.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
|
||||
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText);
|
||||
connect(instCreationTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(instCreationTask.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
connect(instCreationTask.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText);
|
||||
|
||||
connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });
|
||||
connect(instCreationTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });
|
||||
|
||||
m_task.reset(inst_creation_task);
|
||||
m_task.reset(instCreationTask);
|
||||
setAbortable(true);
|
||||
m_task->start();
|
||||
}
|
||||
|
|
@ -365,36 +379,39 @@ void InstanceImportTask::processMultiMC()
|
|||
|
||||
void InstanceImportTask::processModrinth()
|
||||
{
|
||||
shared_qobject_ptr<ModrinthCreationTask> inst_creation_task = nullptr;
|
||||
shared_qobject_ptr<ModrinthCreationTask> instCreationTask = nullptr;
|
||||
if (!m_extra_info.isEmpty()) {
|
||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||
auto pack_id = pack_id_it.value();
|
||||
auto packIdIt = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(packIdIt != m_extra_info.constEnd());
|
||||
const auto& packId = packIdIt.value();
|
||||
|
||||
QString pack_version_id;
|
||||
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||
if (pack_version_id_it != m_extra_info.constEnd())
|
||||
pack_version_id = pack_version_id_it.value();
|
||||
QString packVersionId;
|
||||
auto packVersionIdIt = m_extra_info.constFind("pack_version_id");
|
||||
if (packVersionIdIt != m_extra_info.constEnd()) {
|
||||
packVersionId = packVersionIdIt.value();
|
||||
}
|
||||
|
||||
QString original_instance_id;
|
||||
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||
if (original_instance_id_it != m_extra_info.constEnd())
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
QString originalInstanceId;
|
||||
auto originalInstanceIdIt = m_extra_info.constFind("original_instance_id");
|
||||
if (originalInstanceIdIt != m_extra_info.constEnd()) {
|
||||
originalInstanceId = originalInstanceIdIt.value();
|
||||
}
|
||||
|
||||
inst_creation_task =
|
||||
makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
instCreationTask =
|
||||
makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, packId, packVersionId, originalInstanceId);
|
||||
} else {
|
||||
QString pack_id;
|
||||
QString packId;
|
||||
if (!m_sourceUrl.isEmpty()) {
|
||||
static const QRegularExpression s_regex(R"(data\/([^\/]*)\/versions)");
|
||||
pack_id = s_regex.match(m_sourceUrl.toString()).captured(1);
|
||||
packId = s_regex.match(m_sourceUrl.toString()).captured(1);
|
||||
}
|
||||
|
||||
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||
inst_creation_task = makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||
instCreationTask = makeShared<ModrinthCreationTask>(m_stagingPath, m_globalSettings, m_parent, packId);
|
||||
}
|
||||
|
||||
inst_creation_task->setName(*this);
|
||||
instCreationTask->setName(modifiedName());
|
||||
instCreationTask->setOriginalName(originalName(), version());
|
||||
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
||||
if (m_instIcon == "default") {
|
||||
auto iconKey = QString("Modrinth_%1_Icon").arg(name());
|
||||
|
|
@ -403,30 +420,30 @@ void InstanceImportTask::processModrinth()
|
|||
m_instIcon = iconKey;
|
||||
}
|
||||
}
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
instCreationTask->setIcon(m_instIcon);
|
||||
instCreationTask->setGroup(m_instGroup);
|
||||
instCreationTask->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
auto weak = inst_creation_task.toWeakRef();
|
||||
connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] {
|
||||
auto weak = instCreationTask.toWeakRef();
|
||||
connect(instCreationTask.get(), &Task::succeeded, this, [this, weak] {
|
||||
if (auto sp = weak.lock()) {
|
||||
setOverride(sp->shouldOverride(), sp->originalInstanceID());
|
||||
}
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
connect(instCreationTask.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(instCreationTask.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(instCreationTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(instCreationTask.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(instCreationTask.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
|
||||
connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText);
|
||||
connect(instCreationTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted);
|
||||
connect(instCreationTask.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||
connect(instCreationTask.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText);
|
||||
|
||||
connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });
|
||||
connect(instCreationTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); });
|
||||
|
||||
m_task.reset(inst_creation_task);
|
||||
m_task.reset(instCreationTask);
|
||||
setAbortable(true);
|
||||
m_task->start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,19 +43,20 @@
|
|||
class InstanceImportTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||
virtual ~InstanceImportTask() = default;
|
||||
explicit InstanceImportTask(QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString> extraInfo = {});
|
||||
~InstanceImportTask() override = default;
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
void executeTask() override;
|
||||
|
||||
private:
|
||||
void processMultiMC();
|
||||
void processTechnic();
|
||||
void processFlame();
|
||||
void processModrinth();
|
||||
void downloadFromUrl();
|
||||
|
||||
private slots:
|
||||
void processZipPack();
|
||||
|
|
@ -65,7 +66,7 @@ class InstanceImportTask : public InstanceTask {
|
|||
QUrl m_sourceUrl;
|
||||
QString m_archivePath;
|
||||
Task::Ptr m_task;
|
||||
enum class ModpackType {
|
||||
enum class ModpackType : std::uint8_t {
|
||||
Unknown,
|
||||
MultiMC,
|
||||
Technic,
|
||||
|
|
@ -79,5 +80,4 @@ class InstanceImportTask : public InstanceTask {
|
|||
|
||||
// FIXME: nuke
|
||||
QWidget* m_parent;
|
||||
void downloadFromUrl();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <QStack>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
#include <algorithm>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "ExponentialSeries.h"
|
||||
|
|
@ -62,7 +63,7 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||
const static int g_GROUP_FILE_FORMAT_VERSION = 1;
|
||||
|
||||
InstanceList::InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent)
|
||||
: QAbstractListModel(parent), m_globalSettings(settings)
|
||||
|
|
@ -82,8 +83,6 @@ InstanceList::InstanceList(SettingsObject* settings, const QString& instDir, QOb
|
|||
m_watcher->addPath(m_instDir);
|
||||
}
|
||||
|
||||
InstanceList::~InstanceList() {}
|
||||
|
||||
Qt::DropActions InstanceList::supportedDragActions() const
|
||||
{
|
||||
return Qt::MoveAction;
|
||||
|
|
@ -100,10 +99,7 @@ bool InstanceList::canDropMimeData(const QMimeData* data,
|
|||
[[maybe_unused]] int column,
|
||||
[[maybe_unused]] const QModelIndex& parent) const
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (data != nullptr) && data->hasFormat("application/x-instanceid");
|
||||
}
|
||||
|
||||
bool InstanceList::dropMimeData(const QMimeData* data,
|
||||
|
|
@ -112,10 +108,7 @@ bool InstanceList::dropMimeData(const QMimeData* data,
|
|||
[[maybe_unused]] int column,
|
||||
[[maybe_unused]] const QModelIndex& parent)
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (data != nullptr) && data->hasFormat("application/x-instanceid");
|
||||
}
|
||||
|
||||
QStringList InstanceList::mimeTypes() const
|
||||
|
|
@ -127,7 +120,7 @@ QStringList InstanceList::mimeTypes() const
|
|||
|
||||
QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
|
||||
{
|
||||
auto mimeData = QAbstractListModel::mimeData(indexes);
|
||||
auto* mimeData = QAbstractListModel::mimeData(indexes);
|
||||
if (indexes.size() == 1) {
|
||||
auto instanceId = data(indexes[0], InstanceIDRole).toString();
|
||||
mimeData->setData("application/x-instanceid", instanceId.toUtf8());
|
||||
|
|
@ -138,9 +131,10 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
|
|||
QStringList InstanceList::getLinkedInstancesById(const QString& id) const
|
||||
{
|
||||
QStringList linkedInstances;
|
||||
for (auto& inst : m_instances) {
|
||||
if (inst->isLinkedToInstanceId(id))
|
||||
for (const auto& inst : m_instances) {
|
||||
if (inst->isLinkedToInstanceId(id)) {
|
||||
linkedInstances.append(inst->id());
|
||||
}
|
||||
}
|
||||
return linkedInstances;
|
||||
}
|
||||
|
|
@ -154,8 +148,9 @@ int InstanceList::rowCount(const QModelIndex& parent) const
|
|||
QModelIndex InstanceList::index(int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
if (row < 0 || row >= count())
|
||||
return QModelIndex();
|
||||
if (row < 0 || row >= count()) {
|
||||
return {};
|
||||
}
|
||||
return createIndex(row, column, m_instances.at(row).get());
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +159,7 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const
|
|||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||
auto* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||
switch (role) {
|
||||
case InstancePointerRole: {
|
||||
QVariant v = QVariant::fromValue((void*)pdata);
|
||||
|
|
@ -204,7 +199,7 @@ bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int
|
|||
if (role != Qt::EditRole) {
|
||||
return false;
|
||||
}
|
||||
BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||
auto* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||
auto newName = value.toString();
|
||||
if (pdata->name() == newName) {
|
||||
return true;
|
||||
|
|
@ -224,23 +219,24 @@ Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const
|
|||
|
||||
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
auto* inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
return GroupId();
|
||||
return {};
|
||||
}
|
||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||
if (iter != m_instanceGroupIndex.end()) {
|
||||
return *iter;
|
||||
}
|
||||
return GroupId();
|
||||
return {};
|
||||
}
|
||||
|
||||
void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name)
|
||||
{
|
||||
if (name.isEmpty() && !name.isNull())
|
||||
if (name.isEmpty() && !name.isNull()) {
|
||||
name = QString();
|
||||
}
|
||||
|
||||
auto inst = getInstanceById(id);
|
||||
auto* inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
qDebug() << "Attempt to set a null instance's group";
|
||||
return;
|
||||
|
|
@ -287,19 +283,22 @@ void InstanceList::deleteGroup(const GroupId& name)
|
|||
qDebug() << "Remove" << instID << "from group" << name;
|
||||
removed = true;
|
||||
auto idx = getInstIndex(instance.get());
|
||||
if (idx >= 0)
|
||||
if (idx >= 0) {
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
if (removed) {
|
||||
saveGroupList();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceList::renameGroup(const QString& src, const QString& dst)
|
||||
{
|
||||
m_groupNameCache.remove(src);
|
||||
if (m_collapsedGroups.remove(src))
|
||||
if (m_collapsedGroups.remove(src)) {
|
||||
m_collapsedGroups.insert(dst);
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
qDebug() << "Rename group" << src << "to" << dst;
|
||||
|
|
@ -312,12 +311,14 @@ void InstanceList::renameGroup(const QString& src, const QString& dst)
|
|||
qDebug() << "Set" << instID << "group to" << dst;
|
||||
modified = true;
|
||||
auto idx = getInstIndex(instance.get());
|
||||
if (idx >= 0)
|
||||
if (idx >= 0) {
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modified)
|
||||
if (modified) {
|
||||
saveGroupList();
|
||||
}
|
||||
}
|
||||
|
||||
bool InstanceList::isGroupCollapsed(const QString& group)
|
||||
|
|
@ -327,7 +328,7 @@ bool InstanceList::isGroupCollapsed(const QString& group)
|
|||
|
||||
bool InstanceList::trashInstance(const InstanceId& id)
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
auto* inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
qWarning() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?).";
|
||||
return false;
|
||||
|
|
@ -338,7 +339,7 @@ bool InstanceList::trashInstance(const InstanceId& id)
|
|||
qDebug() << "Will trash instance" << id;
|
||||
QString trashedLoc;
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
if (m_instanceGroupIndex.remove(id) != 0) {
|
||||
decreaseGroupCount(cachedGroupId);
|
||||
saveGroupList();
|
||||
}
|
||||
|
|
@ -422,7 +423,7 @@ bool InstanceList::undoTrashInstance()
|
|||
|
||||
void InstanceList::deleteInstance(const InstanceId& id)
|
||||
{
|
||||
auto inst = getInstanceById(id);
|
||||
auto* inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
qWarning() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
|
||||
return;
|
||||
|
|
@ -430,7 +431,7 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
|||
|
||||
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
if (m_instanceGroupIndex.remove(id) != 0) {
|
||||
decreaseGroupCount(cachedGroupId);
|
||||
saveGroupList();
|
||||
}
|
||||
|
|
@ -452,11 +453,12 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
|||
}
|
||||
}
|
||||
|
||||
static QMap<InstanceId, InstanceLocator> getIdMapping(const std::vector<std::unique_ptr<BaseInstance>>& list)
|
||||
namespace {
|
||||
QMap<InstanceId, InstanceLocator> getIdMapping(const std::vector<std::unique_ptr<BaseInstance>>& list)
|
||||
{
|
||||
QMap<InstanceId, InstanceLocator> out;
|
||||
int i = 0;
|
||||
for (auto& item : list) {
|
||||
for (const auto& item : list) {
|
||||
auto id = item->id();
|
||||
if (out.contains(id)) {
|
||||
qWarning() << "Duplicate ID" << id << "in instance list";
|
||||
|
|
@ -466,6 +468,7 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const std::vector<std::uni
|
|||
}
|
||||
return out;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
QList<InstanceId> InstanceList::discoverInstances()
|
||||
{
|
||||
|
|
@ -475,8 +478,9 @@ QList<InstanceId> InstanceList::discoverInstances()
|
|||
while (iter.hasNext()) {
|
||||
QString subDir = iter.next();
|
||||
QFileInfo dirInfo(subDir);
|
||||
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
|
||||
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists()) {
|
||||
continue;
|
||||
}
|
||||
// if it is a symlink, ignore it if it goes to the instance folder
|
||||
if (dirInfo.isSymLink()) {
|
||||
QFileInfo targetInfo(dirInfo.symLinkTarget());
|
||||
|
|
@ -490,7 +494,7 @@ QList<InstanceId> InstanceList::discoverInstances()
|
|||
out.append(id);
|
||||
qInfo() << "Found instance ID" << id;
|
||||
}
|
||||
instanceSet = QSet<QString>(out.begin(), out.end());
|
||||
m_instanceSet = QSet<QString>(out.begin(), out.end());
|
||||
m_instancesProbed = true;
|
||||
return out;
|
||||
}
|
||||
|
|
@ -518,38 +522,38 @@ InstanceList::InstListError InstanceList::loadList()
|
|||
// get the list of removed instances and sort it by their original index, from last to first
|
||||
auto deadList = existingIds.values();
|
||||
auto orderSortPredicate = [](const InstanceLocator& a, const InstanceLocator& b) -> bool { return a.second > b.second; };
|
||||
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
|
||||
std::ranges::sort(deadList, orderSortPredicate);
|
||||
// remove the contiguous ranges of rows
|
||||
int front_bookmark = -1;
|
||||
int back_bookmark = -1;
|
||||
int frontBookmark = -1;
|
||||
int backBookmark = -1;
|
||||
int currentItem = -1;
|
||||
auto removeNow = [this, &front_bookmark, &back_bookmark, ¤tItem]() {
|
||||
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
||||
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
||||
auto removeNow = [this, &frontBookmark, &backBookmark, ¤tItem]() {
|
||||
beginRemoveRows(QModelIndex(), frontBookmark, backBookmark);
|
||||
m_instances.erase(m_instances.begin() + frontBookmark, m_instances.begin() + backBookmark + 1);
|
||||
endRemoveRows();
|
||||
front_bookmark = -1;
|
||||
back_bookmark = currentItem;
|
||||
frontBookmark = -1;
|
||||
backBookmark = currentItem;
|
||||
};
|
||||
for (auto& removedItem : deadList) {
|
||||
auto instPtr = removedItem.first;
|
||||
auto* instPtr = removedItem.first;
|
||||
instPtr->invalidate();
|
||||
currentItem = removedItem.second;
|
||||
if (back_bookmark == -1) {
|
||||
if (backBookmark == -1) {
|
||||
// no bookmark yet
|
||||
back_bookmark = currentItem;
|
||||
} else if (currentItem == front_bookmark - 1) {
|
||||
backBookmark = currentItem;
|
||||
} else if (currentItem == frontBookmark - 1) {
|
||||
// part of contiguous sequence, continue
|
||||
} else {
|
||||
// seam between previous and current item
|
||||
removeNow();
|
||||
}
|
||||
front_bookmark = currentItem;
|
||||
frontBookmark = currentItem;
|
||||
}
|
||||
if (back_bookmark != -1) {
|
||||
if (backBookmark != -1) {
|
||||
removeNow();
|
||||
}
|
||||
}
|
||||
if (newList.size()) {
|
||||
if (!newList.empty()) {
|
||||
add(newList);
|
||||
}
|
||||
m_dirty = false;
|
||||
|
|
@ -559,9 +563,9 @@ InstanceList::InstListError InstanceList::loadList()
|
|||
|
||||
void InstanceList::updateTotalPlayTime()
|
||||
{
|
||||
totalPlayTime = 0;
|
||||
m_totalPlayTime = 0;
|
||||
for (const auto& itr : m_instances) {
|
||||
totalPlayTime += itr->totalTimePlayed();
|
||||
m_totalPlayTime += static_cast<int>(itr->totalTimePlayed());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -607,11 +611,12 @@ void InstanceList::providerUpdated()
|
|||
}
|
||||
}
|
||||
|
||||
BaseInstance* InstanceList::getInstanceById(QString instId) const
|
||||
BaseInstance* InstanceList::getInstanceById(const QString& instId) const
|
||||
{
|
||||
if (instId.isEmpty())
|
||||
if (instId.isEmpty()) {
|
||||
return nullptr;
|
||||
for (auto& inst : m_instances) {
|
||||
}
|
||||
for (const auto& inst : m_instances) {
|
||||
if (inst->id() == instId) {
|
||||
return inst.get();
|
||||
}
|
||||
|
|
@ -619,14 +624,16 @@ BaseInstance* InstanceList::getInstanceById(QString instId) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
BaseInstance* InstanceList::getInstanceByManagedName(const QString& managed_name) const
|
||||
BaseInstance* InstanceList::getInstanceByManagedName(const QString& managedName) const
|
||||
{
|
||||
if (managed_name.isEmpty())
|
||||
if (managedName.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (auto& instance : m_instances) {
|
||||
if (instance->getManagedPackName() == managed_name)
|
||||
for (const auto& instance : m_instances) {
|
||||
if (instance->getManagedPackName() == managedName) {
|
||||
return instance.get();
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
@ -669,11 +676,11 @@ std::unique_ptr<BaseInstance> InstanceList::loadInstance(const InstanceId& id)
|
|||
|
||||
instanceSettings->registerSetting("InstanceType", "");
|
||||
|
||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||
const QString instType = instanceSettings->get("InstanceType").toString();
|
||||
|
||||
// NOTE: Some launcher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a
|
||||
// OneSix instance
|
||||
if (inst_type == "OneSix" || inst_type.isEmpty()) {
|
||||
if (instType == "OneSix" || instType.isEmpty()) {
|
||||
inst.reset(new MinecraftInstance(m_globalSettings, std::move(instanceSettings), instanceRoot));
|
||||
} else {
|
||||
inst.reset(new NullInstance(m_globalSettings, std::move(instanceSettings), instanceRoot));
|
||||
|
|
@ -681,24 +688,27 @@ std::unique_ptr<BaseInstance> InstanceList::loadInstance(const InstanceId& id)
|
|||
qDebug() << "Loaded instance" << inst->name() << "from" << inst->instanceRoot();
|
||||
|
||||
auto shortcut = inst->shortcuts();
|
||||
if (!shortcut.isEmpty())
|
||||
if (!shortcut.isEmpty()) {
|
||||
qDebug() << "Loaded" << shortcut.size() << "shortcut(s) for instance" << inst->name();
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void InstanceList::increaseGroupCount(const QString& group)
|
||||
{
|
||||
if (group.isEmpty())
|
||||
if (group.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
++m_groupNameCache[group];
|
||||
}
|
||||
|
||||
void InstanceList::decreaseGroupCount(const QString& group)
|
||||
{
|
||||
if (group.isEmpty())
|
||||
if (group.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (--m_groupNameCache[group] < 1) {
|
||||
m_groupNameCache.remove(group);
|
||||
|
|
@ -718,15 +728,16 @@ void InstanceList::saveGroupList()
|
|||
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
||||
const QString& id = iter.key();
|
||||
QString group = iter.value();
|
||||
if (group.isEmpty())
|
||||
const QString& group = iter.value();
|
||||
if (group.isEmpty()) {
|
||||
continue;
|
||||
if (!instanceSet.contains(id)) {
|
||||
}
|
||||
if (!m_instanceSet.contains(id)) {
|
||||
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!reverseGroupMap.count(group)) {
|
||||
if (!reverseGroupMap.contains(group)) {
|
||||
QSet<QString> set;
|
||||
set.insert(id);
|
||||
reverseGroupMap[group] = set;
|
||||
|
|
@ -740,11 +751,11 @@ void InstanceList::saveGroupList()
|
|||
QJsonObject groupsArr;
|
||||
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) {
|
||||
auto list = iter.value();
|
||||
auto name = iter.key();
|
||||
const auto& name = iter.key();
|
||||
QJsonObject groupObj;
|
||||
QJsonArray instanceArr;
|
||||
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
|
||||
for (auto item : list) {
|
||||
for (const auto& item : list) {
|
||||
instanceArr.append(QJsonValue(item));
|
||||
}
|
||||
groupObj.insert("instances", instanceArr);
|
||||
|
|
@ -773,8 +784,9 @@ void InstanceList::loadGroupList()
|
|||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
|
||||
// if there's no group file, fail
|
||||
if (!QFileInfo(groupFileName).exists())
|
||||
if (!QFileInfo(groupFileName).exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray jsonData;
|
||||
try {
|
||||
|
|
@ -804,8 +816,9 @@ void InstanceList::loadGroupList()
|
|||
QJsonObject rootObj = jsonDoc.object();
|
||||
|
||||
// Make sure the format version matches, otherwise fail.
|
||||
if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION)
|
||||
if (rootObj.value("formatVersion").toVariant().toInt() != g_GROUP_FILE_FORMAT_VERSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the groups. if it's not an object, fail
|
||||
if (!rootObj.value("groups").isObject()) {
|
||||
|
|
@ -841,8 +854,9 @@ void InstanceList::loadGroupList()
|
|||
}
|
||||
|
||||
auto hidden = groupObj.value("hidden").toBool(false);
|
||||
if (hidden)
|
||||
if (hidden) {
|
||||
m_collapsedGroups.insert(groupName);
|
||||
}
|
||||
|
||||
// Iterate through the list of instances in the group.
|
||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||
|
|
@ -872,7 +886,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
|
|||
emit instancesChanged();
|
||||
}
|
||||
|
||||
void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value)
|
||||
void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, const QVariant& value)
|
||||
{
|
||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
||||
if (newInstDir != m_instDir) {
|
||||
|
|
@ -899,13 +913,16 @@ void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
|
|||
saveGroupList();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class InstanceStaging : public Task {
|
||||
Q_OBJECT
|
||||
const unsigned minBackoff = 1;
|
||||
const unsigned maxBackoff = 16;
|
||||
|
||||
public:
|
||||
InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObject* settings) : m_parent(parent), backoff(minBackoff, maxBackoff)
|
||||
InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObject* settings)
|
||||
: m_parent(parent), m_backoff(minBackoff, maxBackoff)
|
||||
{
|
||||
m_stagingPath = parent->getStagedInstancePath();
|
||||
|
||||
|
|
@ -954,8 +971,8 @@ class InstanceStaging : public Task {
|
|||
private slots:
|
||||
void childSucceeded()
|
||||
{
|
||||
unsigned sleepTime = backoff();
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group(), *m_child)) {
|
||||
const unsigned sleepTime = m_backoff();
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group())) {
|
||||
m_backoffTimer.stop();
|
||||
emitSucceeded();
|
||||
return;
|
||||
|
|
@ -972,14 +989,14 @@ class InstanceStaging : public Task {
|
|||
void childFailed(const QString& reason)
|
||||
{
|
||||
m_backoffTimer.stop();
|
||||
m_parent->destroyStagingPath(m_stagingPath);
|
||||
FS::deletePath(m_stagingPath);
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void childAborted()
|
||||
{
|
||||
m_backoffTimer.stop();
|
||||
m_parent->destroyStagingPath(m_stagingPath);
|
||||
FS::deletePath(m_stagingPath);
|
||||
emitAborted();
|
||||
}
|
||||
|
||||
|
|
@ -990,11 +1007,12 @@ class InstanceStaging : public Task {
|
|||
* Basically, it starts messing things up while the launcher is extracting/creating instances
|
||||
* and causes that horrible failure that is NTFS to lock files in place because they are open.
|
||||
*/
|
||||
ExponentialSeries backoff;
|
||||
ExponentialSeries m_backoff;
|
||||
QString m_stagingPath;
|
||||
std::unique_ptr<InstanceTask> m_child;
|
||||
QTimer m_backoffTimer;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Task* InstanceList::wrapInstanceTask(InstanceTask* task)
|
||||
{
|
||||
|
|
@ -1009,37 +1027,37 @@ QString InstanceList::getStagedInstancePath()
|
|||
int tries = 0;
|
||||
|
||||
do {
|
||||
if (++tries > 256)
|
||||
if (++tries > 256) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6);
|
||||
result = FS::PathCombine(tempRoot, key);
|
||||
} while (QFileInfo::exists(result));
|
||||
|
||||
if (!QDir::current().mkpath(result))
|
||||
if (!QDir::current().mkpath(result)) {
|
||||
return {};
|
||||
}
|
||||
#ifdef Q_OS_WIN32
|
||||
SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InstanceList::commitStagedInstance(const QString& path,
|
||||
const InstanceName& instanceName,
|
||||
QString groupName,
|
||||
const InstanceTask& commiting)
|
||||
bool InstanceList::commitStagedInstance(const QString& path, const InstanceTask& instanceTask, QString groupName)
|
||||
{
|
||||
if (groupName.isEmpty() && !groupName.isNull())
|
||||
if (groupName.isEmpty() && !groupName.isNull()) {
|
||||
groupName = QString();
|
||||
}
|
||||
|
||||
QString instID;
|
||||
|
||||
auto should_override = commiting.shouldOverride();
|
||||
auto shouldOverride = instanceTask.shouldOverride();
|
||||
|
||||
if (should_override) {
|
||||
instID = commiting.originalInstanceID();
|
||||
if (shouldOverride) {
|
||||
instID = instanceTask.originalInstanceID();
|
||||
} else {
|
||||
instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir);
|
||||
instID = FS::DirNameFromString(instanceTask.modifiedName(), m_instDir);
|
||||
}
|
||||
|
||||
Q_ASSERT(!instID.isEmpty());
|
||||
|
|
@ -1048,7 +1066,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
|||
WatchLock lock(m_watcher, m_instDir);
|
||||
QString destination = FS::PathCombine(m_instDir, instID);
|
||||
|
||||
if (should_override) {
|
||||
if (shouldOverride) {
|
||||
if (!FS::overrideFolder(destination, path)) {
|
||||
qWarning() << "Failed to override" << path << "to" << destination;
|
||||
return false;
|
||||
|
|
@ -1063,7 +1081,7 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
|||
increaseGroupCount(groupName);
|
||||
}
|
||||
|
||||
instanceSet.insert(instID);
|
||||
m_instanceSet.insert(instID);
|
||||
|
||||
emit instancesChanged();
|
||||
emit instanceSelectRequest(instID);
|
||||
|
|
@ -1073,15 +1091,10 @@ bool InstanceList::commitStagedInstance(const QString& path,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InstanceList::destroyStagingPath(const QString& keyPath)
|
||||
{
|
||||
return FS::deletePath(keyPath);
|
||||
}
|
||||
|
||||
int InstanceList::getTotalPlayTime()
|
||||
{
|
||||
updateTotalPlayTime();
|
||||
return totalPlayTime;
|
||||
return m_totalPlayTime;
|
||||
}
|
||||
|
||||
#include "InstanceList.moc"
|
||||
|
|
|
|||
|
|
@ -41,20 +41,20 @@
|
|||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QStack>
|
||||
#include <cstdint>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
class QFileSystemWatcher;
|
||||
class InstanceTask;
|
||||
struct InstanceName;
|
||||
|
||||
using InstanceId = QString;
|
||||
using GroupId = QString;
|
||||
using InstanceLocator = std::pair<BaseInstance*, int>;
|
||||
|
||||
enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir };
|
||||
enum class InstCreateError : std::uint8_t { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir };
|
||||
|
||||
enum class GroupsState { NotLoaded, Steady, Dirty };
|
||||
enum class GroupsState : std::uint8_t { NotLoaded, Steady, Dirty };
|
||||
|
||||
struct TrashShortcutItem {
|
||||
ShortcutData data;
|
||||
|
|
@ -73,8 +73,8 @@ class InstanceList : public QAbstractListModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent = 0);
|
||||
virtual ~InstanceList();
|
||||
explicit InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent = nullptr);
|
||||
~InstanceList() override = default;
|
||||
|
||||
public:
|
||||
QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
|
@ -94,7 +94,7 @@ class InstanceList : public QAbstractListModel {
|
|||
* NoError Indicates that no error occurred.
|
||||
* UnknownError indicates that an unspecified error occurred.
|
||||
*/
|
||||
enum InstListError { NoError = 0, UnknownError };
|
||||
enum InstListError : std::uint8_t { NoError = 0, UnknownError };
|
||||
|
||||
BaseInstance* at(int i) const { return m_instances.at(i).get(); }
|
||||
|
||||
|
|
@ -104,9 +104,9 @@ class InstanceList : public QAbstractListModel {
|
|||
void saveNow();
|
||||
|
||||
/* O(n) */
|
||||
BaseInstance* getInstanceById(QString id) const;
|
||||
BaseInstance* getInstanceById(const QString& id) const;
|
||||
/* O(n) */
|
||||
BaseInstance* getInstanceByManagedName(const QString& managed_name) const;
|
||||
BaseInstance* getInstanceByManagedName(const QString& managedName) const;
|
||||
QModelIndex getInstanceIndexById(const QString& id) const;
|
||||
QStringList getGroups();
|
||||
bool isGroupCollapsed(const QString& groupName);
|
||||
|
|
@ -136,13 +136,7 @@ class InstanceList : public QAbstractListModel {
|
|||
* should_override is used when another similar instance already exists, and we want to override it
|
||||
* - for instance, when updating it.
|
||||
*/
|
||||
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&);
|
||||
|
||||
/**
|
||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||
* Used by instance manipulation tasks.
|
||||
*/
|
||||
bool destroyStagingPath(const QString& keyPath);
|
||||
bool commitStagedInstance(const QString& keyPath, const InstanceTask& instanceTask, QString groupName);
|
||||
|
||||
int getTotalPlayTime();
|
||||
|
||||
|
|
@ -166,7 +160,7 @@ class InstanceList : public QAbstractListModel {
|
|||
void groupsChanged(QSet<QString> groups);
|
||||
|
||||
public slots:
|
||||
void on_InstFolderChanged(const Setting& setting, QVariant value);
|
||||
void on_InstFolderChanged(const Setting& setting, const QVariant& value);
|
||||
void on_GroupStateChanged(const QString& group, bool collapsed);
|
||||
|
||||
private slots:
|
||||
|
|
@ -190,7 +184,7 @@ class InstanceList : public QAbstractListModel {
|
|||
|
||||
private:
|
||||
int m_watchLevel = 0;
|
||||
int totalPlayTime = 0;
|
||||
int m_totalPlayTime = 0;
|
||||
bool m_dirty = false;
|
||||
std::vector<std::unique_ptr<BaseInstance>> m_instances;
|
||||
// id -> refs
|
||||
|
|
@ -202,7 +196,7 @@ class InstanceList : public QAbstractListModel {
|
|||
// FIXME: this is so inefficient that looking at it is almost painful.
|
||||
QSet<QString> m_collapsedGroups;
|
||||
QMap<InstanceId, GroupId> m_instanceGroupIndex;
|
||||
QSet<InstanceId> instanceSet;
|
||||
QSet<InstanceId> m_instanceSet;
|
||||
bool m_groupsLoaded = false;
|
||||
bool m_instancesProbed = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,94 +2,182 @@
|
|||
#include <QDir>
|
||||
|
||||
#include "Application.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/MinecraftLoadAndCheck.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
||||
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& oldName, const QString& newName)
|
||||
{
|
||||
auto dialog =
|
||||
auto* dialog =
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Change instance name"),
|
||||
QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n"
|
||||
"Old name: %1\n"
|
||||
"New name: %2")
|
||||
.arg(old_name, new_name),
|
||||
.arg(oldName, newName),
|
||||
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
|
||||
auto result = dialog->exec();
|
||||
|
||||
if (result == QMessageBox::Yes)
|
||||
if (result == QMessageBox::Yes) {
|
||||
return InstanceNameChange::ShouldChange;
|
||||
}
|
||||
return InstanceNameChange::ShouldKeep;
|
||||
}
|
||||
|
||||
ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name)
|
||||
ShouldUpdate askIfShouldUpdate(QWidget* parent, const QString& originalVersionName)
|
||||
{
|
||||
if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool())
|
||||
if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool()) {
|
||||
return ShouldUpdate::SkipUpdating;
|
||||
}
|
||||
|
||||
auto info = CustomMessageBox::selectable(
|
||||
auto* info = CustomMessageBox::selectable(
|
||||
parent, QObject::tr("Similar modpack was found!"),
|
||||
QObject::tr(
|
||||
"One or more of your instances are from this same modpack%1. Do you want to create a "
|
||||
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
||||
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
||||
.arg(original_version_name),
|
||||
.arg(originalVersionName),
|
||||
QMessageBox::Information, QMessageBox::Cancel);
|
||||
QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole);
|
||||
QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole);
|
||||
|
||||
info->exec();
|
||||
|
||||
if (info->clickedButton() == update)
|
||||
if (info->clickedButton() == update) {
|
||||
return ShouldUpdate::Update;
|
||||
if (info->clickedButton() == skip)
|
||||
}
|
||||
if (info->clickedButton() == skip) {
|
||||
return ShouldUpdate::SkipUpdating;
|
||||
}
|
||||
return ShouldUpdate::Cancel;
|
||||
}
|
||||
|
||||
QString InstanceName::name() const
|
||||
QString InstanceTask::name() const
|
||||
{
|
||||
if (!m_modified_name.isEmpty())
|
||||
if (!m_modifiedName.isEmpty()) {
|
||||
return modifiedName();
|
||||
if (!m_original_version.isEmpty())
|
||||
return QString("%1 %2").arg(m_original_name, m_original_version);
|
||||
}
|
||||
if (!m_originalVersion.isEmpty()) {
|
||||
return QString("%1 %2").arg(m_originalName, m_originalVersion);
|
||||
}
|
||||
|
||||
return m_original_name;
|
||||
return m_originalName;
|
||||
}
|
||||
|
||||
QString InstanceName::originalName() const
|
||||
QString InstanceTask::originalName() const
|
||||
{
|
||||
return m_original_name;
|
||||
return m_originalName;
|
||||
}
|
||||
|
||||
QString InstanceName::modifiedName() const
|
||||
QString InstanceTask::modifiedName() const
|
||||
{
|
||||
if (!m_modified_name.isEmpty())
|
||||
return m_modified_name;
|
||||
return m_original_name;
|
||||
if (!m_modifiedName.isEmpty()) {
|
||||
return m_modifiedName;
|
||||
}
|
||||
return m_originalName;
|
||||
}
|
||||
|
||||
QString InstanceName::version() const
|
||||
QString InstanceTask::version() const
|
||||
{
|
||||
return m_original_version;
|
||||
return m_originalVersion;
|
||||
}
|
||||
|
||||
void InstanceName::setName(InstanceName& other)
|
||||
void InstanceTask::setOriginalName(const QString& name, const QString& version)
|
||||
{
|
||||
m_original_name = other.m_original_name;
|
||||
m_original_version = other.m_original_version;
|
||||
m_modified_name = other.m_modified_name;
|
||||
m_originalName = name;
|
||||
m_originalVersion = version;
|
||||
}
|
||||
void InstanceTask::setOverride(bool override, const QString& instanceIdToOverride)
|
||||
{
|
||||
m_overrideExisting = override;
|
||||
if (!instanceIdToOverride.isEmpty()) {
|
||||
m_originalInstanceId = instanceIdToOverride;
|
||||
}
|
||||
}
|
||||
|
||||
InstanceTask::InstanceTask() : Task(), InstanceName() {}
|
||||
|
||||
ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent)
|
||||
{
|
||||
auto dialog = CustomMessageBox::selectable(parent, QObject::tr("Delete Existing Save Files"),
|
||||
QObject::tr("An earlier version of this mod pack installed save files.\n"
|
||||
"Would you like to remove those existing saves as part of this update?"),
|
||||
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
|
||||
auto* dialog = CustomMessageBox::selectable(parent, QObject::tr("Delete Existing Save Files"),
|
||||
QObject::tr("An earlier version of this mod pack installed save files.\n"
|
||||
"Would you like to remove those existing saves as part of this update?"),
|
||||
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
|
||||
auto result = dialog->exec();
|
||||
return result == QMessageBox::Yes ? ShouldDeleteSaves::Yes : ShouldDeleteSaves::No;
|
||||
}
|
||||
|
||||
void InstanceTask::scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (path.startsWith("saves/")) {
|
||||
if (m_shouldDeleteSaves == ShouldDeleteSaves::NotAsked) {
|
||||
m_shouldDeleteSaves = askIfShouldDeleteSaves(parent);
|
||||
}
|
||||
if (m_shouldDeleteSaves == ShouldDeleteSaves::No) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
qDebug() << "Scheduling" << path << "for removal";
|
||||
m_filesToRemove.append(dir.absoluteFilePath(path));
|
||||
if (checkDisabled) {
|
||||
if (path.endsWith(".disabled")) { // remove it if it was enabled/disabled by user
|
||||
m_filesToRemove.append(dir.absoluteFilePath(path.chopped(9)));
|
||||
} else {
|
||||
m_filesToRemove.append(dir.absoluteFilePath(path + ".disabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceTask::downloadFiles(MinecraftInstance* inst)
|
||||
{
|
||||
if (!APPLICATION->settings()->get("DownloadGameFilesDuringInstanceCreation").toBool()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
setAbortable(true);
|
||||
setAbortButtonText(tr("Skip"));
|
||||
qDebug() << "Downloading game files";
|
||||
|
||||
auto updateTasks = inst->createUpdateTask();
|
||||
if (updateTasks.isEmpty()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
auto task = makeShared<SequentialTask>();
|
||||
task->addTask(makeShared<MinecraftLoadAndCheck>(inst, Net::Mode::Online));
|
||||
for (const auto& t : updateTasks) {
|
||||
task->addTask(t);
|
||||
}
|
||||
connect(task.get(), &Task::finished, this, [this, task] {
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
if (task->wasSuccessful()) {
|
||||
emitSucceeded();
|
||||
} else {
|
||||
emitFailed(tr("Could not download game files: %1").arg(task->failReason()));
|
||||
}
|
||||
});
|
||||
propagateFromOther(task.get());
|
||||
setDetails(tr("Downloading game files"));
|
||||
|
||||
m_gameFilesTask = task;
|
||||
m_gameFilesTask->start();
|
||||
}
|
||||
|
||||
bool InstanceTask::abort()
|
||||
{
|
||||
if (!canAbort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_gameFilesTask) {
|
||||
return m_gameFilesTask->abort();
|
||||
}
|
||||
|
||||
return Task::abort();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDir>
|
||||
#include <cstdint>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
|
||||
/* Helpers */
|
||||
enum class InstanceNameChange { ShouldChange, ShouldKeep };
|
||||
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
|
||||
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
|
||||
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
|
||||
enum class ShouldDeleteSaves { NotAsked, Yes, No };
|
||||
enum class InstanceNameChange : std::uint8_t { ShouldChange, ShouldKeep };
|
||||
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& oldName, const QString& newName);
|
||||
enum class ShouldUpdate : std::uint8_t { Update, SkipUpdating, Cancel };
|
||||
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, const QString& originalVersionName);
|
||||
enum class ShouldDeleteSaves : std::uint8_t { NotAsked, Yes, No };
|
||||
[[nodiscard]] ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent);
|
||||
|
||||
struct InstanceName {
|
||||
public:
|
||||
InstanceName() = default;
|
||||
InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {}
|
||||
|
||||
QString modifiedName() const;
|
||||
QString originalName() const;
|
||||
QString name() const;
|
||||
QString version() const;
|
||||
|
||||
void setName(QString name) { m_modified_name = name; }
|
||||
void setName(InstanceName& other);
|
||||
|
||||
protected:
|
||||
QString m_original_name;
|
||||
QString m_original_version;
|
||||
|
||||
QString m_modified_name;
|
||||
};
|
||||
|
||||
class InstanceTask : public Task, public InstanceName {
|
||||
class InstanceTask : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InstanceTask();
|
||||
InstanceTask() = default;
|
||||
~InstanceTask() override = default;
|
||||
|
||||
void setParentSettings(SettingsObject* settings) { m_globalSettings = settings; }
|
||||
|
|
@ -46,29 +30,48 @@ class InstanceTask : public Task, public InstanceName {
|
|||
void setGroup(const QString& group) { m_instGroup = group; }
|
||||
QString group() const { return m_instGroup; }
|
||||
|
||||
bool shouldConfirmUpdate() const { return m_confirm_update; }
|
||||
void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
|
||||
bool shouldConfirmUpdate() const { return m_confirmUpdate; }
|
||||
void setConfirmUpdate(bool confirm) { m_confirmUpdate = confirm; }
|
||||
|
||||
bool shouldOverride() const { return m_override_existing; }
|
||||
bool shouldOverride() const { return m_overrideExisting; }
|
||||
|
||||
QString originalInstanceID() const { return m_original_instance_id; };
|
||||
QString originalInstanceID() const { return m_originalInstanceId; };
|
||||
|
||||
QString modifiedName() const;
|
||||
QString originalName() const;
|
||||
QString name() const;
|
||||
QString version() const;
|
||||
|
||||
void setName(const QString& name) { m_modifiedName = name; }
|
||||
void setOriginalName(const QString& name, const QString& version);
|
||||
|
||||
protected:
|
||||
void setOverride(bool override, QString instance_id_to_override = {})
|
||||
{
|
||||
m_override_existing = override;
|
||||
if (!instance_id_to_override.isEmpty())
|
||||
m_original_instance_id = instance_id_to_override;
|
||||
}
|
||||
void setOverride(bool override, const QString& instanceIdToOverride = {});
|
||||
|
||||
void scheduleToDelete(QWidget* parent, const QDir& dir, const QString& path, bool checkDisabled = false);
|
||||
void downloadFiles(MinecraftInstance* inst);
|
||||
|
||||
public slots:
|
||||
bool abort() override;
|
||||
|
||||
protected: /* data */
|
||||
SettingsObject* m_globalSettings;
|
||||
SettingsObject* m_globalSettings{};
|
||||
QString m_instIcon;
|
||||
QString m_instGroup;
|
||||
QString m_stagingPath;
|
||||
|
||||
bool m_override_existing = false;
|
||||
bool m_confirm_update = true;
|
||||
bool m_overrideExisting = false;
|
||||
bool m_confirmUpdate = true;
|
||||
|
||||
QString m_original_instance_id;
|
||||
QString m_originalInstanceId;
|
||||
|
||||
QString m_originalName;
|
||||
QString m_originalVersion;
|
||||
|
||||
QString m_modifiedName;
|
||||
|
||||
QStringList m_filesToRemove;
|
||||
ShouldDeleteSaves m_shouldDeleteSaves{};
|
||||
|
||||
Task::Ptr m_gameFilesTask;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,26 +8,30 @@
|
|||
#include "settings/INISettingsObject.h"
|
||||
|
||||
VanillaCreationTask::VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loaderVersion)
|
||||
: m_version(std::move(version)), m_using_loader(true), m_loader(std::move(loader)), m_loader_version(std::move(loaderVersion))
|
||||
: m_version(std::move(version)), m_usingLoader(true), m_loader(std::move(loader)), m_loaderVersion(std::move(loaderVersion))
|
||||
{}
|
||||
|
||||
std::unique_ptr<MinecraftInstance> VanillaCreationTask::createInstance()
|
||||
void VanillaCreationTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
||||
|
||||
auto inst = std::make_unique<MinecraftInstance>(
|
||||
m_instance = std::make_unique<MinecraftInstance>(
|
||||
m_globalSettings, std::make_unique<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg")), m_stagingPath);
|
||||
SettingsObject::Lock lock(inst->settings());
|
||||
{
|
||||
SettingsObject::Lock const lock(m_instance->settings());
|
||||
|
||||
auto* components = inst->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
||||
if (m_using_loader) {
|
||||
components->setComponentVersion(m_loader, m_loader_version->descriptor());
|
||||
auto* components = m_instance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
||||
if (m_usingLoader) {
|
||||
components->setComponentVersion(m_loader, m_loaderVersion->descriptor());
|
||||
}
|
||||
|
||||
m_instance->setName(name());
|
||||
m_instance->setIconKey(m_instIcon);
|
||||
|
||||
components->saveNow();
|
||||
}
|
||||
|
||||
inst->setName(name());
|
||||
inst->setIconKey(m_instIcon);
|
||||
components->saveNow();
|
||||
return inst;
|
||||
downloadFiles(m_instance.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "InstanceCreationTask.h"
|
||||
#include <memory>
|
||||
#include "BaseVersion.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
class VanillaCreationTask final : public InstanceCreationTask {
|
||||
class VanillaCreationTask final : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VanillaCreationTask(BaseVersion::Ptr version) : m_version(std::move(version)) {}
|
||||
VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loaderVersion);
|
||||
|
||||
std::unique_ptr<MinecraftInstance> createInstance() override;
|
||||
void executeTask() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MinecraftInstance> m_instance;
|
||||
|
||||
// Version to update to / create of the instance.
|
||||
BaseVersion::Ptr m_version;
|
||||
|
||||
bool m_using_loader = false;
|
||||
bool m_usingLoader = false;
|
||||
QString m_loader;
|
||||
BaseVersion::Ptr m_loader_version;
|
||||
BaseVersion::Ptr m_loaderVersion;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1091,7 +1091,7 @@ void PackInstallTask::install()
|
|||
|
||||
jarmods.clear();
|
||||
}
|
||||
emitSucceeded();
|
||||
downloadFiles(&instance);
|
||||
}
|
||||
|
||||
} // namespace ATLauncher
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
|
||||
#include "FlameInstanceCreationTask.h"
|
||||
|
||||
#include "InstanceTask.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
#include "modplatform/flame/FileResolvingTask.h"
|
||||
|
|
@ -55,13 +54,13 @@
|
|||
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
#include "SysInfo.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
#include "ui/dialogs/BlockedModsDialog.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <utility>
|
||||
|
||||
#include "HardwareInfo.h"
|
||||
#include "meta/Index.h"
|
||||
|
|
@ -70,199 +69,223 @@
|
|||
#include "net/ApiDownload.h"
|
||||
#include "ui/pages/modplatform/OptionalModDialog.h"
|
||||
|
||||
static const FlameAPI api;
|
||||
|
||||
bool FlameCreationTask::abort()
|
||||
{
|
||||
if (!canAbort())
|
||||
if (!canAbort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_processUpdateFileInfoJob)
|
||||
if (m_processUpdateFileInfoJob) {
|
||||
m_processUpdateFileInfoJob->abort();
|
||||
if (m_filesJob)
|
||||
}
|
||||
if (m_filesJob) {
|
||||
m_filesJob->abort();
|
||||
if (m_modIdResolver)
|
||||
}
|
||||
if (m_modIdResolver) {
|
||||
m_modIdResolver->abort();
|
||||
}
|
||||
|
||||
return InstanceCreationTask::abort();
|
||||
return InstanceTask::abort();
|
||||
}
|
||||
|
||||
bool FlameCreationTask::updateInstance()
|
||||
void FlameCreationTask::executeTask()
|
||||
{
|
||||
auto instance_list = APPLICATION->instances();
|
||||
auto* instanceList = APPLICATION->instances();
|
||||
|
||||
// FIXME: How to handle situations when there's more than one install already for a given modpack?
|
||||
BaseInstance* inst;
|
||||
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
|
||||
inst = instance_list->getInstanceById(original_id);
|
||||
BaseInstance* inst = nullptr;
|
||||
if (auto originalId = originalInstanceID(); !originalId.isEmpty()) {
|
||||
inst = instanceList->getInstanceById(originalId);
|
||||
Q_ASSERT(inst);
|
||||
} else {
|
||||
inst = instance_list->getInstanceByManagedName(originalName());
|
||||
inst = instanceList->getInstanceByManagedName(originalName());
|
||||
|
||||
if (!inst) {
|
||||
inst = instance_list->getInstanceById(originalName());
|
||||
inst = instanceList->getInstanceById(originalName());
|
||||
|
||||
if (!inst)
|
||||
return false;
|
||||
if (!inst) {
|
||||
createInstance();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
QString indexPath(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
|
||||
try {
|
||||
Flame::loadManifest(m_pack, index_path);
|
||||
} catch (const JSONValidationError& e) {
|
||||
setError(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return false;
|
||||
Flame::loadManifest(m_pack, indexPath);
|
||||
} catch (const JSONValidationError&) {
|
||||
// emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
createInstance(); // to keep the backwards comatibility here just create the instance
|
||||
return;
|
||||
}
|
||||
|
||||
auto version_id = inst->getManagedPackVersionName();
|
||||
auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : "";
|
||||
auto versionId = inst->getManagedPackVersionName();
|
||||
auto versionStr = !versionId.isEmpty() ? tr(" (version %1)").arg(versionId) : "";
|
||||
|
||||
if (shouldConfirmUpdate()) {
|
||||
auto should_update = askIfShouldUpdate(m_parent, version_str);
|
||||
if (should_update == ShouldUpdate::SkipUpdating)
|
||||
return false;
|
||||
if (should_update == ShouldUpdate::Cancel) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr);
|
||||
if (shouldUpdate == ShouldUpdate::SkipUpdating) {
|
||||
createInstance();
|
||||
return;
|
||||
}
|
||||
if (shouldUpdate == ShouldUpdate::Cancel) {
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QDir old_inst_dir(inst->instanceRoot());
|
||||
QDir const oldInstDir(inst->instanceRoot());
|
||||
|
||||
QString old_index_folder(FS::PathCombine(old_inst_dir.absolutePath(), "flame"));
|
||||
QString old_index_path(FS::PathCombine(old_index_folder, "manifest.json"));
|
||||
QString const oldIndexFolder(FS::PathCombine(oldInstDir.absolutePath(), "flame"));
|
||||
QString const oldIndexPath(FS::PathCombine(oldIndexFolder, "manifest.json"));
|
||||
|
||||
QFileInfo old_index_file(old_index_path);
|
||||
if (old_index_file.exists()) {
|
||||
Flame::Manifest old_pack;
|
||||
Flame::loadManifest(old_pack, old_index_path);
|
||||
QFileInfo const oldIndexFile(oldIndexPath);
|
||||
auto createInst = [this, inst] {
|
||||
setOverride(true, inst->id());
|
||||
qDebug() << "Will override instance!";
|
||||
|
||||
auto& old_files = old_pack.files;
|
||||
m_oldInstance = inst;
|
||||
|
||||
// We let it go through the createInstance() stage, just with a couple modifications for updating
|
||||
createInstance();
|
||||
};
|
||||
|
||||
auto warnUser = [this, createInst](const QString& title,
|
||||
const QString& text) { // We don't have an old index file, so we may duplicate stuff!
|
||||
auto* dialog = CustomMessageBox::selectable(m_parent, title, text, QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (dialog->exec() == QDialog::DialogCode::Rejected) {
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
|
||||
createInst();
|
||||
};
|
||||
|
||||
if (oldIndexFile.exists()) {
|
||||
Flame::Manifest oldPack;
|
||||
Flame::loadManifest(oldPack, oldIndexPath);
|
||||
|
||||
auto oldFiles = oldPack.files;
|
||||
|
||||
auto& files = m_pack.files;
|
||||
|
||||
// Remove repeated files, we don't need to download them!
|
||||
auto files_iterator = files.begin();
|
||||
while (files_iterator != files.end()) {
|
||||
auto const& file = files_iterator;
|
||||
auto filesIterator = files.begin();
|
||||
while (filesIterator != files.end()) {
|
||||
const auto& file = filesIterator;
|
||||
|
||||
auto old_file = old_files.find(file.key());
|
||||
if (old_file != old_files.end()) {
|
||||
auto oldFile = oldFiles.find(file.key());
|
||||
if (oldFile != oldFiles.end()) {
|
||||
// We found a match, but is it a different version?
|
||||
if (old_file->fileId == file->fileId) {
|
||||
if (oldFile->fileId == file->fileId) {
|
||||
qDebug() << "Removed file at" << file->targetFolder << "with id" << file->fileId << "from list of downloads";
|
||||
|
||||
old_files.remove(file.key());
|
||||
files_iterator = files.erase(files_iterator);
|
||||
oldFiles.remove(file.key());
|
||||
filesIterator = files.erase(filesIterator);
|
||||
|
||||
if (files_iterator != files.begin())
|
||||
files_iterator--;
|
||||
if (filesIterator != files.begin()) {
|
||||
filesIterator--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files_iterator++;
|
||||
filesIterator++;
|
||||
}
|
||||
|
||||
QDir old_minecraft_dir(inst->gameRoot());
|
||||
QDir const oldMinecraftDir(inst->gameRoot());
|
||||
|
||||
// We will remove all the previous overrides, to prevent duplicate files!
|
||||
// TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides?
|
||||
// FIXME: We may want to do something about disabled mods.
|
||||
auto old_overrides = Override::readOverrides("overrides", old_index_folder);
|
||||
for (const auto& entry : old_overrides) {
|
||||
scheduleToDelete(m_parent, old_minecraft_dir, entry);
|
||||
auto oldOverrides = Override::readOverrides("overrides", oldIndexFolder);
|
||||
for (const auto& entry : oldOverrides) {
|
||||
scheduleToDelete(m_parent, oldMinecraftDir, entry);
|
||||
}
|
||||
|
||||
// Remove remaining old files (we need to do an API request to know which ids are which files...)
|
||||
QStringList fileIds;
|
||||
|
||||
for (auto& file : old_files) {
|
||||
for (auto& file : oldFiles) {
|
||||
fileIds.append(QString::number(file.fileId));
|
||||
}
|
||||
|
||||
auto [job, raw_response] = api.getFiles(fileIds);
|
||||
auto [job, rawResponse] = FlameAPI().getFiles(fileIds);
|
||||
|
||||
QEventLoop loop;
|
||||
connect(job.get(), &Task::succeeded, this,
|
||||
[this, rawResponse, fileIds, oldInstDir, oldFiles, oldMinecraftDir, createInst]() mutable {
|
||||
// Parse the API response
|
||||
QJsonParseError parseError{};
|
||||
auto doc = QJsonDocument::fromJson(*rawResponse, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame files task at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *rawResponse;
|
||||
return;
|
||||
}
|
||||
|
||||
connect(job.get(), &Task::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] {
|
||||
// Parse the API response
|
||||
QJsonParseError parse_error{};
|
||||
auto doc = QJsonDocument::fromJson(*raw_response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame files task at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
qWarning() << *raw_response;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
QJsonArray entries;
|
||||
if (fileIds.size() == 1) {
|
||||
entries = { Json::requireObject(Json::requireObject(doc), "data") };
|
||||
} else {
|
||||
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||
}
|
||||
|
||||
try {
|
||||
QJsonArray entries;
|
||||
if (fileIds.size() == 1)
|
||||
entries = { Json::requireObject(Json::requireObject(doc), "data") };
|
||||
else
|
||||
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||
for (auto entry : entries) {
|
||||
auto entryObj = Json::requireObject(entry);
|
||||
|
||||
for (auto entry : entries) {
|
||||
auto entry_obj = Json::requireObject(entry);
|
||||
Flame::File file;
|
||||
// We don't care about blocked mods, we just need local data to delete the file
|
||||
file.version = FlameMod::loadIndexedPackVersion(entryObj);
|
||||
auto id = Json::requireInteger(entryObj, "id");
|
||||
oldFiles.insert(id, file);
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
qCritical() << e.cause() << e.what();
|
||||
}
|
||||
|
||||
Flame::File file;
|
||||
// We don't care about blocked mods, we just need local data to delete the file
|
||||
file.version = FlameMod::loadIndexedPackVersion(entry_obj);
|
||||
auto id = Json::requireInteger(entry_obj, "id");
|
||||
old_files.insert(id, file);
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
qCritical() << e.cause() << e.what();
|
||||
}
|
||||
// Delete the files
|
||||
for (const auto& file : oldFiles) {
|
||||
if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete the files
|
||||
for (auto& file : old_files) {
|
||||
if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty())
|
||||
continue;
|
||||
QString const relativePath(FS::PathCombine(file.targetFolder, file.version.fileName));
|
||||
scheduleToDelete(m_parent, oldMinecraftDir, relativePath, true);
|
||||
}
|
||||
|
||||
QString relative_path(FS::PathCombine(file.targetFolder, file.version.fileName));
|
||||
scheduleToDelete(m_parent, old_minecraft_dir, relative_path, true);
|
||||
}
|
||||
createInst();
|
||||
});
|
||||
connect(job.get(), &Task::aborted, this, [warnUser] {
|
||||
warnUser(tr("Failed to fetch the old files."),
|
||||
tr("We couldn't fetch the old files because the task was aborted. This may cause "
|
||||
"some of the files to be duplicated. Do you want to continue?"));
|
||||
});
|
||||
connect(job.get(), &Task::failed, this, [warnUser](const QString& reason) {
|
||||
warnUser(tr("Failed to fetch the old files."), tr("We couldn't fetch the old files because: %1. This may cause some of the "
|
||||
"files to be duplicated. Do you want to continue?")
|
||||
.arg(reason));
|
||||
});
|
||||
connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files:" << reason; });
|
||||
connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
m_processUpdateFileInfoJob = job;
|
||||
job->start();
|
||||
|
||||
loop.exec();
|
||||
|
||||
m_processUpdateFileInfoJob = nullptr;
|
||||
} else {
|
||||
// We don't have an old index file, so we may duplicate stuff!
|
||||
auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."),
|
||||
tr("We couldn't find a suitable index file for the older version. This may cause some "
|
||||
"of the files to be duplicated. Do you want to continue?"),
|
||||
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (dialog->exec() == QDialog::DialogCode::Rejected) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setOverride(true, inst->id());
|
||||
qDebug() << "Will override instance!";
|
||||
|
||||
m_instance = inst;
|
||||
|
||||
// We let it go through the createInstance() stage, just with a couple modifications for updating
|
||||
return false;
|
||||
warnUser(tr("No index file."), tr("We couldn't find a suitable index file for the older version. This may cause some of the files to "
|
||||
"be duplicated. Do you want to continue?"));
|
||||
}
|
||||
|
||||
QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion)
|
||||
QString FlameCreationTask::getVersionForLoader(const QString& uid,
|
||||
const QString& loaderType,
|
||||
const QString& loaderVersion,
|
||||
const QString& mcVersion)
|
||||
{
|
||||
if (loaderVersion == "recommended") {
|
||||
auto vlist = APPLICATION->metadataIndex()->get(uid);
|
||||
if (!vlist) {
|
||||
setError(tr("Failed to get local metadata index for %1").arg(uid));
|
||||
emitFailed(tr("Failed to get local metadata index for %1").arg(uid));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -270,16 +293,18 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
|
|||
QEventLoop loadVersionLoop;
|
||||
auto task = vlist->getLoadTask();
|
||||
connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||
if (!task->isRunning())
|
||||
if (!task->isRunning()) {
|
||||
task->start();
|
||||
}
|
||||
|
||||
loadVersionLoop.exec();
|
||||
}
|
||||
|
||||
for (auto version : vlist->versions()) {
|
||||
for (const auto& version : vlist->versions()) {
|
||||
// first recommended build we find, we use.
|
||||
if (!version->isRecommended())
|
||||
if (!version->isRecommended()) {
|
||||
continue;
|
||||
}
|
||||
auto reqs = version->requiredSet();
|
||||
|
||||
// filter by minecraft version, if the loader depends on a certain version.
|
||||
|
|
@ -289,13 +314,14 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
|
|||
auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) {
|
||||
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
|
||||
});
|
||||
if (iter == reqs.end())
|
||||
if (iter == reqs.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return version->descriptor();
|
||||
}
|
||||
|
||||
setError(tr("Failed to find version for %1 loader").arg(loaderType));
|
||||
emitFailed(tr("Failed to find version for %1 loader").arg(loaderType));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -307,37 +333,46 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
|
|||
return loaderVersion;
|
||||
}
|
||||
|
||||
std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
||||
void FlameCreationTask::setManagedPack(BaseInstance* instance)
|
||||
{
|
||||
QEventLoop loop;
|
||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||
if (!m_managedId.isEmpty()) {
|
||||
instance->setManagedPack("flame", m_managedId, m_pack.name, m_managedVersionId, m_pack.version);
|
||||
} else {
|
||||
instance->setManagedPack("flame", "", name(), "", "");
|
||||
}
|
||||
}
|
||||
|
||||
QString parent_folder(FS::PathCombine(m_stagingPath, "flame"));
|
||||
void FlameCreationTask::createInstance()
|
||||
{
|
||||
QString const parentFolder(FS::PathCombine(m_stagingPath, "flame"));
|
||||
|
||||
try {
|
||||
QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
if (!m_pack.is_loaded)
|
||||
Flame::loadManifest(m_pack, index_path);
|
||||
QString const indexPath(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
if (!m_pack.isLoaded) {
|
||||
Flame::loadManifest(m_pack, indexPath);
|
||||
}
|
||||
|
||||
// Keep index file in case we need it some other time (like when changing versions)
|
||||
QString new_index_place(FS::PathCombine(parent_folder, "manifest.json"));
|
||||
FS::ensureFilePathExists(new_index_place);
|
||||
FS::move(index_path, new_index_place);
|
||||
QString const newIndexPlace(FS::PathCombine(parentFolder, "manifest.json"));
|
||||
FS::ensureFilePathExists(newIndexPlace);
|
||||
FS::move(indexPath, newIndexPlace);
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
setError(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return nullptr;
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_pack.overrides.isEmpty()) {
|
||||
QString overridePath = FS::PathCombine(m_stagingPath, m_pack.overrides);
|
||||
if (QFile::exists(overridePath)) {
|
||||
// Create a list of overrides in "overrides.txt" inside flame/
|
||||
Override::createOverrides("overrides", parent_folder, overridePath);
|
||||
Override::createOverrides("overrides", parentFolder, overridePath);
|
||||
|
||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
||||
if (!FS::move(overridePath, mcPath)) {
|
||||
setError(tr("Could not rename the overrides folder:\n") + m_pack.overrides);
|
||||
return nullptr;
|
||||
emitFailed(tr("Could not rename the overrides folder:\n") + m_pack.overrides);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logWarning(
|
||||
|
|
@ -353,8 +388,9 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
auto id = loader.id;
|
||||
if (id.startsWith("neoforge-")) {
|
||||
id.remove("neoforge-");
|
||||
if (id.startsWith("1.20.1-"))
|
||||
if (id.startsWith("1.20.1-")) {
|
||||
id.remove("1.20.1-"); // this is a mess for curseforge
|
||||
}
|
||||
loaderType = "neoforge";
|
||||
loaderUid = "net.neoforged";
|
||||
} else if (id.startsWith("forge-")) {
|
||||
|
|
@ -378,7 +414,7 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_unique<INISettingsObject>(configPath);
|
||||
auto instance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath);
|
||||
m_newInstance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath);
|
||||
auto mcVersion = m_pack.minecraft.version;
|
||||
|
||||
// Hack to correct some 'special sauce'...
|
||||
|
|
@ -388,32 +424,33 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
||||
}
|
||||
|
||||
auto components = instance->getPackProfile();
|
||||
auto* components = m_newInstance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||
if (!loaderType.isEmpty()) {
|
||||
auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion);
|
||||
if (version.isEmpty())
|
||||
return nullptr;
|
||||
if (version.isEmpty()) { // because there are more info in getVersionForLoader the emitFailed is trigered inside it
|
||||
return;
|
||||
}
|
||||
components->setComponentVersion(loaderUid, version);
|
||||
}
|
||||
|
||||
if (m_instIcon != "default") {
|
||||
instance->setIconKey(m_instIcon);
|
||||
m_newInstance->setIconKey(m_instIcon);
|
||||
} else {
|
||||
if (m_pack.name.contains("Direwolf20")) {
|
||||
instance->setIconKey("steve");
|
||||
m_newInstance->setIconKey("steve");
|
||||
} else if (m_pack.name.contains("FTB") || m_pack.name.contains("Feed The Beast")) {
|
||||
instance->setIconKey("ftb_logo");
|
||||
m_newInstance->setIconKey("ftb_logo");
|
||||
} else {
|
||||
instance->setIconKey("flame");
|
||||
m_newInstance->setIconKey("flame");
|
||||
}
|
||||
}
|
||||
|
||||
int recommendedRAM = m_pack.minecraft.recommendedRAM;
|
||||
|
||||
// only set memory if this is a fresh instance
|
||||
if (!m_instance && recommendedRAM > 0) {
|
||||
if (!m_oldInstance && recommendedRAM > 0) {
|
||||
const uint64_t sysMiB = HardwareInfo::totalRamMiB();
|
||||
const uint64_t max = sysMiB * 0.9;
|
||||
|
||||
|
|
@ -424,8 +461,8 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
recommendedRAM = max;
|
||||
}
|
||||
|
||||
instance->settings()->set("OverrideMemory", true);
|
||||
instance->settings()->set("MaxMemAlloc", recommendedRAM);
|
||||
m_newInstance->settings()->set("OverrideMemory", true);
|
||||
m_newInstance->settings()->set("MaxMemAlloc", recommendedRAM);
|
||||
}
|
||||
|
||||
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
||||
|
|
@ -439,53 +476,31 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
qDebug() << info.fileName();
|
||||
jarMods.push_back(info.absoluteFilePath());
|
||||
}
|
||||
auto profile = instance->getPackProfile();
|
||||
auto* profile = m_newInstance->getPackProfile();
|
||||
profile->installJarMods(jarMods);
|
||||
// nuke the original files
|
||||
FS::deletePath(jarmodsPath);
|
||||
}
|
||||
|
||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||
if (!m_managedId.isEmpty())
|
||||
instance->setManagedPack("flame", m_managedId, m_pack.name, m_managedVersionId, m_pack.version);
|
||||
else
|
||||
instance->setManagedPack("flame", "", name(), "", "");
|
||||
setManagedPack(m_newInstance.get());
|
||||
|
||||
instance->setName(name());
|
||||
m_newInstance->setName(name());
|
||||
|
||||
m_modIdResolver.reset(new Flame::FileResolvingTask(m_pack));
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop] { idResolverSucceeded(loop); });
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this, &loop](QString reason) {
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this, &FlameCreationTask::idResolverSucceeded);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this](const QString& reason) {
|
||||
m_modIdResolver.reset();
|
||||
setError(tr("Unable to resolve mod IDs:\n") + reason);
|
||||
loop.quit();
|
||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
||||
});
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::aborted, &loop, &QEventLoop::quit);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::aborted, this, &FlameCreationTask::emitAborted);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails);
|
||||
m_modIdResolver->start();
|
||||
|
||||
loop.exec();
|
||||
|
||||
bool did_succeed = getError().isEmpty();
|
||||
|
||||
// Update information of the already installed instance, if any.
|
||||
if (m_instance && did_succeed) {
|
||||
setAbortable(false);
|
||||
auto inst = m_instance.value();
|
||||
|
||||
inst->copyManagedPack(*instance);
|
||||
}
|
||||
|
||||
if (did_succeed) {
|
||||
return instance;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
||||
void FlameCreationTask::idResolverSucceeded()
|
||||
{
|
||||
auto results = m_modIdResolver->getResults().files;
|
||||
|
||||
|
|
@ -500,7 +515,6 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||
OptionalModDialog optionalModDialog(m_parent, optionalFiles);
|
||||
if (optionalModDialog.exec() == QDialog::Rejected) {
|
||||
emitAborted();
|
||||
loop.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -508,7 +522,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||
}
|
||||
|
||||
// first check for blocked mods
|
||||
QList<BlockedMod> blocked_mods;
|
||||
QList<BlockedMod> blockedMods;
|
||||
auto anyBlocked = false;
|
||||
for (const auto& result : results.values()) {
|
||||
if (result.resourceType != ModPlatform::ResourceType::Mod) {
|
||||
|
|
@ -517,19 +531,19 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||
|
||||
// skip optional mods that were not selected
|
||||
if (result.version.downloadUrl.isEmpty()) {
|
||||
BlockedMod blocked_mod;
|
||||
blocked_mod.name = result.version.fileName;
|
||||
blocked_mod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId));
|
||||
blocked_mod.hash = result.version.hash;
|
||||
blocked_mod.matched = false;
|
||||
blocked_mod.localPath = "";
|
||||
blocked_mod.targetFolder = result.targetFolder;
|
||||
BlockedMod blockedMod;
|
||||
blockedMod.name = result.version.fileName;
|
||||
blockedMod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId));
|
||||
blockedMod.hash = result.version.hash;
|
||||
blockedMod.matched = false;
|
||||
blockedMod.localPath = "";
|
||||
blockedMod.targetFolder = result.targetFolder;
|
||||
auto fileName = result.version.fileName;
|
||||
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||
auto relpath = FS::PathCombine(result.targetFolder, fileName);
|
||||
blocked_mod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath);
|
||||
blockedMod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath);
|
||||
|
||||
blocked_mods.append(blocked_mod);
|
||||
blockedMods.append(blockedMod);
|
||||
|
||||
anyBlocked = true;
|
||||
}
|
||||
|
|
@ -537,28 +551,28 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||
if (anyBlocked) {
|
||||
qWarning() << "Blocked mods found, displaying mod list";
|
||||
|
||||
BlockedModsDialog message_dialog(m_parent, tr("Blocked mods found"),
|
||||
tr("The following files are not available for download in third party launchers.<br/>"
|
||||
"You will need to manually download them and add them to the instance."),
|
||||
blocked_mods);
|
||||
BlockedModsDialog messageDialog(m_parent, tr("Blocked mods found"),
|
||||
tr("The following files are not available for download in third party launchers.<br/>"
|
||||
"You will need to manually download them and add them to the instance."),
|
||||
blockedMods);
|
||||
|
||||
message_dialog.setModal(true);
|
||||
messageDialog.setModal(true);
|
||||
|
||||
if (message_dialog.exec()) {
|
||||
qDebug() << "Post dialog blocked mods list:" << blocked_mods;
|
||||
copyBlockedMods(blocked_mods);
|
||||
setupDownloadJob(loop);
|
||||
if (messageDialog.exec() != 0) {
|
||||
qDebug() << "Post dialog blocked mods list:" << blockedMods;
|
||||
copyBlockedMods(blockedMods);
|
||||
setupDownloadJob();
|
||||
} else {
|
||||
m_modIdResolver.reset();
|
||||
setError("Canceled");
|
||||
loop.quit();
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
setupDownloadJob(loop);
|
||||
setupDownloadJob();
|
||||
}
|
||||
}
|
||||
|
||||
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||
void FlameCreationTask::setupDownloadJob()
|
||||
{
|
||||
m_filesJob.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network()));
|
||||
auto results = m_modIdResolver->getResults().files;
|
||||
|
|
@ -582,13 +596,13 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||
}
|
||||
}
|
||||
|
||||
connect(m_filesJob.get(), &NetJob::finished, this, [this, &loop]() {
|
||||
connect(m_filesJob.get(), &NetJob::finished, this, [this]() {
|
||||
m_filesJob.reset();
|
||||
validateOtherResources(loop);
|
||||
validateOtherResources();
|
||||
});
|
||||
connect(m_filesJob.get(), &NetJob::failed, [this](QString reason) {
|
||||
m_filesJob.reset();
|
||||
setError(reason);
|
||||
emitFailed(std::move(reason));
|
||||
});
|
||||
connect(m_filesJob.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
|
|
@ -601,23 +615,24 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||
}
|
||||
|
||||
/// @brief copy the matched blocked mods to the instance staging area
|
||||
/// @param blocked_mods list of the blocked mods and their matched paths
|
||||
void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
||||
/// @param blockedMods list of the blocked mods and their matched paths
|
||||
void FlameCreationTask::copyBlockedMods(const QList<BlockedMod>& blockedMods)
|
||||
{
|
||||
setStatus(tr("Copying Blocked Mods..."));
|
||||
setAbortable(false);
|
||||
int i = 0;
|
||||
int total = blocked_mods.length();
|
||||
setProgress(i, total);
|
||||
for (auto const& mod : blocked_mods) {
|
||||
auto const total = blockedMods.length();
|
||||
setProgress(i, static_cast<int>(total));
|
||||
for (const auto& mod : blockedMods) {
|
||||
if (!mod.matched) {
|
||||
qDebug() << mod.name << "was not matched to a local file, skipping copy";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto destPath = FS::PathCombine(m_stagingPath, "minecraft", mod.targetFolder, mod.name);
|
||||
if (mod.disabled)
|
||||
if (mod.disabled) {
|
||||
destPath += ".disabled";
|
||||
}
|
||||
|
||||
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
|
||||
|
||||
|
|
@ -640,17 +655,17 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
|||
setAbortable(true);
|
||||
}
|
||||
|
||||
void FlameCreationTask::validateOtherResources(QEventLoop& loop)
|
||||
void FlameCreationTask::validateOtherResources()
|
||||
{
|
||||
qDebug() << "Validating whether other resources are in the right place";
|
||||
QStringList zipMods;
|
||||
for (auto [fileName, targetFolder] : m_otherResources) {
|
||||
for (const auto& [fileName, targetFolder] : m_otherResources) {
|
||||
qDebug() << "Checking" << fileName << "...";
|
||||
auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName);
|
||||
|
||||
/// @brief check the target and move the the file
|
||||
/// @return path where file can now be found
|
||||
auto validatePath = [&localPath, this](QString fileName, QString targetFolder, QString realTarget) {
|
||||
auto validatePath = [&localPath, this](const QString& fileName, const QString& targetFolder, const QString& realTarget) {
|
||||
if (targetFolder != realTarget) {
|
||||
qDebug() << "Target folder of" << fileName << "is incorrect, it belongs in" << realTarget;
|
||||
auto destPath = FS::PathCombine(m_stagingPath, "minecraft", realTarget, fileName);
|
||||
|
|
@ -664,7 +679,7 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop)
|
|||
return localPath;
|
||||
};
|
||||
|
||||
auto installWorld = [this](QString worldPath) {
|
||||
auto installWorld = [this](const QString& worldPath) {
|
||||
qDebug() << "Installing World from" << worldPath;
|
||||
QFileInfo worldFileInfo(worldPath);
|
||||
World w(worldFileInfo);
|
||||
|
|
@ -714,13 +729,49 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop)
|
|||
auto task = makeShared<ConcurrentTask>("CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
auto results = m_modIdResolver->getResults().files;
|
||||
auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index");
|
||||
for (auto file : results) {
|
||||
for (const auto& file : results) {
|
||||
if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) {
|
||||
continue;
|
||||
}
|
||||
task->addTask(makeShared<LocalResourceUpdateTask>(folder, file.pack, file.version));
|
||||
}
|
||||
connect(task.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||
connect(task.get(), &Task::finished, this, &FlameCreationTask::finishInstall);
|
||||
m_processUpdateFileInfoJob = task;
|
||||
task->start();
|
||||
}
|
||||
|
||||
void FlameCreationTask::finishInstall()
|
||||
{
|
||||
// Update information of the already installed instance, if any.
|
||||
if (m_oldInstance) {
|
||||
setAbortable(false);
|
||||
setManagedPack(*m_oldInstance);
|
||||
}
|
||||
|
||||
if (shouldOverride()) {
|
||||
bool deleteFailed = false;
|
||||
|
||||
setAbortable(false);
|
||||
setStatus(tr("Removing old conflicting files..."));
|
||||
qDebug() << "Removing old files";
|
||||
|
||||
for (const QString& path : m_filesToRemove) {
|
||||
if (!QFile::exists(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "Removing" << path;
|
||||
|
||||
if (!QFile::remove(path)) {
|
||||
qCritical() << "Could not remove" << path;
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFailed) {
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
downloadFiles(m_newInstance.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,47 +35,51 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "InstanceCreationTask.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
#include "modplatform/flame/FileResolvingTask.h"
|
||||
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "ui/dialogs/BlockedModsDialog.h"
|
||||
|
||||
class FlameCreationTask final : public InstanceCreationTask {
|
||||
class FlameCreationTask final : public InstanceTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameCreationTask(const QString& staging_path,
|
||||
SettingsObject* global_settings,
|
||||
FlameCreationTask(const QString& stagingPath,
|
||||
SettingsObject* globalSettings,
|
||||
QWidget* parent,
|
||||
QString id,
|
||||
QString version_id,
|
||||
QString original_instance_id = {})
|
||||
: InstanceCreationTask(), m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(version_id))
|
||||
QString versionId,
|
||||
const QString& originalInstanceId = {})
|
||||
: m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(versionId))
|
||||
{
|
||||
setStagingPath(staging_path);
|
||||
setParentSettings(global_settings);
|
||||
setStagingPath(stagingPath);
|
||||
setParentSettings(globalSettings);
|
||||
|
||||
m_original_instance_id = std::move(original_instance_id);
|
||||
m_originalInstanceId = originalInstanceId;
|
||||
}
|
||||
|
||||
bool abort() override;
|
||||
|
||||
bool updateInstance() override;
|
||||
std::unique_ptr<MinecraftInstance> createInstance() override;
|
||||
void createInstance();
|
||||
void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void idResolverSucceeded(QEventLoop&);
|
||||
void setupDownloadJob(QEventLoop&);
|
||||
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||
void validateOtherResources(QEventLoop& loop);
|
||||
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
||||
void idResolverSucceeded();
|
||||
void setupDownloadJob();
|
||||
void copyBlockedMods(const QList<BlockedMod>& blockedMods);
|
||||
void validateOtherResources();
|
||||
QString getVersionForLoader(const QString& uid, const QString& loaderType, const QString& version, const QString& mcVersion);
|
||||
void finishInstall();
|
||||
|
||||
private:
|
||||
void setManagedPack(BaseInstance* instance);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
|
@ -91,7 +95,8 @@ class FlameCreationTask final : public InstanceCreationTask {
|
|||
|
||||
QList<std::pair<QString, QString>> m_otherResources;
|
||||
|
||||
std::optional<BaseInstance*> m_instance;
|
||||
std::optional<BaseInstance*> m_oldInstance{};
|
||||
std::unique_ptr<MinecraftInstance> m_newInstance{};
|
||||
|
||||
QStringList m_selectedOptionalMods;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
#include "PackManifest.h"
|
||||
#include "Json.h"
|
||||
|
||||
static void loadFileV1(Flame::File& f, QJsonObject& file)
|
||||
namespace {
|
||||
void loadFileV1(Flame::File& f, QJsonObject& file)
|
||||
{
|
||||
f.projectId = Json::requireInteger(file, "projectID");
|
||||
f.fileId = Json::requireInteger(file, "fileID");
|
||||
f.required = file["required"].toBool(true);
|
||||
}
|
||||
|
||||
static void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader)
|
||||
void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader)
|
||||
{
|
||||
m.id = Json::requireString(modLoader, "id");
|
||||
m.primary = modLoader["primary"].toBool();
|
||||
}
|
||||
|
||||
static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
||||
void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
||||
{
|
||||
m.version = Json::requireString(minecraft, "version");
|
||||
// extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
|
||||
|
|
@ -30,7 +30,7 @@ static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
|||
m.recommendedRAM = minecraft["recommendedRam"].toInt();
|
||||
}
|
||||
|
||||
static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
||||
void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
||||
{
|
||||
auto mc = Json::requireObject(manifest, "minecraft");
|
||||
|
||||
|
|
@ -52,8 +52,9 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
|||
|
||||
pack.overrides = manifest["overrides"].toString("overrides");
|
||||
|
||||
pack.is_loaded = true;
|
||||
pack.isLoaded = true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Flame::loadManifest(Flame::Manifest& m, const QString& filepath)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ struct Manifest {
|
|||
QMap<int, Flame::File> files;
|
||||
QString overrides;
|
||||
|
||||
bool is_loaded = false;
|
||||
bool isLoaded = false;
|
||||
};
|
||||
|
||||
void loadManifest(Flame::Manifest& m, const QString& filepath);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
#include "FTBPackInstallTask.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
|
@ -59,15 +61,18 @@ PackInstallTask::PackInstallTask(Modpack pack, QString version, QWidget* parent)
|
|||
|
||||
bool PackInstallTask::abort()
|
||||
{
|
||||
if (!canAbort())
|
||||
if (!canAbort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool aborted = true;
|
||||
|
||||
if (m_net_job)
|
||||
if (m_net_job) {
|
||||
aborted &= m_net_job->abort();
|
||||
if (m_modIdResolverTask)
|
||||
}
|
||||
if (m_modIdResolverTask) {
|
||||
aborted &= m_modIdResolverTask->abort();
|
||||
}
|
||||
|
||||
return aborted ? InstanceTask::abort() : false;
|
||||
}
|
||||
|
|
@ -78,15 +83,15 @@ void PackInstallTask::executeTask()
|
|||
setAbortable(false);
|
||||
|
||||
// Find pack version
|
||||
auto version_it = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(),
|
||||
[this](const FTB::VersionInfo& a) { return a.name == m_versionName; });
|
||||
auto versionIt = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(),
|
||||
[this](const FTB::VersionInfo& a) { return a.name == m_versionName; });
|
||||
|
||||
if (version_it == m_pack.versions.constEnd()) {
|
||||
if (versionIt == m_pack.versions.constEnd()) {
|
||||
emitFailed(tr("Failed to find pack version %1").arg(m_versionName));
|
||||
return;
|
||||
}
|
||||
|
||||
auto version = *version_it;
|
||||
const auto& version = *versionIt;
|
||||
|
||||
auto netJob = makeShared<NetJob>("FTB::VersionFetch", APPLICATION->network());
|
||||
|
||||
|
|
@ -112,10 +117,10 @@ void PackInstallTask::onManifestDownloadSucceeded(QByteArray* responsePtr)
|
|||
QByteArray response = std::move(*responsePtr);
|
||||
m_net_job.reset();
|
||||
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument const doc = QJsonDocument::fromJson(response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from FTB at " << parseError.offset << " reason: " << parseError.errorString();
|
||||
qWarning() << response;
|
||||
return;
|
||||
}
|
||||
|
|
@ -179,24 +184,25 @@ void PackInstallTask::onResolveModsSucceeded()
|
|||
|
||||
Flame::Manifest results = m_modIdResolverTask->getResults();
|
||||
for (int index = 0; index < m_fileIds.size(); index++) {
|
||||
const auto file_id = m_fileIds.at(index);
|
||||
if (file_id < 0)
|
||||
const auto fileId = m_fileIds.at(index);
|
||||
if (fileId < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Flame::File resultsFile = results.files[file_id];
|
||||
Flame::File const resultsFile = results.files.value(fileId);
|
||||
VersionFile& localFile = m_version.files[index];
|
||||
|
||||
// First check for blocked mods
|
||||
if (resultsFile.version.downloadUrl.isEmpty()) {
|
||||
BlockedMod blocked_mod;
|
||||
blocked_mod.name = resultsFile.version.fileName;
|
||||
blocked_mod.websiteUrl = QString("%1/download/%2").arg(resultsFile.pack.websiteUrl, QString::number(resultsFile.fileId));
|
||||
blocked_mod.hash = resultsFile.version.hash;
|
||||
blocked_mod.matched = false;
|
||||
blocked_mod.localPath = "";
|
||||
blocked_mod.targetFolder = resultsFile.targetFolder;
|
||||
BlockedMod blockedMod;
|
||||
blockedMod.name = resultsFile.version.fileName;
|
||||
blockedMod.websiteUrl = QString("%1/download/%2").arg(resultsFile.pack.websiteUrl, QString::number(resultsFile.fileId));
|
||||
blockedMod.hash = resultsFile.version.hash;
|
||||
blockedMod.matched = false;
|
||||
blockedMod.localPath = "";
|
||||
blockedMod.targetFolder = resultsFile.targetFolder;
|
||||
|
||||
m_blockedMods.append(blocked_mod);
|
||||
m_blockedMods.append(blockedMod);
|
||||
|
||||
anyBlocked = true;
|
||||
} else {
|
||||
|
|
@ -209,14 +215,14 @@ void PackInstallTask::onResolveModsSucceeded()
|
|||
if (anyBlocked) {
|
||||
qDebug() << "Blocked files found, displaying file list";
|
||||
|
||||
BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"),
|
||||
tr("The following files are not available for download in third party launchers.<br/>"
|
||||
"You will need to manually download them and add them to the instance."),
|
||||
m_blockedMods);
|
||||
BlockedModsDialog messageDialog(m_parent, tr("Blocked files found"),
|
||||
tr("The following files are not available for download in third party launchers.<br/>"
|
||||
"You will need to manually download them and add them to the instance."),
|
||||
m_blockedMods);
|
||||
|
||||
message_dialog.setModal(true);
|
||||
messageDialog.setModal(true);
|
||||
|
||||
if (message_dialog.exec() == QDialog::Accepted) {
|
||||
if (messageDialog.exec() == QDialog::Accepted) {
|
||||
qDebug() << "Post dialog blocked mods list: " << m_blockedMods;
|
||||
createInstance();
|
||||
} else {
|
||||
|
|
@ -238,20 +244,21 @@ void PackInstallTask::createInstance()
|
|||
auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_unique<INISettingsObject>(instanceConfigPath);
|
||||
|
||||
MinecraftInstance instance(m_globalSettings, std::move(instanceSettings), m_stagingPath);
|
||||
auto components = instance.getPackProfile();
|
||||
m_instance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath);
|
||||
auto* components = m_instance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
|
||||
for (auto target : m_version.targets) {
|
||||
for (const auto& target : m_version.targets) {
|
||||
if (target.type == "game" && target.name == "minecraft") {
|
||||
components->setComponentVersion("net.minecraft", target.version, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto target : m_version.targets) {
|
||||
if (target.type != "modloader")
|
||||
for (const auto& target : m_version.targets) {
|
||||
if (target.type != "modloader") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (target.name == "forge") {
|
||||
components->setComponentVersion("net.minecraftforge", target.version);
|
||||
|
|
@ -278,11 +285,11 @@ void PackInstallTask::createInstance()
|
|||
|
||||
components->saveNow();
|
||||
|
||||
instance.setName(name());
|
||||
instance.setIconKey(m_instIcon);
|
||||
instance.setManagedPack("ftb", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name);
|
||||
m_instance->setName(name());
|
||||
m_instance->setIconKey(m_instIcon);
|
||||
m_instance->setManagedPack("ftb", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name);
|
||||
|
||||
instance.saveNow();
|
||||
m_instance->saveNow();
|
||||
|
||||
onCreateInstanceSucceeded();
|
||||
}
|
||||
|
|
@ -299,13 +306,14 @@ void PackInstallTask::downloadPack()
|
|||
|
||||
auto jobPtr = makeShared<NetJob>(tr("Mod download"), APPLICATION->network());
|
||||
for (const auto& file : m_version.files) {
|
||||
if (file.serverOnly || file.url.isEmpty())
|
||||
if (file.serverOnly || file.url.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path, file.name);
|
||||
qDebug() << "Will try to download" << file.url << "to" << path;
|
||||
|
||||
QFileInfo file_info(file.name);
|
||||
QFileInfo const fileInfo(file.name);
|
||||
|
||||
auto dl = Net::Download::makeFile(file.url, path);
|
||||
if (!file.sha1.isEmpty()) {
|
||||
|
|
@ -333,27 +341,27 @@ void PackInstallTask::onModDownloadSucceeded()
|
|||
if (!m_blockedMods.isEmpty()) {
|
||||
copyBlockedMods();
|
||||
}
|
||||
emitSucceeded();
|
||||
downloadFiles(m_instance.get());
|
||||
}
|
||||
|
||||
void PackInstallTask::onManifestDownloadFailed(QString reason)
|
||||
{
|
||||
m_net_job.reset();
|
||||
emitFailed(reason);
|
||||
emitFailed(std::move(reason));
|
||||
}
|
||||
void PackInstallTask::onResolveModsFailed(QString reason)
|
||||
{
|
||||
m_net_job.reset();
|
||||
emitFailed(reason);
|
||||
emitFailed(std::move(reason));
|
||||
}
|
||||
void PackInstallTask::onCreateInstanceFailed(QString reason)
|
||||
{
|
||||
emitFailed(reason);
|
||||
emitFailed(std::move(reason));
|
||||
}
|
||||
void PackInstallTask::onModDownloadFailed(QString reason)
|
||||
{
|
||||
m_net_job.reset();
|
||||
emitFailed(reason);
|
||||
emitFailed(std::move(reason));
|
||||
}
|
||||
|
||||
/// @brief copy the matched blocked mods to the instance staging area
|
||||
|
|
@ -362,7 +370,7 @@ void PackInstallTask::copyBlockedMods()
|
|||
setStatus(tr("Copying Blocked Mods..."));
|
||||
setAbortable(false);
|
||||
int i = 0;
|
||||
int total = m_blockedMods.length();
|
||||
auto total = m_blockedMods.length();
|
||||
setProgress(i, total);
|
||||
for (const auto& mod : m_blockedMods) {
|
||||
if (!mod.matched) {
|
||||
|
|
@ -370,14 +378,14 @@ void PackInstallTask::copyBlockedMods()
|
|||
continue;
|
||||
}
|
||||
|
||||
auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name);
|
||||
auto destPath = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name);
|
||||
|
||||
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
|
||||
|
||||
qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
|
||||
qDebug() << "Will try to copy" << mod.localPath << "to" << destPath;
|
||||
|
||||
if (!FS::copy(mod.localPath, dest_path)()) {
|
||||
qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
|
||||
if (!FS::copy(mod.localPath, destPath)()) {
|
||||
qDebug() << "Copy of" << mod.localPath << "to" << destPath << "Failed";
|
||||
}
|
||||
|
||||
i++;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "ui/dialogs/BlockedModsDialog.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
namespace FTB {
|
||||
|
||||
|
|
@ -91,6 +92,8 @@ class PackInstallTask final : public InstanceTask {
|
|||
QMap<QString, QString> m_filesToCopy;
|
||||
QList<BlockedMod> m_blockedMods;
|
||||
|
||||
std::unique_ptr<MinecraftInstance> m_instance;
|
||||
|
||||
// FIXME: nuke
|
||||
QWidget* m_parent;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@
|
|||
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/import_ftb/PackHelpers.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
|
|
@ -51,19 +49,20 @@ void PackInstallTask::copySettings()
|
|||
progress(2, 2);
|
||||
|
||||
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
MinecraftInstance instance(m_globalSettings, std::make_unique<INISettingsObject>(instanceConfigPath), m_stagingPath);
|
||||
m_instance =
|
||||
std::make_unique<MinecraftInstance>(m_globalSettings, std::make_unique<INISettingsObject>(instanceConfigPath), m_stagingPath);
|
||||
|
||||
{
|
||||
SettingsObject::Lock lock(instance.settings());
|
||||
instance.settings()->set("InstanceType", "OneSix");
|
||||
instance.settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000);
|
||||
SettingsObject::Lock const lock(m_instance->settings());
|
||||
m_instance->settings()->set("InstanceType", "OneSix");
|
||||
m_instance->settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000);
|
||||
|
||||
if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
|
||||
instance.settings()->set("OverrideJavaArgs", true);
|
||||
instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString());
|
||||
m_instance->settings()->set("OverrideJavaArgs", true);
|
||||
m_instance->settings()->set("JvmArgs", m_pack.jvmArgs.toString());
|
||||
}
|
||||
|
||||
auto* components = instance.getPackProfile();
|
||||
auto* components = m_instance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
|
||||
|
||||
|
|
@ -92,13 +91,13 @@ void PackInstallTask::copySettings()
|
|||
}
|
||||
components->saveNow();
|
||||
|
||||
instance.setName(name());
|
||||
m_instance->setName(name());
|
||||
if (m_instIcon == "default") {
|
||||
m_instIcon = "ftb_logo";
|
||||
}
|
||||
instance.setIconKey(m_instIcon);
|
||||
m_instance->setIconKey(m_instIcon);
|
||||
}
|
||||
emitSucceeded();
|
||||
downloadFiles(m_instance.get());
|
||||
}
|
||||
|
||||
} // namespace FTBImportAPP
|
||||
|
|
|
|||
|
|
@ -21,8 +21,11 @@
|
|||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "InstanceTask.h"
|
||||
#include "PackHelpers.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
namespace FTBImportAPP {
|
||||
|
||||
|
|
@ -30,11 +33,11 @@ class PackInstallTask : public InstanceTask {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {}
|
||||
virtual ~PackInstallTask() = default;
|
||||
explicit PackInstallTask(Modpack pack) : m_pack(std::move(pack)) {}
|
||||
~PackInstallTask() override = default;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void copySettings();
|
||||
|
|
@ -43,6 +46,8 @@ class PackInstallTask : public InstanceTask {
|
|||
QFuture<bool> m_copyFuture;
|
||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||
|
||||
std::unique_ptr<MinecraftInstance> m_instance;
|
||||
|
||||
const Modpack m_pack;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "PackInstallTask.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
#include <utility>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
|
|
@ -52,12 +53,9 @@
|
|||
|
||||
namespace LegacyFTB {
|
||||
|
||||
PackInstallTask::PackInstallTask(QNetworkAccessManager* network, const Modpack& pack, QString version)
|
||||
{
|
||||
m_pack = pack;
|
||||
m_version = version;
|
||||
m_network = network;
|
||||
}
|
||||
PackInstallTask::PackInstallTask(QNetworkAccessManager* network, Modpack pack, QString version)
|
||||
: m_network(network), m_pack(std::move(pack)), m_version(std::move(version))
|
||||
{}
|
||||
|
||||
void PackInstallTask::executeTask()
|
||||
{
|
||||
|
|
@ -73,22 +71,22 @@ void PackInstallTask::downloadPack()
|
|||
auto path = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("FTBPacks", path);
|
||||
entry->setStale(true);
|
||||
archivePath = entry->getFullPath();
|
||||
netJobContainer.reset(new NetJob("Download FTB Pack", m_network));
|
||||
m_archivePath = entry->getFullPath();
|
||||
m_netJobContainer.reset(new NetJob("Download FTB Pack", m_network));
|
||||
QString url;
|
||||
if (m_pack.type == PackType::Private) {
|
||||
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(path);
|
||||
} else {
|
||||
url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(path);
|
||||
}
|
||||
netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry));
|
||||
m_netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry));
|
||||
|
||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
||||
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
|
||||
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
|
||||
connect(m_netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
||||
connect(m_netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
||||
connect(m_netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
|
||||
connect(m_netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
|
||||
|
||||
netJobContainer->start();
|
||||
m_netJobContainer->start();
|
||||
|
||||
setAbortable(true);
|
||||
progress(1, 4);
|
||||
|
|
@ -102,7 +100,7 @@ void PackInstallTask::unzip()
|
|||
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath,
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), m_archivePath,
|
||||
extractDir.absolutePath() + "/unzip");
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
|
||||
|
|
@ -133,11 +131,12 @@ void PackInstallTask::install()
|
|||
}
|
||||
|
||||
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
MinecraftInstance instance(m_globalSettings, std::make_unique<INISettingsObject>(instanceConfigPath), m_stagingPath);
|
||||
m_instance =
|
||||
std::make_unique<MinecraftInstance>(m_globalSettings, std::make_unique<INISettingsObject>(instanceConfigPath), m_stagingPath);
|
||||
{
|
||||
SettingsObject::Lock lock(instance.settings());
|
||||
SettingsObject::Lock const lock(m_instance->settings());
|
||||
|
||||
auto components = instance.getPackProfile();
|
||||
auto* components = m_instance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
|
||||
|
||||
|
|
@ -177,7 +176,7 @@ void PackInstallTask::install()
|
|||
qDebug() << "Found jarmods, installing...";
|
||||
|
||||
QStringList jarmods;
|
||||
for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
|
||||
for (const auto& info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
|
||||
qDebug() << "Jarmod:" << info.fileName();
|
||||
jarmods.push_back(info.absoluteFilePath());
|
||||
}
|
||||
|
|
@ -199,14 +198,14 @@ void PackInstallTask::install()
|
|||
|
||||
progress(4, 4);
|
||||
|
||||
instance.setName(name());
|
||||
m_instance->setName(name());
|
||||
if (m_instIcon == "default") {
|
||||
m_instIcon = "ftb_logo";
|
||||
}
|
||||
instance.setIconKey(m_instIcon);
|
||||
m_instance->setIconKey(m_instIcon);
|
||||
}
|
||||
|
||||
emitSucceeded();
|
||||
downloadFiles(m_instance.get());
|
||||
}
|
||||
|
||||
bool PackInstallTask::abort()
|
||||
|
|
@ -215,7 +214,7 @@ bool PackInstallTask::abort()
|
|||
return false;
|
||||
}
|
||||
|
||||
netJobContainer->abort();
|
||||
m_netJobContainer->abort();
|
||||
return InstanceTask::abort();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
#pragma once
|
||||
#include "InstanceTask.h"
|
||||
#include "PackHelpers.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
|
@ -14,15 +13,15 @@ class PackInstallTask : public InstanceTask {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PackInstallTask(QNetworkAccessManager* network, const Modpack& pack, QString version);
|
||||
virtual ~PackInstallTask() {}
|
||||
explicit PackInstallTask(QNetworkAccessManager* network, Modpack pack, QString version);
|
||||
~PackInstallTask() override = default;
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
void executeTask() override;
|
||||
|
||||
private:
|
||||
void downloadPack();
|
||||
|
|
@ -36,11 +35,13 @@ class PackInstallTask : public InstanceTask {
|
|||
|
||||
private: /* data */
|
||||
QNetworkAccessManager* m_network;
|
||||
bool abortable = false;
|
||||
bool m_abortable = false;
|
||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||
NetJob::Ptr netJobContainer;
|
||||
QString archivePath;
|
||||
NetJob::Ptr m_netJobContainer;
|
||||
QString m_archivePath;
|
||||
|
||||
std::unique_ptr<MinecraftInstance> m_instance;
|
||||
|
||||
Modpack m_pack;
|
||||
QString m_version;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "net/ChecksumValidator.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ApiHeaderProxy.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
|
@ -39,10 +38,10 @@ bool ModrinthCreationTask::abort()
|
|||
if (m_task) {
|
||||
m_task->abort();
|
||||
}
|
||||
return InstanceCreationTask::abort();
|
||||
return InstanceTask::abort();
|
||||
}
|
||||
|
||||
bool ModrinthCreationTask::updateInstance()
|
||||
void ModrinthCreationTask::executeTask()
|
||||
{
|
||||
auto* instanceList = APPLICATION->instances();
|
||||
|
||||
|
|
@ -58,28 +57,30 @@ bool ModrinthCreationTask::updateInstance()
|
|||
inst = instanceList->getInstanceById(originalName());
|
||||
|
||||
if (!inst) {
|
||||
return false;
|
||||
createInstance();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
|
||||
if (!parseManifest(indexPath, m_files, true, false)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto versionName = inst->getManagedPackVersionName();
|
||||
m_root_path = QFileInfo(inst->gameRoot()).fileName();
|
||||
m_rootPath = QFileInfo(inst->gameRoot()).fileName();
|
||||
auto versionStr = !versionName.isEmpty() ? tr(" (version %1)").arg(versionName) : "";
|
||||
|
||||
if (shouldConfirmUpdate()) {
|
||||
auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr);
|
||||
if (shouldUpdate == ShouldUpdate::SkipUpdating) {
|
||||
return false;
|
||||
createInstance();
|
||||
return;
|
||||
}
|
||||
if (shouldUpdate == ShouldUpdate::Cancel) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,30 +148,28 @@ bool ModrinthCreationTask::updateInstance()
|
|||
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (dialog->exec() == QDialog::DialogCode::Rejected) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
emitAborted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setOverride(true, inst->id());
|
||||
qDebug() << "Will override instance!";
|
||||
|
||||
m_instance = inst;
|
||||
m_oldInstance = inst;
|
||||
|
||||
// We let it go through the createInstance() stage, just with a couple modifications for updating
|
||||
return false;
|
||||
createInstance();
|
||||
}
|
||||
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/
|
||||
std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
|
||||
void ModrinthCreationTask::createInstance()
|
||||
{
|
||||
QEventLoop loop;
|
||||
|
||||
QString parentFolder(FS::PathCombine(m_stagingPath, "mrpack"));
|
||||
|
||||
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
|
||||
if (m_files.empty() && !parseManifest(indexPath, m_files, true, true)) {
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep index file in case we need it some other time (like when changing versions)
|
||||
|
|
@ -178,7 +177,7 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
|
|||
FS::ensureFilePathExists(newIndexPlace);
|
||||
FS::move(indexPath, newIndexPlace);
|
||||
|
||||
auto mcPath = FS::PathCombine(m_stagingPath, m_root_path);
|
||||
auto mcPath = FS::PathCombine(m_stagingPath, m_rootPath);
|
||||
|
||||
auto overridePath = FS::PathCombine(m_stagingPath, "overrides");
|
||||
if (QFile::exists(overridePath)) {
|
||||
|
|
@ -187,8 +186,8 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
|
|||
|
||||
// Apply the overrides
|
||||
if (!FS::move(overridePath, mcPath)) {
|
||||
setError(tr("Could not rename the overrides folder:\n") + "overrides");
|
||||
return nullptr;
|
||||
emitFailed(tr("Could not rename the overrides folder:\n") + "overrides");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,85 +199,80 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
|
|||
|
||||
// Apply the overrides
|
||||
if (!FS::overrideFolder(mcPath, clientOverridePath)) {
|
||||
setError(tr("Could not rename the client overrides folder:\n") + "client overrides");
|
||||
return nullptr;
|
||||
emitFailed(tr("Could not rename the client overrides folder:\n") + "client overrides");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_unique<INISettingsObject>(configPath);
|
||||
auto instance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath);
|
||||
m_newInstance = std::make_unique<MinecraftInstance>(m_globalSettings, std::move(instanceSettings), m_stagingPath);
|
||||
|
||||
auto* components = instance->getPackProfile();
|
||||
auto* components = m_newInstance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_minecraft_version, true);
|
||||
components->setComponentVersion("net.minecraft", m_minecraftVersion, true);
|
||||
|
||||
QString loader;
|
||||
if (!m_fabric_version.isEmpty()) {
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version);
|
||||
if (!m_fabricVersion.isEmpty()) {
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", m_fabricVersion);
|
||||
loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Fabric);
|
||||
}
|
||||
if (!m_quilt_version.isEmpty()) {
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version);
|
||||
if (!m_quiltVersion.isEmpty()) {
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_quiltVersion);
|
||||
loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Quilt);
|
||||
}
|
||||
if (!m_forge_version.isEmpty()) {
|
||||
components->setComponentVersion("net.minecraftforge", m_forge_version);
|
||||
if (!m_forgeVersion.isEmpty()) {
|
||||
components->setComponentVersion("net.minecraftforge", m_forgeVersion);
|
||||
loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Forge);
|
||||
}
|
||||
if (!m_neoForge_version.isEmpty()) {
|
||||
components->setComponentVersion("net.neoforged", m_neoForge_version);
|
||||
if (!m_neoForgeVersion.isEmpty()) {
|
||||
components->setComponentVersion("net.neoforged", m_neoForgeVersion);
|
||||
loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::NeoForge);
|
||||
}
|
||||
|
||||
if (m_instIcon != "default") {
|
||||
instance->setIconKey(m_instIcon);
|
||||
} else if (!m_managed_id.isEmpty()) {
|
||||
instance->setIconKey("modrinth");
|
||||
m_newInstance->setIconKey(m_instIcon);
|
||||
} else if (!m_managedId.isEmpty()) {
|
||||
m_newInstance->setIconKey("modrinth");
|
||||
}
|
||||
|
||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||
if (!m_managed_id.isEmpty()) {
|
||||
instance->setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
|
||||
} else {
|
||||
instance->setManagedPack("modrinth", "", name(), "", "");
|
||||
}
|
||||
setManagedPack(m_newInstance.get());
|
||||
|
||||
instance->setName(name());
|
||||
instance->saveNow();
|
||||
m_newInstance->setName(name());
|
||||
m_newInstance->saveNow();
|
||||
|
||||
auto downloadMods = makeShared<NetJob>(tr("Mod Download Modrinth"), APPLICATION->network());
|
||||
|
||||
auto rootModpackPath = FS::PathCombine(m_stagingPath, m_root_path);
|
||||
auto rootModpackPath = FS::PathCombine(m_stagingPath, m_rootPath);
|
||||
auto rootModpackUrl = QUrl::fromLocalFile(rootModpackPath);
|
||||
// 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);
|
||||
auto filePath = FS::PathCombine(rootModpackPath, fileName);
|
||||
if (!rootModpackUrl.isParentOf(QUrl::fromLocalFile(filePath))) {
|
||||
// This means we somehow got out of the root folder, so abort here to prevent exploits
|
||||
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
|
||||
.arg(fileName));
|
||||
return nullptr;
|
||||
emitFailed(
|
||||
tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
|
||||
.arg(fileName));
|
||||
return;
|
||||
}
|
||||
if (fileName.startsWith("mods/")) {
|
||||
auto* mod = new Mod(filePath);
|
||||
ModDetails d;
|
||||
d.mod_id = filePath;
|
||||
mod->setDetails(d);
|
||||
resources[file.hash.toHex()] = mod;
|
||||
m_resources.insert(file.hash.toHex(), mod);
|
||||
}
|
||||
if (file.downloads.empty()) {
|
||||
setError(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName));
|
||||
return nullptr;
|
||||
emitFailed(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName));
|
||||
return;
|
||||
}
|
||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << filePath;
|
||||
|
||||
Net::ModrinthDownloadMeta meta{
|
||||
.reason = m_instance.has_value() ? "update" : "modpack",
|
||||
.gameVersion = m_minecraft_version,
|
||||
.reason = m_oldInstance.has_value() ? "update" : "modpack",
|
||||
.gameVersion = m_minecraftVersion,
|
||||
.loader = loader,
|
||||
};
|
||||
|
||||
|
|
@ -302,14 +296,9 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
|
|||
}
|
||||
}
|
||||
|
||||
bool endedWell = false;
|
||||
|
||||
connect(downloadMods.get(), &NetJob::succeeded, this, [&endedWell]() { endedWell = true; });
|
||||
connect(downloadMods.get(), &NetJob::failed, [this, &endedWell](const QString& reason) {
|
||||
endedWell = false;
|
||||
setError(reason);
|
||||
});
|
||||
connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
||||
connect(downloadMods.get(), &NetJob::succeeded, this, &ModrinthCreationTask::ensureMetaLoop);
|
||||
connect(downloadMods.get(), &NetJob::failed, this, &ModrinthCreationTask::emitFailed);
|
||||
connect(downloadMods.get(), &NetJob::aborted, this, &ModrinthCreationTask::emitAborted);
|
||||
connect(downloadMods.get(), &NetJob::progress, [this](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
setProgress(current, total);
|
||||
|
|
@ -319,57 +308,6 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
|
|||
setStatus(tr("Downloading mods..."));
|
||||
downloadMods->start();
|
||||
m_task = downloadMods;
|
||||
|
||||
loop.exec();
|
||||
|
||||
if (!endedWell) {
|
||||
for (auto* resource : resources) {
|
||||
delete resource;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QEventLoop ensureMetaLoop;
|
||||
QDir folder = FS::PathCombine(instance->modsRoot(), ".index");
|
||||
auto ensureMetadataTask = makeShared<EnsureMetadataTask>(resources, folder, ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(ensureMetadataTask.get(), &Task::succeeded, this, [&endedWell]() { endedWell = true; });
|
||||
connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit);
|
||||
connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
|
||||
|
||||
ensureMetadataTask->start();
|
||||
m_task = ensureMetadataTask;
|
||||
|
||||
ensureMetaLoop.exec();
|
||||
for (auto* resource : resources) {
|
||||
delete resource;
|
||||
}
|
||||
resources.clear();
|
||||
|
||||
// Update information of the already installed instance, if any.
|
||||
if (m_instance && endedWell) {
|
||||
setAbortable(false);
|
||||
auto* inst = m_instance.value();
|
||||
|
||||
// Only change the name if it didn't use a custom name, so that the previous custom name
|
||||
// is preserved, but if we're using the original one, we update the version string.
|
||||
// NOTE: This needs to come before the copyManagedPack call!
|
||||
if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance->name()) {
|
||||
if (askForChangingInstanceName(m_parent, inst->name(), instance->name()) == InstanceNameChange::ShouldChange) {
|
||||
inst->setName(instance->name());
|
||||
}
|
||||
}
|
||||
|
||||
inst->copyManagedPack(*instance);
|
||||
}
|
||||
|
||||
if (endedWell) {
|
||||
return instance;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector<File>& files, bool setInternalData, bool showOptionalDialog)
|
||||
|
|
@ -385,10 +323,10 @@ bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector<F
|
|||
}
|
||||
|
||||
if (setInternalData) {
|
||||
if (m_managed_version_id.isEmpty()) {
|
||||
m_managed_version_id = obj["versionId"].toString();
|
||||
if (m_managedVersionId.isEmpty()) {
|
||||
m_managedVersionId = obj.value("versionId").toString();
|
||||
}
|
||||
m_managed_name = obj["name"].toString();
|
||||
m_managedName = obj.value("name").toString();
|
||||
}
|
||||
|
||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||
|
|
@ -470,15 +408,15 @@ bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector<F
|
|||
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
||||
QString name = it.key();
|
||||
if (name == "minecraft") {
|
||||
m_minecraft_version = Json::requireString(*it, "Minecraft version");
|
||||
m_minecraftVersion = Json::requireString(*it, "Minecraft version");
|
||||
} else if (name == "fabric-loader") {
|
||||
m_fabric_version = Json::requireString(*it, "Fabric Loader version");
|
||||
m_fabricVersion = Json::requireString(*it, "Fabric Loader version");
|
||||
} else if (name == "quilt-loader") {
|
||||
m_quilt_version = Json::requireString(*it, "Quilt Loader version");
|
||||
m_quiltVersion = Json::requireString(*it, "Quilt Loader version");
|
||||
} else if (name == "forge") {
|
||||
m_forge_version = Json::requireString(*it, "Forge version");
|
||||
m_forgeVersion = Json::requireString(*it, "Forge version");
|
||||
} else if (name == "neoforge") {
|
||||
m_neoForge_version = Json::requireString(*it, "NeoForge version");
|
||||
m_neoForgeVersion = Json::requireString(*it, "NeoForge version");
|
||||
} else {
|
||||
throw JSONValidationError("Unknown dependency type: " + name);
|
||||
}
|
||||
|
|
@ -489,9 +427,91 @@ bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector<F
|
|||
}
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
setError(tr("Could not understand pack index:\n") + e.cause());
|
||||
emitFailed(tr("Could not understand pack index:\n") + e.cause());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModrinthCreationTask::ensureMetaLoop()
|
||||
{
|
||||
QDir const folder = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
||||
auto ensureMetadataTask = makeShared<EnsureMetadataTask>(m_resources, folder, ModPlatform::ResourceProvider::MODRINTH);
|
||||
connect(ensureMetadataTask.get(), &Task::succeeded, this, &ModrinthCreationTask::finishInstall);
|
||||
connect(ensureMetadataTask.get(), &Task::failed, this, &ModrinthCreationTask::emitFailed);
|
||||
connect(ensureMetadataTask.get(), &Task::aborted, this, &ModrinthCreationTask::emitAborted);
|
||||
connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
|
||||
|
||||
ensureMetadataTask->start();
|
||||
m_task = ensureMetadataTask;
|
||||
}
|
||||
|
||||
ModrinthCreationTask::~ModrinthCreationTask()
|
||||
{
|
||||
for (auto* resource : m_resources) {
|
||||
delete resource;
|
||||
}
|
||||
m_resources.clear();
|
||||
}
|
||||
|
||||
void ModrinthCreationTask::setManagedPack(BaseInstance* instance)
|
||||
{
|
||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||
if (!m_managedId.isEmpty()) {
|
||||
instance->setManagedPack("modrinth", m_managedId, m_managedName, m_managedVersionId, version());
|
||||
} else {
|
||||
instance->setManagedPack("modrinth", "", name(), "", "");
|
||||
}
|
||||
}
|
||||
|
||||
void ModrinthCreationTask::finishInstall()
|
||||
{
|
||||
// Update information of the already installed instance, if any.
|
||||
if (m_oldInstance) {
|
||||
setAbortable(false);
|
||||
auto* inst = *m_oldInstance;
|
||||
|
||||
// Only change the name if it didn't use a custom name, so that the previous custom name
|
||||
// is preserved, but if we're using the original one, we update the version string.
|
||||
// NOTE: This needs to come before the setManagedPack call!
|
||||
if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != name()) {
|
||||
if (askForChangingInstanceName(m_parent, inst->name(), name()) == InstanceNameChange::ShouldChange) {
|
||||
inst->setName(name());
|
||||
}
|
||||
}
|
||||
|
||||
setManagedPack(*m_oldInstance);
|
||||
}
|
||||
|
||||
if (shouldOverride()) {
|
||||
bool deleteFailed = false;
|
||||
|
||||
setAbortable(false);
|
||||
setStatus(tr("Removing old conflicting files..."));
|
||||
qDebug() << "Removing old files";
|
||||
|
||||
for (const QString& path : m_filesToRemove) {
|
||||
if (!QFile::exists(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "Removing" << path;
|
||||
|
||||
if (!QFile::remove(path)) {
|
||||
qCritical() << "Could not remove" << path;
|
||||
deleteFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFailed) {
|
||||
emitFailed(tr("Failed to remove old conflicting files."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
downloadFiles(m_newInstance.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@
|
|||
#include <QVector>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceCreationTask.h"
|
||||
#include "InstanceTask.h"
|
||||
|
||||
class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
class Resource;
|
||||
|
||||
class ModrinthCreationTask final : public InstanceTask {
|
||||
Q_OBJECT
|
||||
struct File {
|
||||
QString path;
|
||||
|
|
@ -30,32 +32,42 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
|||
QString id,
|
||||
QString versionId = {},
|
||||
QString originalInstanceId = {})
|
||||
: m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(versionId))
|
||||
: m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(versionId))
|
||||
{
|
||||
setStagingPath(stagingPath);
|
||||
setParentSettings(globalSettings);
|
||||
|
||||
m_original_instance_id = std::move(originalInstanceId);
|
||||
m_originalInstanceId = std::move(originalInstanceId);
|
||||
}
|
||||
~ModrinthCreationTask() override;
|
||||
|
||||
bool abort() override;
|
||||
|
||||
bool updateInstance() override;
|
||||
std::unique_ptr<MinecraftInstance> createInstance() override;
|
||||
void createInstance();
|
||||
void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void finishInstall();
|
||||
|
||||
private:
|
||||
bool parseManifest(const QString&, std::vector<File>&, bool setInternalData = true, bool showOptionalDialog = true);
|
||||
|
||||
void ensureMetaLoop();
|
||||
void setManagedPack(BaseInstance* instance);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
||||
QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version;
|
||||
QString m_managed_id, m_managed_version_id, m_managed_name;
|
||||
QString m_minecraftVersion, m_fabricVersion, m_quiltVersion, m_forgeVersion, m_neoForgeVersion;
|
||||
QString m_managedId, m_managedVersionId, m_managedName;
|
||||
|
||||
std::vector<File> m_files;
|
||||
Task::Ptr m_task;
|
||||
|
||||
std::optional<BaseInstance*> m_instance;
|
||||
std::optional<BaseInstance*> m_oldInstance;
|
||||
std::unique_ptr<MinecraftInstance> m_newInstance{};
|
||||
|
||||
QString m_root_path = "minecraft";
|
||||
QString m_rootPath = "minecraft";
|
||||
|
||||
QHash<QString, Resource*> m_resources;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@
|
|||
#include <tasks/Task.h>
|
||||
|
||||
#include "IconPickerDialog.h"
|
||||
#include "ProgressDialog.h"
|
||||
#include "VersionSelectDialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
|
|
@ -69,20 +68,19 @@
|
|||
|
||||
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
||||
const QString& url,
|
||||
const QMap<QString, QString>& extra_info,
|
||||
const QMap<QString, QString>& extraInfo,
|
||||
QWidget* parent)
|
||||
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
||||
: QDialog(parent), ui(new Ui::NewInstanceDialog), m_instIconKey("default")
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowIcon(QIcon::fromTheme("new"));
|
||||
|
||||
InstIconKey = "default";
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey));
|
||||
|
||||
QStringList groups = APPLICATION->instances()->getGroups();
|
||||
groups.prepend("");
|
||||
int index = groups.indexOf(initialGroup);
|
||||
auto index = groups.indexOf(initialGroup);
|
||||
if (index == -1) {
|
||||
index = 1;
|
||||
groups.insert(index, initialGroup);
|
||||
|
|
@ -102,35 +100,35 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
|||
ui->verticalLayout->insertWidget(2, m_container);
|
||||
|
||||
m_container->addButtons(m_buttons);
|
||||
connect(m_container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* selected) {
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(creationTask && !instName().isEmpty());
|
||||
connect(m_container, &PageContainer::selectedPageChanged, this, [this](BasePage* /*previous*/, BasePage* /*selected*/) {
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(m_creationTask && !instName().isEmpty());
|
||||
});
|
||||
|
||||
// Bonk Qt over its stupid head and make sure it understands which button is the default one...
|
||||
// See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button
|
||||
auto OkButton = m_buttons->button(QDialogButtonBox::Ok);
|
||||
OkButton->setDefault(true);
|
||||
OkButton->setAutoDefault(true);
|
||||
OkButton->setText(tr("OK"));
|
||||
connect(OkButton, &QPushButton::clicked, this, &NewInstanceDialog::accept);
|
||||
auto* okButton = m_buttons->button(QDialogButtonBox::Ok);
|
||||
okButton->setDefault(true);
|
||||
okButton->setAutoDefault(true);
|
||||
okButton->setText(tr("OK"));
|
||||
connect(okButton, &QPushButton::clicked, this, &NewInstanceDialog::accept);
|
||||
|
||||
auto CancelButton = m_buttons->button(QDialogButtonBox::Cancel);
|
||||
CancelButton->setDefault(false);
|
||||
CancelButton->setAutoDefault(false);
|
||||
CancelButton->setText(tr("Cancel"));
|
||||
connect(CancelButton, &QPushButton::clicked, this, &NewInstanceDialog::reject);
|
||||
auto* cancelButton = m_buttons->button(QDialogButtonBox::Cancel);
|
||||
cancelButton->setDefault(false);
|
||||
cancelButton->setAutoDefault(false);
|
||||
cancelButton->setText(tr("Cancel"));
|
||||
connect(cancelButton, &QPushButton::clicked, this, &NewInstanceDialog::reject);
|
||||
|
||||
auto HelpButton = m_buttons->button(QDialogButtonBox::Help);
|
||||
HelpButton->setDefault(false);
|
||||
HelpButton->setAutoDefault(false);
|
||||
HelpButton->setText(tr("Help"));
|
||||
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
||||
auto* helpButton = m_buttons->button(QDialogButtonBox::Help);
|
||||
helpButton->setDefault(false);
|
||||
helpButton->setAutoDefault(false);
|
||||
helpButton->setText(tr("Help"));
|
||||
connect(helpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
||||
|
||||
if (!url.isEmpty()) {
|
||||
QUrl actualUrl(url);
|
||||
m_container->selectPage("import");
|
||||
importPage->setUrl(url);
|
||||
importPage->setExtraInfo(extra_info);
|
||||
m_importPage->setUrl(url);
|
||||
m_importPage->setExtraInfo(extraInfo);
|
||||
}
|
||||
|
||||
updateDialogState();
|
||||
|
|
@ -138,7 +136,7 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
|||
if (APPLICATION->settings()->get("NewInstanceGeometry").isValid()) {
|
||||
restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toString().toUtf8()));
|
||||
} else {
|
||||
auto screen = parent->screen();
|
||||
auto* screen = parent->screen();
|
||||
auto geometry = screen->availableSize();
|
||||
resize(width(), qMin(geometry.height() - 50, 710));
|
||||
}
|
||||
|
|
@ -171,13 +169,14 @@ QList<BasePage*> NewInstanceDialog::getPages()
|
|||
{
|
||||
QList<BasePage*> pages;
|
||||
|
||||
importPage = new ImportPage(this);
|
||||
m_importPage = new ImportPage(this);
|
||||
|
||||
pages.append(new CustomPage(this));
|
||||
pages.append(importPage);
|
||||
pages.append(m_importPage);
|
||||
pages.append(new AtlPage(this));
|
||||
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
||||
if (APPLICATION->capabilities() & Application::SupportsFlame) {
|
||||
pages.append(new FlamePage(this));
|
||||
}
|
||||
pages.append(new FtbPage(this));
|
||||
pages.append(new LegacyFTB::Page(this));
|
||||
pages.append(new FTBImportAPP::ImportFTBPage(this));
|
||||
|
|
@ -199,41 +198,41 @@ NewInstanceDialog::~NewInstanceDialog()
|
|||
|
||||
void NewInstanceDialog::setSuggestedPack(const QString& name, InstanceTask* task)
|
||||
{
|
||||
creationTask.reset(task);
|
||||
m_creationTask.reset(task);
|
||||
|
||||
ui->instNameTextBox->setPlaceholderText(name);
|
||||
importVersion.clear();
|
||||
m_importVersion.clear();
|
||||
|
||||
if (!task) {
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
|
||||
importIcon = false;
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey));
|
||||
m_importIcon = false;
|
||||
}
|
||||
|
||||
auto allowOK = task && !instName().isEmpty();
|
||||
auto allowOK = (task != nullptr) && !instName().isEmpty();
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allowOK);
|
||||
}
|
||||
|
||||
void NewInstanceDialog::setSuggestedPack(const QString& name, QString version, InstanceTask* task)
|
||||
{
|
||||
creationTask.reset(task);
|
||||
m_creationTask.reset(task);
|
||||
|
||||
ui->instNameTextBox->setPlaceholderText(name);
|
||||
importVersion = std::move(version);
|
||||
m_importVersion = std::move(version);
|
||||
|
||||
if (!task) {
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
|
||||
importIcon = false;
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey));
|
||||
m_importIcon = false;
|
||||
}
|
||||
|
||||
auto allowOK = task && !instName().isEmpty();
|
||||
auto allowOK = (task != nullptr) && !instName().isEmpty();
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allowOK);
|
||||
}
|
||||
|
||||
void NewInstanceDialog::setSuggestedIconFromFile(const QString& path, const QString& name)
|
||||
{
|
||||
importIcon = true;
|
||||
importIconPath = path;
|
||||
importIconName = name;
|
||||
m_importIcon = true;
|
||||
m_importIconPath = path;
|
||||
m_importIconName = name;
|
||||
|
||||
// Hmm, for some reason they can be to small
|
||||
ui->iconButton->setIcon(QIcon(path));
|
||||
|
|
@ -241,22 +240,22 @@ void NewInstanceDialog::setSuggestedIconFromFile(const QString& path, const QStr
|
|||
|
||||
void NewInstanceDialog::setSuggestedIcon(const QString& key)
|
||||
{
|
||||
if (key == "default")
|
||||
if (key == "default") {
|
||||
return;
|
||||
}
|
||||
|
||||
auto icon = APPLICATION->icons()->getIcon(key);
|
||||
importIcon = false;
|
||||
m_importIcon = false;
|
||||
|
||||
ui->iconButton->setIcon(icon);
|
||||
}
|
||||
|
||||
InstanceTask* NewInstanceDialog::extractTask()
|
||||
{
|
||||
InstanceTask* extracted = creationTask.release();
|
||||
InstanceTask* extracted = m_creationTask.release();
|
||||
|
||||
InstanceName inst_name(ui->instNameTextBox->placeholderText().trimmed(), importVersion);
|
||||
inst_name.setName(ui->instNameTextBox->text().trimmed());
|
||||
extracted->setName(inst_name);
|
||||
extracted->setName(ui->instNameTextBox->text().trimmed());
|
||||
extracted->setOriginalName(ui->instNameTextBox->placeholderText().trimmed(), m_importVersion);
|
||||
|
||||
extracted->setGroup(instGroup());
|
||||
extracted->setIcon(iconKey());
|
||||
|
|
@ -265,21 +264,21 @@ InstanceTask* NewInstanceDialog::extractTask()
|
|||
|
||||
void NewInstanceDialog::updateDialogState()
|
||||
{
|
||||
auto allowOK = creationTask && !instName().isEmpty();
|
||||
auto OkButton = m_buttons->button(QDialogButtonBox::Ok);
|
||||
if (OkButton->isEnabled() != allowOK) {
|
||||
OkButton->setEnabled(allowOK);
|
||||
auto allowOK = m_creationTask && !instName().isEmpty();
|
||||
auto* okButton = m_buttons->button(QDialogButtonBox::Ok);
|
||||
if (okButton->isEnabled() != allowOK) {
|
||||
okButton->setEnabled(allowOK);
|
||||
}
|
||||
}
|
||||
|
||||
QString NewInstanceDialog::instName() const
|
||||
{
|
||||
auto result = ui->instNameTextBox->text().trimmed();
|
||||
if (result.size()) {
|
||||
if (result.size() != 0) {
|
||||
return result;
|
||||
}
|
||||
result = ui->instNameTextBox->placeholderText().trimmed();
|
||||
if (result.size()) {
|
||||
if (result.size() != 0) {
|
||||
return result;
|
||||
}
|
||||
return QString();
|
||||
|
|
@ -291,19 +290,19 @@ QString NewInstanceDialog::instGroup() const
|
|||
}
|
||||
QString NewInstanceDialog::iconKey() const
|
||||
{
|
||||
return InstIconKey;
|
||||
return m_instIconKey;
|
||||
}
|
||||
|
||||
void NewInstanceDialog::on_iconButton_clicked()
|
||||
{
|
||||
importIconNow(); // so the user can switch back
|
||||
IconPickerDialog dlg(this);
|
||||
dlg.execWithSelection(InstIconKey);
|
||||
dlg.execWithSelection(m_instIconKey);
|
||||
|
||||
if (dlg.result() == QDialog::Accepted) {
|
||||
InstIconKey = dlg.selectedIconKey;
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
|
||||
importIcon = false;
|
||||
m_instIconKey = dlg.selectedIconKey;
|
||||
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(m_instIconKey));
|
||||
m_importIcon = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -314,22 +313,22 @@ void NewInstanceDialog::on_instNameTextBox_textChanged([[maybe_unused]] const QS
|
|||
|
||||
void NewInstanceDialog::importIconNow()
|
||||
{
|
||||
if (importIcon) {
|
||||
APPLICATION->icons()->installIcon(importIconPath, importIconName);
|
||||
InstIconKey = importIconName.mid(0, importIconName.lastIndexOf('.'));
|
||||
importIcon = false;
|
||||
if (m_importIcon) {
|
||||
APPLICATION->icons()->installIcon(m_importIconPath, m_importIconName);
|
||||
m_instIconKey = m_importIconName.mid(0, m_importIconName.lastIndexOf('.'));
|
||||
m_importIcon = false;
|
||||
}
|
||||
APPLICATION->settings()->set("NewInstanceGeometry", QString::fromUtf8(saveGeometry().toBase64()));
|
||||
}
|
||||
|
||||
void NewInstanceDialog::selectedPageChanged(BasePage* previous, BasePage* selected)
|
||||
{
|
||||
auto prevPage = dynamic_cast<ModpackProviderBasePage*>(previous);
|
||||
auto* prevPage = dynamic_cast<ModpackProviderBasePage*>(previous);
|
||||
if (prevPage) {
|
||||
m_searchTerm = prevPage->getSerachTerm();
|
||||
}
|
||||
|
||||
auto nextPage = dynamic_cast<ModpackProviderBasePage*>(selected);
|
||||
auto* nextPage = dynamic_cast<ModpackProviderBasePage*>(selected);
|
||||
if (nextPage) {
|
||||
nextPage->setSearchTerm(m_searchTerm);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ class NewInstanceDialog : public QDialog, public BasePageProvider {
|
|||
public:
|
||||
explicit NewInstanceDialog(const QString& initialGroup,
|
||||
const QString& url = QString(),
|
||||
const QMap<QString, QString>& extra_info = {},
|
||||
QWidget* parent = 0);
|
||||
~NewInstanceDialog();
|
||||
const QMap<QString, QString>& extraInfo = {},
|
||||
QWidget* parent = nullptr);
|
||||
~NewInstanceDialog() override;
|
||||
|
||||
void updateDialogState();
|
||||
|
||||
|
|
@ -84,22 +84,23 @@ class NewInstanceDialog : public QDialog, public BasePageProvider {
|
|||
void on_instNameTextBox_textChanged(const QString& arg1);
|
||||
void selectedPageChanged(BasePage* previous, BasePage* selected);
|
||||
|
||||
private:
|
||||
void importIconNow();
|
||||
|
||||
private:
|
||||
Ui::NewInstanceDialog* ui = nullptr;
|
||||
PageContainer* m_container = nullptr;
|
||||
QDialogButtonBox* m_buttons = nullptr;
|
||||
|
||||
QString InstIconKey;
|
||||
ImportPage* importPage = nullptr;
|
||||
std::unique_ptr<InstanceTask> creationTask;
|
||||
QString m_instIconKey;
|
||||
ImportPage* m_importPage = nullptr;
|
||||
std::unique_ptr<InstanceTask> m_creationTask;
|
||||
|
||||
bool importIcon = false;
|
||||
QString importIconPath;
|
||||
QString importIconName;
|
||||
bool m_importIcon = false;
|
||||
QString m_importIconPath;
|
||||
QString m_importIconName;
|
||||
|
||||
QString importVersion;
|
||||
QString m_importVersion;
|
||||
|
||||
QString m_searchTerm;
|
||||
|
||||
void importIconNow();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@
|
|||
#include <memory>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "InstanceImportTask.h"
|
||||
#include "InstanceList.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "Json.h"
|
||||
#include "Markdown.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
|
|
@ -29,11 +27,10 @@
|
|||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
/** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen.
|
||||
* ... thanks Qt.
|
||||
*/
|
||||
namespace {
|
||||
class NoBigComboBoxStyle : public QProxyStyle {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
@ -41,8 +38,9 @@ class NoBigComboBoxStyle : public QProxyStyle {
|
|||
// clang-format off
|
||||
int styleHint(QStyle::StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const override
|
||||
{
|
||||
if (hint == QStyle::SH_ComboBox_Popup)
|
||||
return false;
|
||||
if (hint == QStyle::SH_ComboBox_Popup) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return QProxyStyle::styleHint(hint, option, widget, returnData);
|
||||
}
|
||||
|
|
@ -58,39 +56,42 @@ class NoBigComboBoxStyle : public QProxyStyle {
|
|||
public:
|
||||
static NoBigComboBoxStyle* getInstance(QStyle* style)
|
||||
{
|
||||
static QHash<QStyle*, NoBigComboBoxStyle*> s_singleton_instances_ = {};
|
||||
static std::mutex s_singleton_instances_mutex_;
|
||||
static QHash<QStyle*, NoBigComboBoxStyle*> s_singleton_instances = {};
|
||||
static std::mutex s_singleton_instances_mutex;
|
||||
|
||||
std::lock_guard<std::mutex> lock(s_singleton_instances_mutex_);
|
||||
auto inst_iter = s_singleton_instances_.constFind(style);
|
||||
std::scoped_lock const lock(s_singleton_instances_mutex);
|
||||
auto instIter = s_singleton_instances.constFind(style);
|
||||
NoBigComboBoxStyle* inst = nullptr;
|
||||
if (inst_iter == s_singleton_instances_.constEnd() || *inst_iter == nullptr) {
|
||||
if (instIter == s_singleton_instances.constEnd() || *instIter == nullptr) {
|
||||
inst = new NoBigComboBoxStyle(style);
|
||||
inst->setParent(APPLICATION);
|
||||
s_singleton_instances_.insert(style, inst);
|
||||
s_singleton_instances.insert(style, inst);
|
||||
qDebug() << "QProxyStyle NoBigComboBox created for" << style->objectName() << style;
|
||||
} else {
|
||||
inst = *inst_iter;
|
||||
inst = *instIter;
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
private:
|
||||
NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
|
||||
explicit NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
|
||||
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, const QString& type, QWidget* parent)
|
||||
{
|
||||
if (type == "modrinth")
|
||||
if (type == "modrinth") {
|
||||
return new ModrinthManagedPackPage(inst, nullptr, parent);
|
||||
if (type == "flame" && (APPLICATION->capabilities() & Application::SupportsFlame))
|
||||
}
|
||||
if (type == "flame" && ((APPLICATION->capabilities() & Application::SupportsFlame) != 0U)) {
|
||||
return new FlameManagedPackPage(inst, nullptr, parent);
|
||||
}
|
||||
|
||||
return new GenericManagedPackPage(inst, nullptr, parent);
|
||||
}
|
||||
|
||||
ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||
: QWidget(parent), m_instance_window(instance_window), ui(new Ui::ManagedPackPage), m_inst(inst)
|
||||
ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent)
|
||||
: QWidget(parent), m_instanceWindow(instanceWindow), ui(new Ui::ManagedPackPage), m_inst(inst)
|
||||
{
|
||||
Q_ASSERT(inst);
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi
|
|||
// NOTE: GTK2 themes crash with the proxy style.
|
||||
// This seems like an upstream bug, so there's not much else that can be done.
|
||||
if (!QStyleFactory::keys().contains("gtk2")) {
|
||||
auto comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style());
|
||||
auto* comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style());
|
||||
ui->versionsComboBox->setStyle(comboStyle);
|
||||
}
|
||||
|
||||
|
|
@ -115,20 +116,22 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi
|
|||
openedImpl();
|
||||
});
|
||||
|
||||
connect(ui->changelogTextBrowser, &QTextBrowser::anchorClicked, this, [](const QUrl url) {
|
||||
connect(ui->changelogTextBrowser, &QTextBrowser::anchorClicked, this, [](const QUrl& url) {
|
||||
if (url.scheme().isEmpty()) {
|
||||
auto querry =
|
||||
QUrlQuery(url.query()).queryItemValue("remoteUrl", QUrl::FullyDecoded); // curseforge workaround for linkout?remoteUrl=
|
||||
auto decoded = QUrl::fromPercentEncoding(querry.toUtf8());
|
||||
auto newUrl = QUrl(decoded);
|
||||
if (newUrl.isValid() && (newUrl.scheme() == "http" || newUrl.scheme() == "https"))
|
||||
if (newUrl.isValid() && (newUrl.scheme() == "http" || newUrl.scheme() == "https")) {
|
||||
QDesktopServices ::openUrl(newUrl);
|
||||
}
|
||||
return;
|
||||
}
|
||||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
connect(ui->urlLine, &QLineEdit::textChanged, this, [this](QString text) { m_inst->settings()->set("ManagedPackURL", text.trimmed()); });
|
||||
connect(ui->urlLine, &QLineEdit::textChanged, this,
|
||||
[this](const QString& text) { m_inst->settings()->set("ManagedPackURL", text.trimmed()); });
|
||||
}
|
||||
|
||||
ManagedPackPage::~ManagedPackPage()
|
||||
|
|
@ -169,10 +172,12 @@ void ManagedPackPage::openedImpl()
|
|||
QString ManagedPackPage::displayName() const
|
||||
{
|
||||
auto type = m_inst->getManagedPackType();
|
||||
if (type.isEmpty())
|
||||
if (type.isEmpty()) {
|
||||
return {};
|
||||
if (type == "flame")
|
||||
}
|
||||
if (type == "flame") {
|
||||
type = "CurseForge";
|
||||
}
|
||||
return type.replace(0, 1, type[0].toUpper());
|
||||
}
|
||||
|
||||
|
|
@ -200,26 +205,26 @@ bool ManagedPackPage::runUpdateTask(InstanceTask* task)
|
|||
{
|
||||
Q_ASSERT(task);
|
||||
|
||||
unique_qobject_ptr<Task> wrapped_task(APPLICATION->instances()->wrapInstanceTask(task));
|
||||
unique_qobject_ptr<Task> const wrappedTask(APPLICATION->instances()->wrapInstanceTask(task));
|
||||
|
||||
connect(wrapped_task.get(), &Task::failed,
|
||||
connect(wrappedTask.get(), &Task::failed,
|
||||
[this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||
connect(wrapped_task.get(), &Task::succeeded, [this, task]() {
|
||||
connect(wrappedTask.get(), &Task::succeeded, [this, task]() {
|
||||
QStringList warnings = task->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
}
|
||||
});
|
||||
connect(wrapped_task.get(), &Task::aborted, [this] {
|
||||
connect(wrappedTask.get(), &Task::aborted, [this] {
|
||||
CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information)
|
||||
->show();
|
||||
});
|
||||
|
||||
ProgressDialog loadDialog(this);
|
||||
loadDialog.setSkipButton(true, tr("Abort"));
|
||||
loadDialog.execWithTask(wrapped_task.get());
|
||||
loadDialog.execWithTask(wrappedTask.get());
|
||||
|
||||
return wrapped_task->wasSuccessful();
|
||||
return wrappedTask->wasSuccessful();
|
||||
}
|
||||
|
||||
void ManagedPackPage::suggestVersion()
|
||||
|
|
@ -246,8 +251,8 @@ void ManagedPackPage::setFailState()
|
|||
ui->reloadButton->setVisible(true);
|
||||
}
|
||||
|
||||
ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||
: ManagedPackPage(inst, instance_window, parent)
|
||||
ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent)
|
||||
: ManagedPackPage(inst, instanceWindow, parent)
|
||||
{
|
||||
Q_ASSERT(inst->isManagedPack());
|
||||
connect(ui->versionsComboBox, &QComboBox::currentIndexChanged, this, &ModrinthManagedPackPage::suggestVersion);
|
||||
|
|
@ -265,8 +270,8 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_fetch_job && m_fetch_job->isRunning()) {
|
||||
m_fetch_job->abort();
|
||||
if (m_fetchJob && m_fetchJob->isRunning()) {
|
||||
m_fetchJob->abort();
|
||||
}
|
||||
|
||||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion>> callbacks{};
|
||||
|
|
@ -300,16 +305,16 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||
};
|
||||
callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
|
||||
.mcVersions = {},
|
||||
.loaders = {},
|
||||
.resourceType = ModPlatform::ResourceType::Modpack,
|
||||
.includeChangelog = true },
|
||||
std::move(callbacks));
|
||||
m_fetchJob = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
|
||||
.mcVersions = {},
|
||||
.loaders = {},
|
||||
.resourceType = ModPlatform::ResourceType::Modpack,
|
||||
.includeChangelog = true },
|
||||
std::move(callbacks));
|
||||
|
||||
ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
|
||||
|
||||
m_fetch_job->start();
|
||||
m_fetchJob->start();
|
||||
}
|
||||
|
||||
QString ModrinthManagedPackPage::url() const
|
||||
|
|
@ -334,12 +339,13 @@ void ModrinthManagedPackPage::suggestVersion()
|
|||
/// @brief Called when the update task has completed.
|
||||
/// Internally handles the closing of the instance window if the update was successful and shows a message box.
|
||||
/// @param did_succeed Whether the update task was successful.
|
||||
void ManagedPackPage::onUpdateTaskCompleted(bool did_succeed) const
|
||||
void ManagedPackPage::onUpdateTaskCompleted(bool didSucceed) const
|
||||
{
|
||||
// Close the window if the update was successful
|
||||
if (did_succeed) {
|
||||
if (m_instance_window != nullptr)
|
||||
m_instance_window->close();
|
||||
if (didSucceed) {
|
||||
if (m_instanceWindow != nullptr) {
|
||||
m_instanceWindow->close();
|
||||
}
|
||||
|
||||
CustomMessageBox::selectable(nullptr, tr("Update Successful"),
|
||||
tr("The instance updated to pack version %1 successfully.").arg(m_inst->getManagedPackVersionName()),
|
||||
|
|
@ -375,15 +381,16 @@ void ModrinthManagedPackPage::update()
|
|||
void ModrinthManagedPackPage::updateFromFile()
|
||||
{
|
||||
auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), tr("Modrinth pack") + " (*.mrpack *.zip)");
|
||||
if (output.isEmpty())
|
||||
if (output.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePack(output);
|
||||
}
|
||||
|
||||
// FLAME
|
||||
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||
: ManagedPackPage(inst, instance_window, parent)
|
||||
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent)
|
||||
: ManagedPackPage(inst, instanceWindow, parent)
|
||||
{
|
||||
Q_ASSERT(inst->isManagedPack());
|
||||
connect(ui->versionsComboBox, &QComboBox::currentIndexChanged, this, &FlameManagedPackPage::suggestVersion);
|
||||
|
|
@ -418,8 +425,8 @@ void FlameManagedPackPage::parseManagedPack()
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_fetch_job && m_fetch_job->isRunning()) {
|
||||
m_fetch_job->abort();
|
||||
if (m_fetchJob && m_fetchJob->isRunning()) {
|
||||
m_fetchJob->abort();
|
||||
}
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
|
|
@ -453,14 +460,14 @@ void FlameManagedPackPage::parseManagedPack()
|
|||
};
|
||||
callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
|
||||
.mcVersions = {},
|
||||
.loaders = {},
|
||||
.resourceType = ModPlatform::ResourceType::Modpack,
|
||||
.includeChangelog = true },
|
||||
std::move(callbacks));
|
||||
m_fetchJob = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
|
||||
.mcVersions = {},
|
||||
.loaders = {},
|
||||
.resourceType = ModPlatform::ResourceType::Modpack,
|
||||
.includeChangelog = true },
|
||||
std::move(callbacks));
|
||||
|
||||
m_fetch_job->start();
|
||||
m_fetchJob->start();
|
||||
}
|
||||
|
||||
QString FlameManagedPackPage::url() const
|
||||
|
|
@ -504,36 +511,36 @@ void FlameManagedPackPage::update()
|
|||
void FlameManagedPackPage::updateFromFile()
|
||||
{
|
||||
auto output = QFileDialog::getOpenFileUrl(this, tr("Choose update file"), QDir::homePath(), tr("CurseForge pack") + " (*.zip)");
|
||||
if (output.isEmpty())
|
||||
if (output.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePack(output);
|
||||
}
|
||||
|
||||
void ManagedPackPage::updatePack(const QUrl& url, QString versionID, QString versionName)
|
||||
void ManagedPackPage::updatePack(const QUrl& url, const QString& versionID, const QString& versionName)
|
||||
{
|
||||
QMap<QString, QString> extra_info;
|
||||
QMap<QString, QString> extraInfo;
|
||||
// NOTE: Don't use 'm_pack.id' here, since we didn't completely parse all the metadata for the pack, including this field.
|
||||
extra_info.insert("pack_id", m_inst->getManagedPackID());
|
||||
extra_info.insert("pack_version_id", versionID);
|
||||
extra_info.insert("original_instance_id", m_inst->id());
|
||||
extraInfo.insert("pack_id", m_inst->getManagedPackID());
|
||||
extraInfo.insert("pack_version_id", versionID);
|
||||
extraInfo.insert("original_instance_id", m_inst->id());
|
||||
|
||||
auto extracted = new InstanceImportTask(url, this, std::move(extra_info));
|
||||
auto* extracted = new InstanceImportTask(url, this, std::move(extraInfo));
|
||||
|
||||
if (versionName.isEmpty()) {
|
||||
extracted->setName(m_inst->name());
|
||||
} else {
|
||||
InstanceName inst_name(m_inst->getManagedPackName(), versionName);
|
||||
inst_name.setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), versionName));
|
||||
extracted->setName(inst_name);
|
||||
extracted->setOriginalName(m_inst->getManagedPackName(), versionName);
|
||||
extracted->setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), versionName));
|
||||
}
|
||||
extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
|
||||
extracted->setIcon(m_inst->iconKey());
|
||||
extracted->setConfirmUpdate(false);
|
||||
|
||||
// Run our task then handle the result
|
||||
auto did_succeed = runUpdateTask(extracted);
|
||||
onUpdateTaskCompleted(did_succeed);
|
||||
auto didSucceed = runUpdateTask(extracted);
|
||||
onUpdateTaskCompleted(didSucceed);
|
||||
}
|
||||
|
||||
#include "ManagedPackPage.moc"
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "ui/pages/BasePage.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
|
@ -28,12 +26,12 @@ class ManagedPackPage : public QWidget, public BasePage {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline static ManagedPackPage* createPage(BaseInstance* inst, QWidget* parent = nullptr)
|
||||
static ManagedPackPage* createPage(BaseInstance* inst, QWidget* parent = nullptr)
|
||||
{
|
||||
return ManagedPackPage::createPage(inst, inst->getManagedPackType(), parent);
|
||||
}
|
||||
|
||||
static ManagedPackPage* createPage(BaseInstance* inst, QString type, QWidget* parent = nullptr);
|
||||
static ManagedPackPage* createPage(BaseInstance* inst, const QString& type, QWidget* parent = nullptr);
|
||||
~ManagedPackPage() override;
|
||||
|
||||
QString displayName() const override;
|
||||
|
|
@ -56,7 +54,7 @@ class ManagedPackPage : public QWidget, public BasePage {
|
|||
*/
|
||||
virtual QString url() const { return {}; };
|
||||
|
||||
void setInstanceWindow(InstanceWindow* window) { m_instance_window = window; }
|
||||
void setInstanceWindow(InstanceWindow* window) { m_instanceWindow = window; }
|
||||
|
||||
public slots:
|
||||
/** Gets the current version selection and update the UI, including the update button and the changelog.
|
||||
|
|
@ -77,7 +75,7 @@ class ManagedPackPage : public QWidget, public BasePage {
|
|||
void setFailState();
|
||||
|
||||
protected:
|
||||
ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
|
||||
ManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr);
|
||||
|
||||
/** Run the InstanceTask, with a progress dialog and all.
|
||||
* Similar to MainWindow::instanceFromInstanceTask
|
||||
|
|
@ -86,17 +84,17 @@ class ManagedPackPage : public QWidget, public BasePage {
|
|||
*/
|
||||
bool runUpdateTask(InstanceTask*);
|
||||
|
||||
void updatePack(const QUrl& url, QString versionID = {}, QString versionName = {});
|
||||
void updatePack(const QUrl& url, const QString& versionID = {}, const QString& versionName = {});
|
||||
|
||||
void onUpdateTaskCompleted(bool didSucceed) const;
|
||||
|
||||
protected:
|
||||
InstanceWindow* m_instance_window = nullptr;
|
||||
InstanceWindow* m_instanceWindow = nullptr;
|
||||
|
||||
Ui::ManagedPackPage* ui;
|
||||
BaseInstance* m_inst;
|
||||
|
||||
bool m_loaded = false;
|
||||
|
||||
void onUpdateTaskCompleted(bool did_succeed) const;
|
||||
};
|
||||
|
||||
/** Simple page for when we aren't a managed pack. */
|
||||
|
|
@ -104,8 +102,8 @@ class GenericManagedPackPage final : public ManagedPackPage {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr)
|
||||
: ManagedPackPage(inst, instance_window, parent)
|
||||
GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr)
|
||||
: ManagedPackPage(inst, instanceWindow, parent)
|
||||
{}
|
||||
~GenericManagedPackPage() override = default;
|
||||
|
||||
|
|
@ -117,7 +115,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
|
||||
ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr);
|
||||
~ModrinthManagedPackPage() override = default;
|
||||
|
||||
void parseManagedPack() override;
|
||||
|
|
@ -131,7 +129,7 @@ class ModrinthManagedPackPage final : public ManagedPackPage {
|
|||
void updateFromFile() override;
|
||||
|
||||
private:
|
||||
Task::Ptr m_fetch_job = nullptr;
|
||||
Task::Ptr m_fetchJob = nullptr;
|
||||
|
||||
ModPlatform::IndexedPack m_pack;
|
||||
ModrinthAPI m_api;
|
||||
|
|
@ -141,7 +139,7 @@ class FlameManagedPackPage final : public ManagedPackPage {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
|
||||
FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instanceWindow, QWidget* parent = nullptr);
|
||||
~FlameManagedPackPage() override = default;
|
||||
|
||||
void parseManagedPack() override;
|
||||
|
|
@ -155,7 +153,7 @@ class FlameManagedPackPage final : public ManagedPackPage {
|
|||
void updateFromFile() override;
|
||||
|
||||
private:
|
||||
Task::Ptr m_fetch_job = nullptr;
|
||||
Task::Ptr m_fetchJob = nullptr;
|
||||
|
||||
ModPlatform::IndexedPack m_pack;
|
||||
FlameAPI m_api;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue