Merge pull request #32 from flowln/modpack_update_page
Closes https://github.com/PrismLauncher/PrismLauncher/issues/180 Closes https://github.com/PrismLauncher/PrismLauncher/issues/170
This commit is contained in:
commit
4a13d72997
@ -161,6 +161,8 @@ class Config {
|
||||
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
||||
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
||||
|
||||
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
|
||||
|
||||
QString versionString() const;
|
||||
/**
|
||||
* \brief Converts the Version to a string.
|
||||
|
@ -680,6 +680,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/instance/GameOptionsPage.h
|
||||
ui/pages/instance/VersionPage.cpp
|
||||
ui/pages/instance/VersionPage.h
|
||||
ui/pages/instance/ManagedPackPage.cpp
|
||||
ui/pages/instance/ManagedPackPage.h
|
||||
ui/pages/instance/TexturePackPage.h
|
||||
ui/pages/instance/ResourcePackPage.h
|
||||
ui/pages/instance/ShaderPackPage.h
|
||||
@ -919,6 +921,7 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/instance/OtherLogsPage.ui
|
||||
ui/pages/instance/InstanceSettingsPage.ui
|
||||
ui/pages/instance/VersionPage.ui
|
||||
ui/pages/instance/ManagedPackPage.ui
|
||||
ui/pages/instance/WorldListPage.ui
|
||||
ui/pages/instance/ScreenshotsPage.ui
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||
|
@ -55,11 +55,9 @@
|
||||
|
||||
#include <quazip/quazipdir.h>
|
||||
|
||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
|
||||
{
|
||||
m_sourceUrl = sourceUrl;
|
||||
m_parent = parent;
|
||||
}
|
||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
|
||||
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
|
||||
{}
|
||||
|
||||
bool InstanceImportTask::abort()
|
||||
{
|
||||
@ -259,14 +257,28 @@ void InstanceImportTask::extractAborted()
|
||||
|
||||
void InstanceImportTask::processFlame()
|
||||
{
|
||||
auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent);
|
||||
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 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();
|
||||
|
||||
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();
|
||||
|
||||
auto* inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
|
||||
inst_creation_task->setName(*this);
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride());
|
||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
@ -323,14 +335,29 @@ void InstanceImportTask::processMultiMC()
|
||||
|
||||
void InstanceImportTask::processModrinth()
|
||||
{
|
||||
auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, m_sourceUrl.toString());
|
||||
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();
|
||||
|
||||
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 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();
|
||||
|
||||
auto* inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
|
||||
inst_creation_task->setName(*this);
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride());
|
||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
|
@ -56,7 +56,7 @@ class InstanceImportTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
|
||||
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||
|
||||
bool abort() override;
|
||||
const QVector<Flame::File> &getBlockedFiles() const
|
||||
@ -101,6 +101,10 @@ private: /* data */
|
||||
Modrinth,
|
||||
} m_modpackType = ModpackType::Unknown;
|
||||
|
||||
// Extra info we might need, that's available before, but can't be derived from
|
||||
// the source URL / the resource it points to alone.
|
||||
QMap<QString, QString> m_extra_info;
|
||||
|
||||
//FIXME: nuke
|
||||
QWidget* m_parent;
|
||||
};
|
||||
|
@ -816,7 +816,7 @@ class InstanceStaging : public Task {
|
||||
void childSucceded()
|
||||
{
|
||||
unsigned sleepTime = backoff();
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, m_child->shouldOverride()))
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get()))
|
||||
{
|
||||
emitSucceeded();
|
||||
return;
|
||||
@ -880,25 +880,22 @@ QString InstanceList::getStagedInstancePath()
|
||||
return path;
|
||||
}
|
||||
|
||||
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, bool should_override)
|
||||
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting)
|
||||
{
|
||||
QDir dir;
|
||||
QString instID;
|
||||
InstancePtr inst;
|
||||
|
||||
auto should_override = commiting.shouldOverride();
|
||||
|
||||
if (should_override) {
|
||||
// This is to avoid problems when the instance folder gets manually renamed
|
||||
if ((inst = getInstanceByManagedName(instanceName.originalName()))) {
|
||||
instID = QFileInfo(inst->instanceRoot()).fileName();
|
||||
} else if ((inst = getInstanceByManagedName(instanceName.modifiedName()))) {
|
||||
instID = QFileInfo(inst->instanceRoot()).fileName();
|
||||
} else {
|
||||
instID = FS::RemoveInvalidFilenameChars(instanceName.modifiedName(), '-');
|
||||
}
|
||||
instID = commiting.originalInstanceID();
|
||||
} else {
|
||||
instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir);
|
||||
}
|
||||
|
||||
Q_ASSERT(!instID.isEmpty());
|
||||
|
||||
{
|
||||
WatchLock lock(m_watcher, m_instDir);
|
||||
QString destination = FS::PathCombine(m_instDir, instID);
|
||||
|
@ -133,7 +133,7 @@ public:
|
||||
* 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, const QString& groupName, bool should_override);
|
||||
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&);
|
||||
|
||||
/**
|
||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
#include "ui/pages/instance/LogPage.h"
|
||||
#include "ui/pages/instance/VersionPage.h"
|
||||
#include "ui/pages/instance/ManagedPackPage.h"
|
||||
#include "ui/pages/instance/ModFolderPage.h"
|
||||
#include "ui/pages/instance/ResourcePackPage.h"
|
||||
#include "ui/pages/instance/TexturePackPage.h"
|
||||
@ -33,6 +34,7 @@ public:
|
||||
values.append(new LogPage(inst));
|
||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||
values.append(new VersionPage(onesix.get()));
|
||||
values.append(ManagedPackPage::createPage(onesix.get()));
|
||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||
values.append(modsPage);
|
||||
|
@ -18,6 +18,29 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
|
||||
return InstanceNameChange::ShouldKeep;
|
||||
}
|
||||
|
||||
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
|
||||
{
|
||||
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),
|
||||
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
||||
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
|
||||
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
|
||||
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
|
||||
|
||||
info->exec();
|
||||
|
||||
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
||||
return ShouldUpdate::Update;
|
||||
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
||||
return ShouldUpdate::SkipUpdating;
|
||||
return ShouldUpdate::Cancel;
|
||||
|
||||
}
|
||||
|
||||
QString InstanceName::name() const
|
||||
{
|
||||
if (!m_modified_name.isEmpty())
|
||||
|
@ -6,6 +6,8 @@
|
||||
/* 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);
|
||||
|
||||
struct InstanceName {
|
||||
public:
|
||||
@ -42,10 +44,20 @@ class InstanceTask : public Task, public InstanceName {
|
||||
void setGroup(const QString& group) { m_instGroup = group; }
|
||||
QString group() const { return m_instGroup; }
|
||||
|
||||
[[nodiscard]] bool shouldConfirmUpdate() const { return m_confirm_update; }
|
||||
void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
|
||||
|
||||
bool shouldOverride() const { return m_override_existing; }
|
||||
|
||||
[[nodiscard]] QString originalInstanceID() const { return m_original_instance_id; };
|
||||
|
||||
protected:
|
||||
void setOverride(bool override) { m_override_existing = override; }
|
||||
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;
|
||||
}
|
||||
|
||||
protected: /* data */
|
||||
SettingsObjectPtr m_globalSettings;
|
||||
@ -54,4 +66,7 @@ class InstanceTask : public Task, public InstanceName {
|
||||
QString m_stagingPath;
|
||||
|
||||
bool m_override_existing = false;
|
||||
bool m_confirm_update = true;
|
||||
|
||||
QString m_original_instance_id;
|
||||
};
|
||||
|
@ -81,7 +81,12 @@ bool FlameCreationTask::updateInstance()
|
||||
auto instance_list = APPLICATION->instances();
|
||||
|
||||
// FIXME: How to handle situations when there's more than one install already for a given modpack?
|
||||
auto inst = instance_list->getInstanceByManagedName(originalName());
|
||||
InstancePtr inst;
|
||||
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
|
||||
inst = instance_list->getInstanceById(original_id);
|
||||
Q_ASSERT(inst);
|
||||
} else {
|
||||
inst = instance_list->getInstanceByManagedName(originalName());
|
||||
|
||||
if (!inst) {
|
||||
inst = instance_list->getInstanceById(originalName());
|
||||
@ -89,6 +94,7 @@ bool FlameCreationTask::updateInstance()
|
||||
if (!inst)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
|
||||
@ -102,26 +108,15 @@ bool FlameCreationTask::updateInstance()
|
||||
auto version_id = inst->getManagedPackVersionName();
|
||||
auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : "";
|
||||
|
||||
auto info = CustomMessageBox::selectable(
|
||||
m_parent, tr("Similar modpack was found!"),
|
||||
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(version_str),
|
||||
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
||||
info->setButtonText(QMessageBox::Ok, tr("Update existing instance"));
|
||||
info->setButtonText(QMessageBox::Abort, tr("Create new instance"));
|
||||
info->setButtonText(QMessageBox::Reset, tr("Cancel"));
|
||||
|
||||
info->exec();
|
||||
|
||||
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
||||
if (shouldConfirmUpdate()) {
|
||||
auto should_update = askIfShouldUpdate(m_parent, version_str);
|
||||
if (should_update == ShouldUpdate::SkipUpdating)
|
||||
return false;
|
||||
|
||||
if (info->clickedButton() == info->button(QMessageBox::Reset)) {
|
||||
if (should_update == ShouldUpdate::Cancel) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QDir old_inst_dir(inst->instanceRoot());
|
||||
|
||||
@ -244,7 +239,7 @@ bool FlameCreationTask::updateInstance()
|
||||
}
|
||||
}
|
||||
|
||||
setOverride(true);
|
||||
setOverride(true, inst->id());
|
||||
qDebug() << "Will override instance!";
|
||||
|
||||
m_instance = inst;
|
||||
@ -366,7 +361,7 @@ bool FlameCreationTask::createInstance()
|
||||
FS::deletePath(jarmodsPath);
|
||||
}
|
||||
|
||||
instance.setManagedPack("flame", {}, m_pack.name, {}, m_pack.version);
|
||||
instance.setManagedPack("flame", m_managed_id, m_pack.name, m_managed_version_id, m_pack.version);
|
||||
instance.setName(name());
|
||||
|
||||
m_mod_id_resolver = new Flame::FileResolvingTask(APPLICATION->network(), m_pack);
|
||||
@ -390,14 +385,6 @@ bool FlameCreationTask::createInstance()
|
||||
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())) {
|
||||
if (askForChangingInstanceName(m_parent, inst->name(), instance.name()) == InstanceNameChange::ShouldChange)
|
||||
inst->setName(instance.name());
|
||||
}
|
||||
|
||||
inst->copyManagedPack(instance);
|
||||
}
|
||||
|
||||
|
@ -51,11 +51,21 @@ class FlameCreationTask final : public InstanceCreationTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameCreationTask(const QString& staging_path, SettingsObjectPtr global_settings, QWidget* parent)
|
||||
: InstanceCreationTask(), m_parent(parent)
|
||||
FlameCreationTask(const QString& staging_path,
|
||||
SettingsObjectPtr global_settings,
|
||||
QWidget* parent,
|
||||
QString id,
|
||||
QString version_id,
|
||||
QString original_instance_id = {})
|
||||
: InstanceCreationTask()
|
||||
, m_parent(parent)
|
||||
, m_managed_id(std::move(id))
|
||||
, m_managed_version_id(std::move(version_id))
|
||||
{
|
||||
setStagingPath(staging_path);
|
||||
setParentSettings(global_settings);
|
||||
|
||||
m_original_instance_id = std::move(original_instance_id);
|
||||
}
|
||||
|
||||
bool abort() override;
|
||||
@ -78,5 +88,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
||||
NetJob* m_process_update_file_info_job = nullptr;
|
||||
NetJob::Ptr m_files_job = nullptr;
|
||||
|
||||
QString m_managed_id, m_managed_version_id;
|
||||
|
||||
std::optional<InstancePtr> m_instance;
|
||||
};
|
||||
|
@ -33,7 +33,12 @@ bool ModrinthCreationTask::updateInstance()
|
||||
auto instance_list = APPLICATION->instances();
|
||||
|
||||
// FIXME: How to handle situations when there's more than one install already for a given modpack?
|
||||
auto inst = instance_list->getInstanceByManagedName(originalName());
|
||||
InstancePtr inst;
|
||||
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
|
||||
inst = instance_list->getInstanceById(original_id);
|
||||
Q_ASSERT(inst);
|
||||
} else {
|
||||
inst = instance_list->getInstanceByManagedName(originalName());
|
||||
|
||||
if (!inst) {
|
||||
inst = instance_list->getInstanceById(originalName());
|
||||
@ -41,6 +46,7 @@ bool ModrinthCreationTask::updateInstance()
|
||||
if (!inst)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString index_path = FS::PathCombine(m_stagingPath, "modrinth.index.json");
|
||||
if (!parseManifest(index_path, m_files, true, false))
|
||||
@ -49,26 +55,15 @@ bool ModrinthCreationTask::updateInstance()
|
||||
auto version_name = inst->getManagedPackVersionName();
|
||||
auto version_str = !version_name.isEmpty() ? tr(" (version %1)").arg(version_name) : "";
|
||||
|
||||
auto info = CustomMessageBox::selectable(
|
||||
m_parent, tr("Similar modpack was found!"),
|
||||
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(version_str),
|
||||
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
||||
info->setButtonText(QMessageBox::Ok, tr("Create new instance"));
|
||||
info->setButtonText(QMessageBox::Abort, tr("Update existing instance"));
|
||||
info->setButtonText(QMessageBox::Reset, tr("Cancel"));
|
||||
|
||||
info->exec();
|
||||
|
||||
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
||||
if (shouldConfirmUpdate()) {
|
||||
auto should_update = askIfShouldUpdate(m_parent, version_str);
|
||||
if (should_update == ShouldUpdate::SkipUpdating)
|
||||
return false;
|
||||
|
||||
if (info->clickedButton() == info->button(QMessageBox::Reset)) {
|
||||
if (should_update == ShouldUpdate::Cancel) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove repeated files, we don't need to download them!
|
||||
QDir old_inst_dir(inst->instanceRoot());
|
||||
@ -149,7 +144,7 @@ bool ModrinthCreationTask::updateInstance()
|
||||
}
|
||||
|
||||
|
||||
setOverride(true);
|
||||
setOverride(true, inst->id());
|
||||
qDebug() << "Will override instance!";
|
||||
|
||||
m_instance = inst;
|
||||
@ -222,7 +217,7 @@ bool ModrinthCreationTask::createInstance()
|
||||
instance.setIconKey("modrinth");
|
||||
}
|
||||
|
||||
instance.setManagedPack("modrinth", getManagedPackID(), m_managed_name, m_managed_version_id, version());
|
||||
instance.setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version());
|
||||
instance.setName(name());
|
||||
instance.saveNow();
|
||||
|
||||
@ -295,6 +290,7 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
|
||||
}
|
||||
|
||||
if (set_managed_info) {
|
||||
if (m_managed_version_id.isEmpty())
|
||||
m_managed_version_id = Json::ensureString(obj, "versionId", {}, "Managed ID");
|
||||
m_managed_name = Json::ensureString(obj, "name", {}, "Managed Name");
|
||||
}
|
||||
@ -395,13 +391,3 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ModrinthCreationTask::getManagedPackID() const
|
||||
{
|
||||
if (!m_source_url.isEmpty()) {
|
||||
QRegularExpression regex(R"(data\/(.*)\/versions)");
|
||||
return regex.match(m_source_url).captured(1);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -14,11 +14,21 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthCreationTask(QString staging_path, SettingsObjectPtr global_settings, QWidget* parent, QString source_url = {})
|
||||
: InstanceCreationTask(), m_parent(parent), m_source_url(std::move(source_url))
|
||||
ModrinthCreationTask(QString staging_path,
|
||||
SettingsObjectPtr global_settings,
|
||||
QWidget* parent,
|
||||
QString id,
|
||||
QString version_id = {},
|
||||
QString original_instance_id = {})
|
||||
: InstanceCreationTask()
|
||||
, m_parent(parent)
|
||||
, m_managed_id(std::move(id))
|
||||
, m_managed_version_id(std::move(version_id))
|
||||
{
|
||||
setStagingPath(staging_path);
|
||||
setParentSettings(global_settings);
|
||||
|
||||
m_original_instance_id = std::move(original_instance_id);
|
||||
}
|
||||
|
||||
bool abort() override;
|
||||
@ -28,14 +38,12 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
|
||||
private:
|
||||
bool parseManifest(const QString&, std::vector<Modrinth::File>&, bool set_managed_info = true, bool show_optional_dialog = true);
|
||||
QString getManagedPackID() const;
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
||||
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
|
||||
QString m_managed_id, m_managed_version_id, m_managed_name;
|
||||
QString m_source_url;
|
||||
|
||||
std::vector<Modrinth::File> m_files;
|
||||
NetJob::Ptr m_files_job;
|
||||
|
@ -128,6 +128,7 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
|
||||
|
||||
file.name = Json::requireString(obj, "name");
|
||||
file.version = Json::requireString(obj, "version_number");
|
||||
file.changelog = Json::ensureString(obj, "changelog");
|
||||
|
||||
file.id = Json::requireString(obj, "id");
|
||||
file.project_id = Json::requireString(obj, "project_id");
|
||||
|
@ -80,6 +80,7 @@ struct ModpackExtra {
|
||||
struct ModpackVersion {
|
||||
QString name;
|
||||
QString version;
|
||||
QString changelog;
|
||||
|
||||
QString id;
|
||||
QString project_id;
|
||||
|
@ -132,6 +132,12 @@ InstanceWindow::InstanceWindow(InstancePtr instance, QWidget *parent)
|
||||
{
|
||||
connect(m_instance.get(), &BaseInstance::statusChanged, this, &InstanceWindow::on_instanceStatusChanged);
|
||||
}
|
||||
|
||||
// add ourself as the modpack page's instance window
|
||||
{
|
||||
static_cast<ManagedPackPage*>(m_container->getPage("managed_pack"))->setInstanceWindow(this);
|
||||
}
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
class BasePage;
|
||||
|
||||
class BasePageContainer
|
||||
{
|
||||
public:
|
||||
virtual ~BasePageContainer(){};
|
||||
virtual bool selectPage(QString pageId) = 0;
|
||||
virtual BasePage* getPage(QString pageId) { return nullptr; };
|
||||
virtual void refreshContainer() = 0;
|
||||
virtual bool requestClose() = 0;
|
||||
};
|
||||
|
432
launcher/ui/pages/instance/ManagedPackPage.cpp
Normal file
432
launcher/ui/pages/instance/ManagedPackPage.cpp
Normal file
@ -0,0 +1,432 @@
|
||||
// SPDX-FileCopyrightText: 2022 flow <flowlnlnln@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "ManagedPackPage.h"
|
||||
#include "ui_ManagedPackPage.h"
|
||||
|
||||
#include <QListView>
|
||||
#include <QProxyStyle>
|
||||
|
||||
#include <HoeDown.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "InstanceImportTask.h"
|
||||
#include "InstanceList.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "Json.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
|
||||
/** This is just to override the combo box popup behavior so that the combo box doesn't take the whole screen.
|
||||
* ... thanks Qt.
|
||||
*/
|
||||
class NoBigComboBoxStyle : public QProxyStyle {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
|
||||
|
||||
// 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;
|
||||
|
||||
return QProxyStyle::styleHint(hint, option, widget, returnData);
|
||||
}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
|
||||
{
|
||||
if (type == "modrinth")
|
||||
return new ModrinthManagedPackPage(inst, nullptr, parent);
|
||||
if (type == "flame" && (APPLICATION->capabilities() & Application::SupportsFlame))
|
||||
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)
|
||||
{
|
||||
Q_ASSERT(inst);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->versionsComboBox->setStyle(new NoBigComboBoxStyle(ui->versionsComboBox->style()));
|
||||
|
||||
ui->reloadButton->setVisible(false);
|
||||
connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){
|
||||
ui->reloadButton->setVisible(false);
|
||||
|
||||
m_loaded = false;
|
||||
// Pretend we're opening the page again
|
||||
openedImpl();
|
||||
});
|
||||
}
|
||||
|
||||
ManagedPackPage::~ManagedPackPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ManagedPackPage::openedImpl()
|
||||
{
|
||||
ui->packName->setText(m_inst->getManagedPackName());
|
||||
ui->packVersion->setText(m_inst->getManagedPackVersionName());
|
||||
ui->packOrigin->setText(tr("Website: <a href=%1>%2</a> | Pack ID: %3 | Version ID: %4")
|
||||
.arg(url(), displayName(), m_inst->getManagedPackID(), m_inst->getManagedPackVersionID()));
|
||||
|
||||
parseManagedPack();
|
||||
}
|
||||
|
||||
QString ManagedPackPage::displayName() const
|
||||
{
|
||||
auto type = m_inst->getManagedPackType();
|
||||
if (type.isEmpty())
|
||||
return {};
|
||||
if (type == "flame")
|
||||
type = "CurseForge";
|
||||
return type.replace(0, 1, type[0].toUpper());
|
||||
}
|
||||
|
||||
QIcon ManagedPackPage::icon() const
|
||||
{
|
||||
return APPLICATION->getThemedIcon(m_inst->getManagedPackType());
|
||||
}
|
||||
|
||||
QString ManagedPackPage::helpPage() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ManagedPackPage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
bool ManagedPackPage::shouldDisplay() const
|
||||
{
|
||||
return m_inst->isManagedPack();
|
||||
}
|
||||
|
||||
bool ManagedPackPage::runUpdateTask(InstanceTask* task)
|
||||
{
|
||||
Q_ASSERT(task);
|
||||
|
||||
unique_qobject_ptr<Task> wrapped_task(APPLICATION->instances()->wrapInstanceTask(task));
|
||||
|
||||
connect(task, &Task::failed,
|
||||
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||
connect(task, &Task::succeeded, [this, task]() {
|
||||
QStringList warnings = task->warnings();
|
||||
if (warnings.count())
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
|
||||
});
|
||||
connect(task, &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(task);
|
||||
|
||||
return task->wasSuccessful();
|
||||
}
|
||||
|
||||
void ManagedPackPage::suggestVersion()
|
||||
{
|
||||
ui->updateButton->setText(tr("Update pack"));
|
||||
ui->updateButton->setDisabled(false);
|
||||
}
|
||||
|
||||
void ManagedPackPage::setFailState()
|
||||
{
|
||||
qDebug() << "Setting fail state!";
|
||||
|
||||
// We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
|
||||
ui->versionsComboBox->blockSignals(true);
|
||||
ui->versionsComboBox->clear();
|
||||
ui->versionsComboBox->addItem(tr("Failed to search for available versions."), {});
|
||||
ui->versionsComboBox->blockSignals(false);
|
||||
|
||||
ui->changelogTextBrowser->setText(tr("Failed to request changelog data for this modpack."));
|
||||
|
||||
ui->updateButton->setText(tr("Cannot update!"));
|
||||
ui->updateButton->setDisabled(true);
|
||||
|
||||
ui->reloadButton->setVisible(true);
|
||||
}
|
||||
|
||||
ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||
: ManagedPackPage(inst, instance_window, parent)
|
||||
{
|
||||
Q_ASSERT(inst->isManagedPack());
|
||||
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
||||
connect(ui->updateButton, &QPushButton::pressed, this, &ModrinthManagedPackPage::update);
|
||||
}
|
||||
|
||||
// MODRINTH
|
||||
|
||||
void ModrinthManagedPackPage::parseManagedPack()
|
||||
{
|
||||
qDebug() << "Parsing Modrinth pack";
|
||||
|
||||
// No need for the extra work because we already have everything we need.
|
||||
if (m_loaded)
|
||||
return;
|
||||
|
||||
if (m_fetch_job && m_fetch_job->isRunning())
|
||||
m_fetch_job->abort();
|
||||
|
||||
m_fetch_job.reset(new NetJob(QString("Modrinth::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
|
||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get()));
|
||||
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
setFailState();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Modrinth::loadIndexedVersions(m_pack, doc);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading modrinth modpack version: " << e.cause();
|
||||
|
||||
setFailState();
|
||||
return;
|
||||
}
|
||||
|
||||
// We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
|
||||
ui->versionsComboBox->blockSignals(true);
|
||||
ui->versionsComboBox->clear();
|
||||
ui->versionsComboBox->blockSignals(false);
|
||||
|
||||
for (auto version : m_pack.versions) {
|
||||
QString name;
|
||||
|
||||
if (!version.name.contains(version.version))
|
||||
name = QString("%1 — %2").arg(version.name, version.version);
|
||||
else
|
||||
name = version.name;
|
||||
|
||||
// NOTE: the id from version isn't the same id in the modpack format spec...
|
||||
// e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index..............
|
||||
if (version.version == m_inst->getManagedPackVersionName())
|
||||
name.append(tr(" (Current)"));
|
||||
|
||||
ui->versionsComboBox->addItem(name, QVariant(version.id));
|
||||
}
|
||||
|
||||
suggestVersion();
|
||||
|
||||
m_loaded = true;
|
||||
});
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::failed, this, &ModrinthManagedPackPage::setFailState);
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::aborted, this, &ModrinthManagedPackPage::setFailState);
|
||||
|
||||
ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
|
||||
|
||||
m_fetch_job->start();
|
||||
}
|
||||
|
||||
QString ModrinthManagedPackPage::url() const
|
||||
{
|
||||
return "https://modrinth.com/mod/" + m_inst->getManagedPackID();
|
||||
}
|
||||
|
||||
void ModrinthManagedPackPage::suggestVersion()
|
||||
{
|
||||
auto index = ui->versionsComboBox->currentIndex();
|
||||
auto version = m_pack.versions.at(index);
|
||||
|
||||
HoeDown md_parser;
|
||||
ui->changelogTextBrowser->setHtml(md_parser.process(version.changelog.toUtf8()));
|
||||
|
||||
ManagedPackPage::suggestVersion();
|
||||
}
|
||||
|
||||
void ModrinthManagedPackPage::update()
|
||||
{
|
||||
auto index = ui->versionsComboBox->currentIndex();
|
||||
auto version = m_pack.versions.at(index);
|
||||
|
||||
QMap<QString, QString> extra_info;
|
||||
// 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", version.id);
|
||||
extra_info.insert("original_instance_id", m_inst->id());
|
||||
|
||||
auto extracted = new InstanceImportTask(version.download_url, this, std::move(extra_info));
|
||||
|
||||
InstanceName inst_name(m_inst->getManagedPackName(), version.version);
|
||||
inst_name.setName(m_inst->name().replace(m_inst->getManagedPackVersionName(), version.version));
|
||||
extracted->setName(inst_name);
|
||||
|
||||
extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
|
||||
extracted->setIcon(m_inst->iconKey());
|
||||
extracted->setConfirmUpdate(false);
|
||||
|
||||
auto did_succeed = runUpdateTask(extracted);
|
||||
|
||||
if (m_instance_window && did_succeed)
|
||||
m_instance_window->close();
|
||||
}
|
||||
|
||||
// FLAME
|
||||
|
||||
FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent)
|
||||
: ManagedPackPage(inst, instance_window, parent)
|
||||
{
|
||||
Q_ASSERT(inst->isManagedPack());
|
||||
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
||||
connect(ui->updateButton, &QPushButton::pressed, this, &FlameManagedPackPage::update);
|
||||
}
|
||||
|
||||
void FlameManagedPackPage::parseManagedPack()
|
||||
{
|
||||
qDebug() << "Parsing Flame pack";
|
||||
|
||||
// We need to tell the user to redownload the pack, since we didn't save the required info previously
|
||||
if (m_inst->getManagedPackID().isEmpty()) {
|
||||
setFailState();
|
||||
QString message =
|
||||
tr("<h1>Hey there!</h1>"
|
||||
"<h4>"
|
||||
"It seems like your Pack ID is null. This is because of a bug in older versions of the launcher.<br/>"
|
||||
"Unfortunately, we can't do the proper API requests without this information.<br/>"
|
||||
"<br/>"
|
||||
"So, in order for this feature to work, you will need to re-download the modpack from the built-in downloader.<br/>"
|
||||
"<br/>"
|
||||
"Don't worry though, it will ask you to update this instance instead, so you'll not lose this instance!"
|
||||
"</h4>");
|
||||
|
||||
ui->changelogTextBrowser->setHtml(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// No need for the extra work because we already have everything we need.
|
||||
if (m_loaded)
|
||||
return;
|
||||
|
||||
if (m_fetch_job && m_fetch_job->isRunning())
|
||||
m_fetch_job->abort();
|
||||
|
||||
m_fetch_job.reset(new NetJob(QString("Flame::PackVersions(%1)").arg(m_inst->getManagedPackName()), APPLICATION->network()));
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
|
||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get()));
|
||||
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
setFailState();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto obj = doc.object();
|
||||
auto data = Json::ensureArray(obj, "data");
|
||||
Flame::loadIndexedPackVersions(m_pack, data);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << *response;
|
||||
qWarning() << "Error while reading flame modpack version: " << e.cause();
|
||||
|
||||
setFailState();
|
||||
return;
|
||||
}
|
||||
|
||||
// We block signals here so that suggestVersion() doesn't get called, causing an assertion fail.
|
||||
ui->versionsComboBox->blockSignals(true);
|
||||
ui->versionsComboBox->clear();
|
||||
ui->versionsComboBox->blockSignals(false);
|
||||
|
||||
for (auto version : m_pack.versions) {
|
||||
QString name;
|
||||
|
||||
name = version.version;
|
||||
|
||||
if (version.fileId == m_inst->getManagedPackVersionID().toInt())
|
||||
name.append(tr(" (Current)"));
|
||||
|
||||
ui->versionsComboBox->addItem(name, QVariant(version.fileId));
|
||||
}
|
||||
|
||||
suggestVersion();
|
||||
|
||||
m_loaded = true;
|
||||
});
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::failed, this, &FlameManagedPackPage::setFailState);
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::aborted, this, &FlameManagedPackPage::setFailState);
|
||||
|
||||
m_fetch_job->start();
|
||||
}
|
||||
|
||||
QString FlameManagedPackPage::url() const
|
||||
{
|
||||
// FIXME: We should display the websiteUrl field, but this requires doing the API request first :(
|
||||
return {};
|
||||
}
|
||||
|
||||
void FlameManagedPackPage::suggestVersion()
|
||||
{
|
||||
auto index = ui->versionsComboBox->currentIndex();
|
||||
auto version = m_pack.versions.at(index);
|
||||
|
||||
ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId));
|
||||
|
||||
ManagedPackPage::suggestVersion();
|
||||
}
|
||||
|
||||
void FlameManagedPackPage::update()
|
||||
{
|
||||
auto index = ui->versionsComboBox->currentIndex();
|
||||
auto version = m_pack.versions.at(index);
|
||||
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", m_inst->getManagedPackID());
|
||||
extra_info.insert("pack_version_id", QString::number(version.fileId));
|
||||
extra_info.insert("original_instance_id", m_inst->id());
|
||||
|
||||
auto extracted = new InstanceImportTask(version.downloadUrl, this, std::move(extra_info));
|
||||
|
||||
extracted->setName(m_inst->name());
|
||||
extracted->setGroup(APPLICATION->instances()->getInstanceGroup(m_inst->id()));
|
||||
extracted->setIcon(m_inst->iconKey());
|
||||
extracted->setConfirmUpdate(false);
|
||||
|
||||
auto did_succeed = runUpdateTask(extracted);
|
||||
|
||||
if (m_instance_window && did_succeed)
|
||||
m_instance_window->close();
|
||||
}
|
||||
|
||||
#include "ManagedPackPage.moc"
|
152
launcher/ui/pages/instance/ManagedPackPage.h
Normal file
152
launcher/ui/pages/instance/ManagedPackPage.h
Normal file
@ -0,0 +1,152 @@
|
||||
// SPDX-FileCopyrightText: 2022 flow <flowlnlnln@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
||||
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlamePackIndex.h"
|
||||
|
||||
#include "ui/pages/BasePage.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ManagedPackPage;
|
||||
}
|
||||
|
||||
class InstanceTask;
|
||||
class InstanceWindow;
|
||||
|
||||
class ManagedPackPage : public QWidget, public BasePage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline 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);
|
||||
~ManagedPackPage() override;
|
||||
|
||||
[[nodiscard]] QString displayName() const override;
|
||||
[[nodiscard]] QIcon icon() const override;
|
||||
[[nodiscard]] QString helpPage() const override;
|
||||
[[nodiscard]] QString id() const override { return "managed_pack"; }
|
||||
[[nodiscard]] bool shouldDisplay() const override;
|
||||
|
||||
void openedImpl() override;
|
||||
|
||||
bool apply() override { return true; }
|
||||
void retranslate() override;
|
||||
|
||||
/** Gets the necessary information about the managed pack, such as
|
||||
* available versions*/
|
||||
virtual void parseManagedPack(){};
|
||||
|
||||
/** URL of the managed pack.
|
||||
* Not the version-specific one.
|
||||
*/
|
||||
[[nodiscard]] virtual QString url() const { return {}; };
|
||||
|
||||
void setInstanceWindow(InstanceWindow* window) { m_instance_window = window; }
|
||||
|
||||
public slots:
|
||||
/** Gets the current version selection and update the UI, including the update button and the changelog.
|
||||
*/
|
||||
virtual void suggestVersion();
|
||||
|
||||
virtual void update(){};
|
||||
|
||||
protected slots:
|
||||
/** Does the necessary UI changes for when something failed.
|
||||
*
|
||||
* This includes:
|
||||
* - Setting an appropriate text on the version selector to indicate a fail;
|
||||
* - Setting an appropriate text on the changelog text browser to indicate a fail;
|
||||
* - Disable the update button.
|
||||
*/
|
||||
void setFailState();
|
||||
|
||||
protected:
|
||||
ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
|
||||
|
||||
/** Run the InstanceTask, with a progress dialog and all.
|
||||
* Similar to MainWindow::instanceFromInstanceTask
|
||||
*
|
||||
* Returns whether the task was successful.
|
||||
*/
|
||||
bool runUpdateTask(InstanceTask*);
|
||||
|
||||
protected:
|
||||
InstanceWindow* m_instance_window = nullptr;
|
||||
|
||||
Ui::ManagedPackPage* ui;
|
||||
BaseInstance* m_inst;
|
||||
|
||||
bool m_loaded = false;
|
||||
};
|
||||
|
||||
/** Simple page for when we aren't a managed pack. */
|
||||
class GenericManagedPackPage final : public ManagedPackPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GenericManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr)
|
||||
: ManagedPackPage(inst, instance_window, parent)
|
||||
{}
|
||||
~GenericManagedPackPage() override = default;
|
||||
|
||||
// TODO: We may want to show this page with some useful info at some point.
|
||||
[[nodiscard]] bool shouldDisplay() const override { return false; };
|
||||
};
|
||||
|
||||
class ModrinthManagedPackPage final : public ManagedPackPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
|
||||
~ModrinthManagedPackPage() override = default;
|
||||
|
||||
void parseManagedPack() override;
|
||||
[[nodiscard]] QString url() const override;
|
||||
|
||||
public slots:
|
||||
void suggestVersion() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
NetJob::Ptr m_fetch_job = nullptr;
|
||||
|
||||
Modrinth::Modpack m_pack;
|
||||
ModrinthAPI m_api;
|
||||
};
|
||||
|
||||
class FlameManagedPackPage final : public ManagedPackPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameManagedPackPage(BaseInstance* inst, InstanceWindow* instance_window, QWidget* parent = nullptr);
|
||||
~FlameManagedPackPage() override = default;
|
||||
|
||||
void parseManagedPack() override;
|
||||
[[nodiscard]] QString url() const override;
|
||||
|
||||
public slots:
|
||||
void suggestVersion() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
NetJob::Ptr m_fetch_job = nullptr;
|
||||
|
||||
Flame::IndexedPack m_pack;
|
||||
FlameAPI m_api;
|
||||
};
|
193
launcher/ui/pages/instance/ManagedPackPage.ui
Normal file
193
launcher/ui/pages/instance/ManagedPackPage.ui
Normal file
@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ManagedPackPage</class>
|
||||
<widget class="QWidget" name="ManagedPackPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>731</width>
|
||||
<height>538</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="packInformationBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Pack information</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="packNameLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="packNameLabel">
|
||||
<property name="text">
|
||||
<string>Pack name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="packName">
|
||||
<property name="text">
|
||||
<string notr="true">placeholder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="packVersionLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="packVersionLabel">
|
||||
<property name="text">
|
||||
<string>Current version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="packVersion">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">placeholder</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="packOriginLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="packOriginLabel">
|
||||
<property name="text">
|
||||
<string>Provider information:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="packOrigin">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">placeholder</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="updateToVersionLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update to version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="versionsComboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="updateButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fetching versions...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="changelogBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Changelog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="changelogTextBrowser">
|
||||
<property name="placeholderText">
|
||||
<string>No changelog available for this version!</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="reloadButton">
|
||||
<property name="text">
|
||||
<string>Reload page</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -197,12 +197,18 @@ void FlamePage::suggestCurrent()
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedVersion.isEmpty() || selectedVersion == "-1") {
|
||||
if (m_selected_version_index == -1) {
|
||||
dialog->setSuggestedPack();
|
||||
return;
|
||||
}
|
||||
|
||||
dialog->setSuggestedPack(current.name, new InstanceImportTask(selectedVersion,this));
|
||||
auto version = current.versions.at(m_selected_version_index);
|
||||
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", QString::number(current.addonId));
|
||||
extra_info.insert("pack_version_id", QString::number(version.fileId));
|
||||
|
||||
dialog->setSuggestedPack(current.name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info)));
|
||||
QString editedLogoName;
|
||||
editedLogoName = "curseforge_" + current.logoName.section(".", 0, 0);
|
||||
listModel->getLogo(current.logoName, current.logoUrl,
|
||||
@ -211,11 +217,18 @@ void FlamePage::suggestCurrent()
|
||||
|
||||
void FlamePage::onVersionSelectionChanged(QString data)
|
||||
{
|
||||
if (data.isNull() || data.isEmpty()) {
|
||||
selectedVersion = "";
|
||||
bool is_blocked = false;
|
||||
ui->versionSelectionBox->currentData().toInt(&is_blocked);
|
||||
|
||||
if (data.isNull() || data.isEmpty() || is_blocked) {
|
||||
m_selected_version_index = -1;
|
||||
return;
|
||||
}
|
||||
selectedVersion = ui->versionSelectionBox->currentData().toString();
|
||||
|
||||
m_selected_version_index = ui->versionSelectionBox->currentIndex();
|
||||
|
||||
Q_ASSERT(current.versions.at(m_selected_version_index).downloadUrl == ui->versionSelectionBox->currentData().toString());
|
||||
|
||||
suggestCurrent();
|
||||
}
|
||||
|
||||
|
@ -99,5 +99,5 @@ private:
|
||||
Flame::ListModel* listModel = nullptr;
|
||||
Flame::IndexedPack current;
|
||||
|
||||
QString selectedVersion;
|
||||
int m_selected_version_index = -1;
|
||||
};
|
||||
|
@ -300,7 +300,11 @@ void ModrinthPage::suggestCurrent()
|
||||
|
||||
for (auto& ver : current.versions) {
|
||||
if (ver.id == selectedVersion) {
|
||||
dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this));
|
||||
QMap<QString, QString> extra_info;
|
||||
extra_info.insert("pack_id", current.id);
|
||||
extra_info.insert("pack_version_id", ver.id);
|
||||
|
||||
dialog->setSuggestedPack(current.name, ver.version, new InstanceImportTask(ver.download_url, this, std::move(extra_info)));
|
||||
auto iconName = current.iconName;
|
||||
m_model->getLogo(iconName, current.iconUrl.toString(),
|
||||
[this, iconName](QString logo) { dialog->setSuggestedIconFromFile(logo, iconName); });
|
||||
|
@ -130,6 +130,11 @@ bool PageContainer::selectPage(QString pageId)
|
||||
return false;
|
||||
}
|
||||
|
||||
BasePage* PageContainer::getPage(QString pageId)
|
||||
{
|
||||
return m_model->findPageEntryById(pageId);
|
||||
}
|
||||
|
||||
void PageContainer::refreshContainer()
|
||||
{
|
||||
m_proxyModel->invalidate();
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool selectPage(QString pageId) override;
|
||||
BasePage* getPage(QString pageId) override;
|
||||
|
||||
void refreshContainer() override;
|
||||
virtual void setParentContainer(BasePageContainer * container)
|
||||
|
Loading…
Reference in New Issue
Block a user