NOISSUE Add FTB App import page to new instance dialog

This commit is contained in:
Petr Mrázek 2022-05-28 22:42:09 +02:00
parent 399247a018
commit 41e5b3b628
9 changed files with 681 additions and 4 deletions

View File

@ -555,8 +555,7 @@ set(LOGIC_SOURCES
${META_SOURCES}
${ICONS_SOURCES}
${FTB_SOURCES}
${FLAME_SOURCES}
${MODPACKSCH_SOURCES}
${FTBA_SOURCES}
${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES}
${MODRINTH_SOURCES}
@ -721,6 +720,13 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/legacy_ftb/ListModel.h
ui/pages/modplatform/legacy_ftb/ListModel.cpp
ui/pages/modplatform/import_ftb/PackInstallTask.cpp
ui/pages/modplatform/import_ftb/PackInstallTask.h
ui/pages/modplatform/import_ftb/Model.cpp
ui/pages/modplatform/import_ftb/Model.h
ui/pages/modplatform/import_ftb/FTBAPage.cpp
ui/pages/modplatform/import_ftb/FTBAPage.h
ui/pages/modplatform/modrinth/ModrinthData.h
ui/pages/modplatform/modrinth/ModrinthModel.cpp
ui/pages/modplatform/modrinth/ModrinthModel.h
@ -848,6 +854,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/atlauncher/AtlPage.ui
ui/pages/modplatform/VanillaPage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/import_ftb/FTBAPage.ui
ui/pages/modplatform/ImportPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui

View File

@ -36,6 +36,7 @@
#include "ui/pages/modplatform/VanillaPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/import_ftb/FTBAPage.h"
#include "ui/pages/modplatform/ImportPage.h"
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
#include "ui/pages/modplatform/technic/TechnicPage.h"
@ -128,9 +129,10 @@ QList<BasePage *> NewInstanceDialog::getPages()
{
new VanillaPage(this),
importPage,
new AtlPage(this),
new LegacyFTB::Page(this),
new ModrinthPage(this),
new AtlPage(this),
new ImportFTB::FTBAPage(this),
new LegacyFTB::Page(this),
technicPage
};
}

View File

