From ebb17cb5f8b4b7771c28bb0286846aa52d29d6a0 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 26 Mar 2020 03:38:31 +0100 Subject: NOISSUE no means no. --- api/logic/minecraft/MojangVersionFormat.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'api') diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp index 33d3c54c..0f6cf6a6 100644 --- a/api/logic/minecraft/MojangVersionFormat.cpp +++ b/api/logic/minecraft/MojangVersionFormat.cpp @@ -130,6 +130,10 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi { Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "mainClass", out->mainClass); + if(out->mainClass.contains("forgewrapper") || out->mainClass.contains("zekerzhayard")) { + out->mainClass.clear(); + out->addProblem(ProblemSeverity::Error, QObject::tr("Forge workarounds have no place in MultiMC.")); + } Bits::readString(in, "minecraftArguments", out->minecraftArguments); if(out->minecraftArguments.isEmpty()) { -- cgit From e6cc65cf69b3fb2c1fa08e6768669f826048af20 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 26 Mar 2020 10:38:13 +0100 Subject: NOISSUE no means no, #2 --- CMakeLists.txt | 2 +- api/logic/minecraft/MojangVersionFormat.cpp | 4 ---- api/logic/minecraft/OneSixVersionFormat.cpp | 4 ++++ 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'api') diff --git a/CMakeLists.txt b/CMakeLists.txt index b3b451e1..ede71307 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ set(MultiMC_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetc ######## Set version numbers ######## set(MultiMC_VERSION_MAJOR 0) set(MultiMC_VERSION_MINOR 6) -set(MultiMC_VERSION_HOTFIX 9) +set(MultiMC_VERSION_HOTFIX 10) # Build number set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp index 0f6cf6a6..33d3c54c 100644 --- a/api/logic/minecraft/MojangVersionFormat.cpp +++ b/api/logic/minecraft/MojangVersionFormat.cpp @@ -130,10 +130,6 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi { Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "mainClass", out->mainClass); - if(out->mainClass.contains("forgewrapper") || out->mainClass.contains("zekerzhayard")) { - out->mainClass.clear(); - out->addProblem(ProblemSeverity::Error, QObject::tr("Forge workarounds have no place in MultiMC.")); - } Bits::readString(in, "minecraftArguments", out->minecraftArguments); if(out->minecraftArguments.isEmpty()) { diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp index 6f3b926b..3d3cf916 100644 --- a/api/logic/minecraft/OneSixVersionFormat.cpp +++ b/api/logic/minecraft/OneSixVersionFormat.cpp @@ -151,6 +151,10 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc QJsonObject libObj = requireObject(libVal); // parse the library auto lib = libraryFromJson(libObj, filename); + if(lib->rawName().artifactId() == "ForgeWrapper") { + out->mainClass.clear(); + out->addProblem(ProblemSeverity::Error, QObject::tr("Forge workarounds have no place in MultiMC.")); + } out->libraries.append(lib); } }; -- cgit From 0281845fc840fae131856b2fda47be9b8ca1affc Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Fri, 27 Mar 2020 02:23:15 +0100 Subject: GH-2544 allow adding files to `libraries` without affecting classpath This is done by adding library-like objects into the `mavenFiles` list in version JSONs. --- api/logic/minecraft/LaunchProfile.cpp | 22 +++++++++++++++++++++ api/logic/minecraft/LaunchProfile.h | 7 ++++++- api/logic/minecraft/OneSixVersionFormat.cpp | 29 ++++++++++++++++++---------- api/logic/minecraft/VersionFile.cpp | 6 +++++- api/logic/minecraft/VersionFile.h | 3 +++ api/logic/minecraft/update/LibrariesTask.cpp | 1 + 6 files changed, 56 insertions(+), 12 deletions(-) (limited to 'api') diff --git a/api/logic/minecraft/LaunchProfile.cpp b/api/logic/minecraft/LaunchProfile.cpp index c39bdf04..41705187 100644 --- a/api/logic/minecraft/LaunchProfile.cpp +++ b/api/logic/minecraft/LaunchProfile.cpp @@ -11,6 +11,7 @@ void LaunchProfile::clear() m_mainClass.clear(); m_appletClass.clear(); m_libraries.clear(); + m_mavenFiles.clear(); m_traits.clear(); m_jarMods.clear(); m_mainJar.reset(); @@ -157,6 +158,22 @@ void LaunchProfile::applyLibrary(LibraryPtr library) } } +void LaunchProfile::applyMavenFile(LibraryPtr mavenFile) +{ + if(!mavenFile->isActive()) + { + return; + } + + if(mavenFile->isNative()) + { + return; + } + + // unlike libraries, we do not keep only one version or try to dedupe them + m_mavenFiles.append(Library::limitedCopy(mavenFile)); +} + const LibraryPtr LaunchProfile::getMainJar() const { return m_mainJar; @@ -253,6 +270,11 @@ const QList & LaunchProfile::getNativeLibraries() const return m_nativeLibraries; } +const QList & LaunchProfile::getMavenFiles() const +{ + return m_mavenFiles; +} + void LaunchProfile::getLibraryFiles( const QString& architecture, QStringList& jars, diff --git a/api/logic/minecraft/LaunchProfile.h b/api/logic/minecraft/LaunchProfile.h index 77174079..c1752531 100644 --- a/api/logic/minecraft/LaunchProfile.h +++ b/api/logic/minecraft/LaunchProfile.h @@ -20,6 +20,7 @@ public: /* application of profile variables from patches */ void applyJarMods(const QList &jarMods); void applyMods(const QList &jarMods); void applyLibrary(LibraryPtr library); + void applyMavenFile(LibraryPtr library); void applyMainJar(LibraryPtr jar); void applyProblemSeverity(ProblemSeverity severity); /// clear the profile @@ -37,6 +38,7 @@ public: /* getters for profile variables */ const QList & getJarMods() const; const QList & getLibraries() const; const QList & getNativeLibraries() const; + const QList & getMavenFiles() const; const LibraryPtr getMainJar() const; void getLibraryFiles( const QString & architecture, @@ -79,10 +81,13 @@ private: /// the list of libraries QList m_libraries; + /// the list of maven files to be placed in the libraries folder, but not acted upon + QList m_mavenFiles; + /// the main jar LibraryPtr m_mainJar; - /// the list of libraries + /// the list of native libraries QList m_nativeLibraries; /// traits, collected from all the version files (version files can only add) diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp index 3d3cf916..a0b6fd0e 100644 --- a/api/logic/minecraft/OneSixVersionFormat.cpp +++ b/api/logic/minecraft/OneSixVersionFormat.cpp @@ -144,18 +144,14 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc } } - auto readLibs = [&](const char * which) + auto readLibs = [&](const char * which, QList & out) { for (auto libVal : requireArray(root.value(which))) { QJsonObject libObj = requireObject(libVal); // parse the library auto lib = libraryFromJson(libObj, filename); - if(lib->rawName().artifactId() == "ForgeWrapper") { - out->mainClass.clear(); - out->addProblem(ProblemSeverity::Error, QObject::tr("Forge workarounds have no place in MultiMC.")); - } - out->libraries.append(lib); + out.append(lib); } }; bool hasPlusLibs = root.contains("+libraries"); @@ -164,16 +160,20 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc { out->addProblem(ProblemSeverity::Warning, QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported.")); - readLibs("libraries"); - readLibs("+libraries"); + readLibs("libraries", out->libraries); + readLibs("+libraries", out->libraries); } else if (hasLibs) { - readLibs("libraries"); + readLibs("libraries", out->libraries); } else if(hasPlusLibs) { - readLibs("+libraries"); + readLibs("+libraries", out->libraries); + } + + if(root.contains("mavenFiles")) { + readLibs("mavenFiles", out->mavenFiles); } // if we have mainJar, just use it @@ -280,6 +280,15 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch } root.insert("libraries", array); } + if (!patch->mavenFiles.isEmpty()) + { + QJsonArray array; + for (auto value: patch->mavenFiles) + { + array.append(OneSixVersionFormat::libraryToJson(value.get())); + } + root.insert("mavenFiles", array); + } if (!patch->jarMods.isEmpty()) { QJsonArray array; diff --git a/api/logic/minecraft/VersionFile.cpp b/api/logic/minecraft/VersionFile.cpp index cfb9e504..5bad57e9 100644 --- a/api/logic/minecraft/VersionFile.cpp +++ b/api/logic/minecraft/VersionFile.cpp @@ -41,6 +41,10 @@ void VersionFile::applyTo(LaunchProfile *profile) { profile->applyLibrary(library); } + for (auto mavenFile : mavenFiles) + { + profile->applyMavenFile(mavenFile); + } profile->applyProblemSeverity(getProblemSeverity()); } @@ -53,4 +57,4 @@ void VersionFile::applyTo(LaunchProfile *profile) throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion); } } -*/ \ No newline at end of file +*/ diff --git a/api/logic/minecraft/VersionFile.h b/api/logic/minecraft/VersionFile.h index 3dc9db96..29ddd0bc 100644 --- a/api/logic/minecraft/VersionFile.h +++ b/api/logic/minecraft/VersionFile.h @@ -75,6 +75,9 @@ public: /* data */ /// Mojang: list of libraries to add to the version QList libraries; + /// MultiMC: list of maven files to put in the libraries folder, but not in classpath + QList mavenFiles; + /// The main jar (Minecraft version library, normally) LibraryPtr mainJar; diff --git a/api/logic/minecraft/update/LibrariesTask.cpp b/api/logic/minecraft/update/LibrariesTask.cpp index 912f492b..a000f77f 100644 --- a/api/logic/minecraft/update/LibrariesTask.cpp +++ b/api/logic/minecraft/update/LibrariesTask.cpp @@ -45,6 +45,7 @@ void LibrariesTask::executeTask() QList libArtifactPool; libArtifactPool.append(profile->getLibraries()); libArtifactPool.append(profile->getNativeLibraries()); + libArtifactPool.append(profile->getMavenFiles()); libArtifactPool.append(profile->getMainJar()); processArtifactPool(libArtifactPool, failedLocalLibraries, inst->getLocalLibraryPath()); -- cgit From 3ff93a42161a5fe9301db1054dcb62c7d79ac77d Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 31 Mar 2020 03:13:19 +0200 Subject: NOISSUE Bare-bones twitch pack browser --- api/logic/Env.cpp | 1 + application/CMakeLists.txt | 8 +- application/dialogs/NewInstanceDialog.cpp | 3 + application/dialogs/NewInstanceDialog.h | 2 + application/pages/modplatform/twitch/TwitchData.h | 38 ++++ .../pages/modplatform/twitch/TwitchModel.cpp | 253 +++++++++++++++++++++ application/pages/modplatform/twitch/TwitchModel.h | 65 ++++++ .../pages/modplatform/twitch/TwitchPage.cpp | 86 +++++++ application/pages/modplatform/twitch/TwitchPage.h | 77 +++++++ application/pages/modplatform/twitch/TwitchPage.ui | 63 +++++ 10 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 application/pages/modplatform/twitch/TwitchData.h create mode 100644 application/pages/modplatform/twitch/TwitchModel.cpp create mode 100644 application/pages/modplatform/twitch/TwitchModel.h create mode 100644 application/pages/modplatform/twitch/TwitchPage.cpp create mode 100644 application/pages/modplatform/twitch/TwitchPage.h create mode 100644 application/pages/modplatform/twitch/TwitchPage.ui (limited to 'api') diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp index 77546bbc..0d496d4e 100644 --- a/api/logic/Env.cpp +++ b/api/logic/Env.cpp @@ -97,6 +97,7 @@ void Env::initHttpMetaCache() m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath()); m_metacache->addBase("general", QDir("cache").absolutePath()); m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath()); + m_metacache->addBase("TwitchPacks", QDir("cache/TwitchPacks").absolutePath()); m_metacache->addBase("skins", QDir("accounts/skins").absolutePath()); m_metacache->addBase("root", QDir::currentPath()); m_metacache->addBase("translations", QDir("translations").absolutePath()); diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index 94ba56d0..99ec8b8f 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -133,6 +133,11 @@ SET(MULTIMC_SOURCES pages/modplatform/legacy_ftb/Page.h pages/modplatform/legacy_ftb/ListModel.h pages/modplatform/legacy_ftb/ListModel.cpp + pages/modplatform/twitch/TwitchData.h + pages/modplatform/twitch/TwitchModel.cpp + pages/modplatform/twitch/TwitchModel.h + pages/modplatform/twitch/TwitchPage.cpp + pages/modplatform/twitch/TwitchPage.h pages/modplatform/ImportPage.cpp pages/modplatform/ImportPage.h @@ -251,7 +256,8 @@ SET(MULTIMC_UIS # Platform pages pages/modplatform/VanillaPage.ui pages/modplatform/legacy_ftb/Page.ui - pages/modplatform/ImportPage.ui + pages/modplatform/twitch/TwitchPage.ui + pages/modplatform/twitch/ImportPage.ui # Dialogs dialogs/CopyInstanceDialog.ui diff --git a/application/dialogs/NewInstanceDialog.cpp b/application/dialogs/NewInstanceDialog.cpp index 804340bc..511f991e 100644 --- a/application/dialogs/NewInstanceDialog.cpp +++ b/application/dialogs/NewInstanceDialog.cpp @@ -35,6 +35,7 @@ #include "widgets/PageContainer.h" #include #include +#include #include @@ -119,11 +120,13 @@ void NewInstanceDialog::accept() QList NewInstanceDialog::getPages() { importPage = new ImportPage(this); + twitchPage = new TwitchPage(this); return { new VanillaPage(this), importPage, new LegacyFTB::Page(this), + twitchPage }; } diff --git a/application/dialogs/NewInstanceDialog.h b/application/dialogs/NewInstanceDialog.h index c86ab73f..0b8b2fb8 100644 --- a/application/dialogs/NewInstanceDialog.h +++ b/application/dialogs/NewInstanceDialog.h @@ -29,6 +29,7 @@ class NewInstanceDialog; class PageContainer; class QDialogButtonBox; class ImportPage; +class TwitchPage; class NewInstanceDialog : public QDialog, public BasePageProvider { @@ -67,6 +68,7 @@ private: QString InstIconKey; ImportPage *importPage = nullptr; + TwitchPage *twitchPage = nullptr; std::unique_ptr creationTask; bool importIcon = false; diff --git a/application/pages/modplatform/twitch/TwitchData.h b/application/pages/modplatform/twitch/TwitchData.h new file mode 100644 index 00000000..dd000b84 --- /dev/null +++ b/application/pages/modplatform/twitch/TwitchData.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace Twitch { + +struct ModpackAuthor { + QString name; + QString url; +}; + +struct ModpackFile { + int addonId; + int fileId; + QString version; + QString mcVersion; + QString downloadUrl; +}; + +struct Modpack +{ + bool broken = true; + int addonId = 0; + + QString name; + QString description; + QList authors; + QString mcVersion; + QString logoName; + QString logoUrl; + QString websiteUrl; + + ModpackFile latestFile; +}; +} + +Q_DECLARE_METATYPE(Twitch::Modpack) diff --git a/application/pages/modplatform/twitch/TwitchModel.cpp b/application/pages/modplatform/twitch/TwitchModel.cpp new file mode 100644 index 00000000..210341dc --- /dev/null +++ b/application/pages/modplatform/twitch/TwitchModel.cpp @@ -0,0 +1,253 @@ +#include "TwitchModel.h" +#include "MultiMC.h" + +#include +#include + +#include +#include + +#include +#include + +#include "net/URLConstants.h" + +namespace Twitch { + +ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) +{ +} + +ListModel::~ListModel() +{ +} + +int ListModel::rowCount(const QModelIndex &parent) const +{ + return modpacks.size(); +} + +int ListModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QVariant ListModel::data(const QModelIndex &index, int role) const +{ + int pos = index.row(); + if(pos >= modpacks.size() || pos < 0 || !index.isValid()) + { + return QString("INVALID INDEX %1").arg(pos); + } + + Modpack pack = modpacks.at(pos); + if(role == Qt::DisplayRole) + { + return pack.name; + } + else if (role == Qt::ToolTipRole) + { + if(pack.description.length() > 100) + { + //some magic to prevent to long tooltips and replace html linebreaks + QString edit = pack.description.left(97); + edit = edit.left(edit.lastIndexOf("
")).left(edit.lastIndexOf(" ")).append("..."); + return edit; + + } + return pack.description; + } + else if(role == Qt::DecorationRole) + { + if(m_logoMap.contains(pack.logoName)) + { + return (m_logoMap.value(pack.logoName)); + } + QIcon icon = MMC->getThemedIcon("screenshot-placeholder"); + ((ListModel *)this)->requestLogo(pack.logoName, pack.logoUrl); + return icon; + } + else if(role == Qt::UserRole) + { + QVariant v; + v.setValue(pack); + return v; + } + + return QVariant(); +} + +void ListModel::logoLoaded(QString logo, QIcon out) +{ + m_loadingLogos.removeAll(logo); + m_logoMap.insert(logo, out); + for(int i = 0; i < modpacks.size(); i++) { + if(modpacks[i].logoName == logo) { + emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole}); + } + } +} + +void ListModel::logoFailed(QString logo) +{ + m_failedLogos.append(logo); + m_loadingLogos.removeAll(logo); +} + +void ListModel::requestLogo(QString logo, QString url) +{ + if(m_loadingLogos.contains(logo) || m_failedLogos.contains(logo)) + { + return; + } + + MetaEntryPtr entry = ENV.metacache()->resolveEntry("TwitchPacks", QString("logos/%1").arg(logo.section(".", 0, 0))); + NetJob *job = new NetJob(QString("Twitch Icon Download %1").arg(logo)); + job->addNetAction(Net::Download::makeCached(QUrl(url), entry)); + + auto fullPath = entry->getFullPath(); + QObject::connect(job, &NetJob::finished, this, [this, logo, fullPath] + { + emit logoLoaded(logo, QIcon(fullPath)); + if(waitingCallbacks.contains(logo)) + { + waitingCallbacks.value(logo)(fullPath); + } + }); + + QObject::connect(job, &NetJob::failed, this, [this, logo] + { + emit logoFailed(logo); + }); + + job->start(); + + m_loadingLogos.append(logo); +} + +void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback) +{ + if(m_logoMap.contains(logo)) + { + callback(ENV.metacache()->resolveEntry("TwitchPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath()); + } + else + { + requestLogo(logo, logoUrl); + } +} + +Qt::ItemFlags ListModel::flags(const QModelIndex &index) const +{ + return QAbstractListModel::flags(index); +} + +void ListModel::searchWithTerm(const QString& term) +{ + if(currentSearchTerm == term) { + return; + } + NetJob *netJob = new NetJob("Twitch::Search"); + auto searchUrl = QString( + "https://addons-ecs.forgesvc.net/api/v2/addon/search?" + "categoryId=0&" + "gameId=432&" + //"gameVersion=1.12.2&" + "index=0&" + "pageSize=25&" + "searchFilter=%1&" + "sectionId=4471&" + "sort=0" + ).arg(term); + netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); + jobPtr = netJob; + jobPtr->start(); + QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished); + QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed); +} + +void Twitch::ListModel::searchRequestFinished() +{ + jobPtr.reset(); + + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); + if(parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Twitch at " << parse_error.offset << " reason: " << parse_error.errorString(); + qWarning() << response; + return; + } + + QList newList; + auto objs = doc.array(); + for(auto projectIter: objs) { + Modpack pack; + auto project = projectIter.toObject(); + pack.addonId = project.value("id").toInt(0); + if (pack.addonId == 0) { + continue; + } + pack.name = project.value("name").toString(); + pack.websiteUrl = project.value("websiteUrl").toString(); + pack.description = project.value("summary").toString(); + auto attachments = project.value("attachments").toArray(); + for(auto attachmentIter: attachments) { + auto attachment = attachmentIter.toObject(); + bool isDefault = attachment.value("isDefault").toBool(false); + if(!isDefault) { + continue; + } + pack.logoName = attachment.value("title").toString(); + pack.logoUrl = attachment.value("thumbnailUrl").toString(); + } + auto authors = project.value("authors").toArray(); + for(auto authorIter: authors) { + auto author = authorIter.toObject(); + ModpackAuthor packAuthor; + packAuthor.name = author.value("name").toString(); + packAuthor.url = author.value("url").toString(); + pack.authors.append(packAuthor); + } + int defaultFileId = project.value("defaultFileId").toInt(0); + if(defaultFileId == 0) { + continue; + } + bool found = false; + auto files = project.value("latestFiles").toArray(); + for(auto fileIter: files) { + auto file = fileIter.toObject(); + int id = file.value("id").toInt(0); + // NOTE: for now, ignore everything that's not the default... + if(id != defaultFileId) { + continue; + } + pack.latestFile.addonId = pack.addonId; + pack.latestFile.fileId = id; + // FIXME: what to do when there's more than one, or there's no version? + auto versionArray = file.value("gameVersion").toArray(); + if(versionArray.size() != 1) { + continue; + } + pack.latestFile.mcVersion = versionArray[0].toString(); + pack.latestFile.version = file.value("displayName").toString(); + pack.latestFile.downloadUrl = file.value("downloadUrl").toString(); + found = true; + break; + } + if(!found) { + return; + } + pack.broken = false; + newList.append(pack); + } + beginResetModel(); + newList.swap(modpacks); + endResetModel(); +} + +void Twitch::ListModel::searchRequestFailed(QString reason) +{ + jobPtr.reset(); +} + +} diff --git a/application/pages/modplatform/twitch/TwitchModel.h b/application/pages/modplatform/twitch/TwitchModel.h new file mode 100644 index 00000000..1241a079 --- /dev/null +++ b/application/pages/modplatform/twitch/TwitchModel.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "TwitchData.h" + +namespace Twitch { + + +typedef QMap LogoMap; +typedef std::function LogoCallback; + +class ListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + ListModel(QObject *parent); + virtual ~ListModel(); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + + void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback); + void searchWithTerm(const QString & term); + +private slots: + void logoFailed(QString logo); + void logoLoaded(QString logo, QIcon out); + + void searchRequestFinished(); + void searchRequestFailed(QString reason); + +private: + void requestLogo(QString file, QString url); + +private: + QList modpacks; + QStringList m_failedLogos; + QStringList m_loadingLogos; + LogoMap m_logoMap; + QMap waitingCallbacks; + + QString currentSearchTerm; + NetJobPtr jobPtr; + QByteArray response; +}; + +} diff --git a/application/pages/modplatform/twitch/TwitchPage.cpp b/application/pages/modplatform/twitch/TwitchPage.cpp new file mode 100644 index 00000000..80d83133 --- /dev/null +++ b/application/pages/modplatform/twitch/TwitchPage.cpp @@ -0,0 +1,86 @@ +#include "TwitchPage.h" +#include "ui_TwitchPage.h" + +#include "MultiMC.h" +#include "dialogs/NewInstanceDialog.h" +#include +#include "TwitchModel.h" +#include + +TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent) + : QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog) +{ + ui->setupUi(this); + connect(ui->searchButton, &QPushButton::clicked, this, &TwitchPage::triggerSearch); + ui->searchEdit->installEventFilter(this); + model = new Twitch::ListModel(this); + ui->packView->setModel(model); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TwitchPage::onSelectionChanged); +} + +TwitchPage::~TwitchPage() +{ + delete ui; +} + +bool TwitchPage::eventFilter(QObject* watched, QEvent* event) +{ + if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Return) { + triggerSearch(); + keyEvent->accept(); + return true; + } + } + return QWidget::eventFilter(watched, event); +} + +bool TwitchPage::shouldDisplay() const +{ + return true; +} + +void TwitchPage::openedImpl() +{ + suggestCurrent(); +} + +void TwitchPage::triggerSearch() +{ + model->searchWithTerm(ui->searchEdit->text()); +} + +void TwitchPage::onSelectionChanged(QModelIndex first, QModelIndex second) +{ + if(!first.isValid()) + { + if(isOpened) + { + dialog->setSuggestedPack(); + } + return; + } + current = model->data(first, Qt::UserRole).value(); + suggestCurrent(); +} + +void TwitchPage::suggestCurrent() +{ + if(!isOpened) + { + return; + } + if(current.broken) + { + dialog->setSuggestedPack(); + } + + dialog->setSuggestedPack(current.name, new InstanceImportTask(current.latestFile.downloadUrl)); + QString editedLogoName; + editedLogoName = "twitch_" + current.logoName.section(".", 0, 0); + model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo) + { + dialog->setSuggestedIconFromFile(logo, editedLogoName); + }); +} diff --git a/application/pages/modplatform/twitch/TwitchPage.h b/application/pages/modplatform/twitch/TwitchPage.h new file mode 100644 index 00000000..04e3a1c6 --- /dev/null +++ b/application/pages/modplatform/twitch/TwitchPage.h @@ -0,0 +1,77 @@ +/* Copyright 2013-2019 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "pages/BasePage.h" +#include +#include "tasks/Task.h" +#include "TwitchData.h" + +namespace Ui +{ +class TwitchPage; +} + +class NewInstanceDialog; + +namespace Twitch { + class ListModel; +} + +class TwitchPage : public QWidget, public BasePage +{ + Q_OBJECT + +public: + explicit TwitchPage(NewInstanceDialog* dialog, QWidget *parent = 0); + virtual ~TwitchPage(); + virtual QString displayName() const override + { + return tr("Twitch"); + } + virtual QIcon icon() const override + { + return MMC->getThemedIcon("twitch"); + } + virtual QString id() const override + { + return "twitch"; + } + virtual QString helpPage() const override + { + return "Twitch-platform"; + } + virtual bool shouldDisplay() const override; + + void openedImpl() override; + + bool eventFilter(QObject * watched, QEvent * event) override; + +private: + void suggestCurrent(); + +private slots: + void triggerSearch(); + void onSelectionChanged(QModelIndex first, QModelIndex second); + +private: + Ui::TwitchPage *ui = nullptr; + NewInstanceDialog* dialog = nullptr; + Twitch::ListModel* model = nullptr; + Twitch::Modpack current; +}; diff --git a/application/pages/modplatform/twitch/TwitchPage.ui b/application/pages/modplatform/twitch/TwitchPage.ui new file mode 100644 index 00000000..7a8203b1 --- /dev/null +++ b/application/pages/modplatform/twitch/TwitchPage.ui @@ -0,0 +1,63 @@ + + + TwitchPage + + + + 0 + 0 + 875 + 745 + + + + + + + + + + Search + + + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + false + + + true + + + false + + + true + + + false + + + + + + + searchEdit + searchButton + packView + + + -- cgit From 5c921589f1db2acb9691b3077fddf6a9d7336693 Mon Sep 17 00:00:00 2001 From: "John C. Allwein" <5902494+johnnyapol@users.noreply.github.com> Date: Sat, 18 Apr 2020 19:14:35 -0400 Subject: NOISSUE fix compiling of api/logic/Version.cpp -Wrange-loop-construct triggers this error in clang --- api/logic/Version.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'api') diff --git a/api/logic/Version.cpp b/api/logic/Version.cpp index 42eac669..6392a50f 100644 --- a/api/logic/Version.cpp +++ b/api/logic/Version.cpp @@ -78,7 +78,7 @@ void Version::parse() // FIXME: this is bad. versions can contain a lot more separators... QStringList parts = m_string.split('.'); - for (const auto part : parts) + for (const auto &part : parts) { m_sections.append(Section(part)); } -- cgit