@ -0,0 +1,122 @@
/*
* Copyright 2022 Petr Mrázek
*
* This source is subject to the Microsoft Permissive License (MS-PL).
* Please see the COPYING.md file for more information.
*/
#include "FTBAPage.h"
#include "ui_FTBAPage.h"
#include "Application.h"
#include "ui/dialogs/NewInstanceDialog.h"
#include "PackInstallTask.h"
#include "Model.h"
namespace ImportFTB {
FTBAPage::FTBAPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), dialog(dialog), ui(new Ui::FTBAPage)
{
ui->setupUi(this);
packModel = new Model(this);
ui->packList->setModel(packModel);
ui->packList->setSortingEnabled(true);
ui->packList->header()->hide();
ui->packList->setIndentation(0);
ui->packList->setIconSize(QSize(42, 42));
connect(ui->packList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBAPage::onPublicPackSelectionChanged);
ui->packList->selectionModel()->reset();
currentList = ui->packList;
currentModpackInfo = ui->packDescription;
currentList->selectionModel()->reset();
QModelIndex idx = currentList->currentIndex();
if(idx.isValid())
{
auto pack = packModel->data(idx, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&pack);
}
else
{
onPackSelectionChanged();
}
}
FTBAPage::~FTBAPage()
{
delete ui;
}
bool FTBAPage::shouldDisplay() const
{
return true;
}
void FTBAPage::openedImpl()
{
if(!initialized)
{
packModel->reload();
initialized = true;
}
suggestCurrent();
}
void FTBAPage::suggestCurrent()
{
if(!isOpened)
{
return;
}
if(!selected)
{
dialog->setSuggestedPack();
return;
}
auto selectedPack = *selected;
dialog->setSuggestedPack(selectedPack.name, new PackInstallTask(selectedPack));
QString logoName = QString("ftb_%1").arg(selectedPack.id);
dialog->setSuggestedIconFromFile(selectedPack.iconPath, logoName);
}
void FTBAPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
Modpack selectedPack = packModel->data(now, Qt::UserRole).value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
void FTBAPage::onPackSelectionChanged(Modpack* pack)
{
if(pack)
{
currentModpackInfo->setHtml("Pack by <b>" + pack->authors.join(", ") + "</b>" + "<br>Minecraft " + pack->mcVersion + "<br>" + "<br>" + pack->description);
selected = *pack;
}
else
{
currentModpackInfo->setHtml("");
if(isOpened)
{
dialog->setSuggestedPack();
}
return;
}
suggestCurrent();
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2022 Petr Mrázek
*
* This source is subject to the Microsoft Permissive License (MS-PL).
* Please see the COPYING.md file for more information.
*/
#pragma once
#include <QWidget>
#include <QTreeView>
#include <QTextBrowser>
#include "ui/pages/BasePage.h"
#include <Application.h>
#include "tasks/Task.h"
#include "QObjectPtr.h"
#include "Model.h"
class NewInstanceDialog;
namespace ImportFTB {
namespace Ui
{
class FTBAPage;
}
class Model;
class FTBAPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit FTBAPage(NewInstanceDialog * dialog, QWidget *parent = 0);
virtual ~FTBAPage();
QString displayName() const override
{
return tr("FTBApp Import");
}
QIcon icon() const override
{
return APPLICATION->getThemedIcon("ftb_logo");
}
QString id() const override
{
return "import_ftb";
}
QString helpPage() const override
{
return "FTB-app";
}
bool shouldDisplay() const override;
void openedImpl() override;
private:
void suggestCurrent();
void onPackSelectionChanged(Modpack *pack = nullptr);
private slots:
void onPublicPackSelectionChanged(QModelIndex first, QModelIndex second);
private:
QTreeView* currentList = nullptr;
QTextBrowser* currentModpackInfo = nullptr;
bool initialized = false;
nonstd::optional<Modpack> selected;
Model* packModel = nullptr;
NewInstanceDialog* dialog = nullptr;
Ui::FTBAPage *ui = nullptr;
};
}

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2022 Petr Mrázek
This source is subject to the Microsoft Permissive License (MS-PL).
Please see the COPYING.md file for more information.
-->
<ui version="4.0">
<class>ImportFTB::FTBAPage</class>
<widget class="QWidget" name="ImportFTB::FTBAPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1461</width>
<height>1011</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="packList">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="packDescription"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,216 @@
/*
* Copyright 2022 Petr Mrázek
*
* This source is subject to the Microsoft Permissive License (MS-PL).
* Please see the COPYING.md file for more information.
*/
#include "Model.h"
#include "Application.h"
#include <MMCStrings.h>
#include <Version.h>
#include <QtMath>
#include <QLabel>
#include <RWStorage.h>
#include <BuildConfig.h>
#include <FileSystem.h>
#include <Json.h>
namespace ImportFTB {
Model::Model(QObject *parent) : QAbstractListModel(parent)
{
}
int Model::rowCount(const QModelIndex &parent) const
{
return modpacks.size();
}
int Model::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant Model::data(const QModelIndex &index, int role) const
{
int pos = index.row();
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
{
return QVariant();
}
Modpack pack = modpacks.at(pos);
if(role == Qt::DisplayRole)
{
return pack.name;
}
else if(role == Qt::DecorationRole)
{
return pack.icon;
}
else if(role == Qt::UserRole)
{
QVariant v;
v.setValue(pack);
return v;
}
return QVariant();
}
namespace {
QString getFTBAInstances() {
QByteArray data;
try
{
auto path = FS::PathCombine(QDir::homePath(), ".ftba/bin/settings.json");
data = FS::read(path);
}
catch (const Exception &e)
{
qWarning() << "Could not read FTBA settings file";
return QString();
}
try
{
auto document = Json::requireDocument(data);
auto object = Json::requireObject(document);
return Json::requireString(object, "instanceLocation");
}
catch (Json::JsonException & e)
{
qCritical() << "Could not read FTBA settings file as JSON: " << e.cause();
return QString();
}
}
/*
Reference from an FTBA file, as of 28.05.2022
{
"_private": false,
"uuid": "25850fc6-ec61-4bc6-8397-77e4497bf922",
"id": 95,
"art": "data:image/png;base64,...==",
"path": "/home/peterix/.ftba/instances/25850fc6-ec61-4bc6-8397-77e4497bf922",
"versionId": 2127,
"name": "FTB Presents Direwolf20 1.18",
"minMemory": 4096,
"recMemory": 6144,
"memory": 6144,
"version": "1.2.0",
"dir": "/home/peterix/.ftba/instances/25850fc6-ec61-4bc6-8397-77e4497bf922",
"authors": [
"FTB Team"
],
"description": "Direwolf's modded legacy started all the way back in 2011. With 5 modpacks from the FTB Team, dedicated to creating a simple yet unique form of kitchen-sink modpacks.\n\nThis new iteration of Direwolf's modpack is packed with hand-selected mods from Direwolf20 himself, the FTB Team have worked hard in forging a kitchen-sink styled modded Minecraft experience that just about anyone can play. \n\nFollow along with Direwolf on his YouTube series, learning with him as you venture out into new modded territory. Improve his designs as you build up more infrastructure and automate anything and everything. The world is yours, for you to do anything in.",
"mcVersion": "1.18.2",
"jvmArgs": "",
"embeddedJre": true,
"url": "",
"artUrl": "https://apps.modpacks.ch/modpacks/art/90/1024_1024.png",
"width": 1920,
"height": 1080,
"modLoader": "1.18.2-forge-40.1.20",
"isModified": false,
"isImport": false,
"cloudSaves": false,
"hasInstMods": false,
"installComplete": true,
"packType": 0,
"totalPlayTime": 0,
"lastPlayed": 1653168242
}
*/
bool parseModpackJson(const QByteArray& data, Modpack & out) {
try
{
auto document = Json::requireDocument(data);
auto object = Json::requireObject(document);
bool isInstalled = Json::requireBoolean(object, "installComplete");
if(!isInstalled) {
return false;
}
out.id = Json::requireInteger(object, "id");
out.name = Json::requireString(object, "name");
out.version = Json::requireString(object, "version");
out.description = Json::requireString(object, "description");
auto authorsArray = Json::requireArray(object, "authors");
for(auto author: authorsArray) {
out.authors.append(Json::requireString(author));
}
out.mcVersion = Json::requireString(object, "mcVersion");
out.modLoader = Json::requireString(object, "modLoader");
out.hasInstMods = Json::requireBoolean(object, "hasInstMods");
out.minMemory = Json::requireInteger(object, "minMemory");
out.recMemory = Json::requireInteger(object, "recMemory");
out.memory = Json::requireInteger(object, "memory");
return true;
}
catch (Json::JsonException & e)
{
qCritical() << "Could not read FTBA settings file as JSON: " << e.cause();
return false;
}
}
bool tryLoadInstance(const QString& location, Modpack & out) {
QFileInfo dirInfo(location);
if(dirInfo.isSymLink())
return false;
QByteArray data;
auto jsonLocation = FS::PathCombine(location, "instance.json");
try
{
data = FS::read(jsonLocation);
if(!parseModpackJson(data, out)) {
return false;
}
out.instanceDir = location;
out.iconPath = FS::PathCombine(location, "folder.jpg");
out.icon = QIcon(out.iconPath);
return true;
}
catch (const Exception &e)
{
qWarning() << "Could not read FTBA instance JSON file: " << jsonLocation;
return false;
}
return false;
}
}
void Model::reload() {
beginResetModel();
modpacks.clear();
QString instancesLocation = getFTBAInstances();
if(instancesLocation.isEmpty()) {
qDebug() << "FTB instances are not found...";
}
else {
qDebug() << "Discovering FTB instances in" << instancesLocation;
QDirIterator iter(instancesLocation, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
while (iter.hasNext()) {
QString subDir = iter.next();
ImportFTB::Modpack out;
if(!tryLoadInstance(subDir, out)) {
continue;
}
modpacks.append(out);
}
}
endResetModel();
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2022 Petr Mrázek
*
* This source is subject to the Microsoft Permissive License (MS-PL).
* Please see the COPYING.md file for more information.
*/
#pragma once
#include <RWStorage.h>
#include <QAbstractListModel>
#include <QIcon>
#include <QList>
#include <QString>
#include <QStringList>
#include <QMetaType>
#include <functional>
#include <nonstd/optional>
namespace ImportFTB {
struct Modpack
{
int id = 0;
int versionId = 0;
QString name;
QString version;
QString description;
QStringList authors;
QString mcVersion;
QString modLoader;
bool hasInstMods = false;
int minMemory = 1024;
int recMemory = 2048;
int memory = 2048;
QString instanceDir;
QString iconPath;
QIcon icon;
QString getLogoName() {
return QString("ftb_%1").arg(id);
}
};
class Model : public QAbstractListModel
{
Q_OBJECT
private:
QList<Modpack> modpacks;
public:
Model(QObject *parent);
virtual ~Model() = default;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
void reload();
};
}
Q_DECLARE_METATYPE(ImportFTB::Modpack)

View File

@ -0,0 +1,96 @@
/*
* Copyright 2022 Petr Mrázek
*
* This source is subject to the Microsoft Permissive License (MS-PL).
* Please see the COPYING.md file for more information.
*/
#include "PackInstallTask.h"
#include <QtConcurrent>
#include "MMCZip.h"
#include "BaseInstance.h"
#include "FileSystem.h"
#include "settings/INISettingsObject.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "minecraft/GradleSpecifier.h"
#include "BuildConfig.h"
#include "Application.h"
#include <pathmatcher/RegexpMatcher.h>
namespace ImportFTB {
PackInstallTask::PackInstallTask(Modpack pack) {
m_pack = pack;
}
void PackInstallTask::executeTask() {
FS::copy folderCopy(m_pack.instanceDir, FS::PathCombine(m_stagingPath, ".minecraft"));
folderCopy.followSymlinks(true).blacklist(new RegexpMatcher("(instance|version)[.]json"));
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &PackInstallTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &PackInstallTask::copyAborted);
m_copyFutureWatcher.setFuture(m_copyFuture);
abortable = true;
}
void PackInstallTask::copyFinished() {
abortable = false;
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
instanceSettings->suspendSave();
instanceSettings->registerSetting("InstanceType", "Legacy");
instanceSettings->set("InstanceType", "OneSix");
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
auto components = instance.getPackProfile();
components->buildingFromScratch();
components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
auto modloader = m_pack.modLoader;
if(!modloader.isEmpty()) {
if(modloader.contains("-forge-")) {
auto forgeVersion = modloader.replace(m_pack.mcVersion, "").replace("-forge-", "");
components->setComponentVersion("net.minecraftforge", forgeVersion, true);
}
else {
qWarning() << "Unknown modloader version: " << modloader;
}
}
components->saveNow();
instance.setName(m_instName);
if(m_instIcon == "default")
{
m_instIcon = "ftb_logo";
}
instance.setIconKey(m_instIcon);
instanceSettings->resumeSave();
emitSucceeded();
}
void PackInstallTask::copyAborted() {
emitAborted();
}
bool ImportFTB::PackInstallTask::canAbort() const {
return abortable;
}
bool ImportFTB::PackInstallTask::abort() {
if(abortable) {
m_copyFuture.cancel();
return true;
}
return false;
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2022 Petr Mrázek
*
* This source is subject to the Microsoft Permissive License (MS-PL).
* Please see the COPYING.md file for more information.
*/
#pragma once
#include "InstanceTask.h"
#include "Model.h"
#include <QFuture>
#include <QFutureWatcher>
namespace ImportFTB {
class PackInstallTask : public InstanceTask
{
Q_OBJECT
public:
explicit PackInstallTask(Modpack pack);
virtual ~PackInstallTask() = default;
bool canAbort() const override;
bool abort() override;
protected:
virtual void executeTask() override;
private:
void install();
private slots:
void copyFinished();
void copyAborted();
private:
bool abortable = false;
Modpack m_pack;
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
};
}