From 08f85f1a936b4744c6ad8e77c1c6928ebee34dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 8 Feb 2020 15:00:20 +0100 Subject: [PATCH 01/18] Update changelog and set version to 0.6.8 --- CMakeLists.txt | 2 +- changelog.md | 58 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f1032c09..d02896430 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 7) +set(MultiMC_VERSION_HOTFIX 8) # Build number set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") diff --git a/changelog.md b/changelog.md index 544c7abfe..f61f76311 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,52 @@ -# MultiMC 0.6.7 +# MultiMC 0.6.8 + +This is mostly about removal of the 'curse URL' related features, because they were of low quality and generally unreliable. + +There are some bug fixes included. + +MultiMC also migrated to a new continuous deployment system, which makes everything that much smoother. + +### New or changed features + +- GH-852: Instance group expansion status now saves/loads as expected. + +- The bees have invaded the launcher. We now have a bee icon. + +- Translations have been overhauled, yet again... + + - We now have a [crowdin site](https://translate.multimc.org/) for all the translation work. + + - Translations are made based on the development version, and for the development version. + + - Many strings have been tweaked to make translating the application easier. + + - When selecting languages, European Portuguese is now displaying properly. + +- Accessibility has been further improved - the main window reads as `MultiMC`, not a long string of nonsensical version numbers, when announced by a screen reader. + +- Removed the unimplemented Technic page from instance creation dialog. + +- GH-2859: Broken twitch URL import method was removed. + +- GH-2819: Filter bar in mod lists now also works with descriptions and author lists. + +- GH-2832: Version page now has buttons for opening the Minecraft and internal libraries folders of the instance. + +- GH-2769: When copying an instance, there's now an option to keep or remove the total play time from the copy. + +### Bugfixes + +- GH-2880: Clicking the service status indicators now opens a valid site again, instead of going nowhere. + +- GH-2853: When collapsing groups in instance view, the action no longer becomes 'sticky' and doesn't apply to items clicked afterwards. + +- GH-2787: "Download All" button works again. + +- When dependencies are customized, the la ncher will not try to update them in an infinite loop when something else requires a different version. + +# Previous releases + +## MultiMC 0.6.7 The previous release introduced some extra buttons that made the instance window way too big for some displays. This release is aimed at fixing that, along with other UI and performance improvements. @@ -47,8 +95,6 @@ There are some accessibility fixes thrown in too. Sorting cascades from 'Enabled' to 'Name' and then 'Version'. This means that if you sort 'Enabled', the enabled and disabled mods are still sorted by name and mods with the same name will be also sorted by version. -# Previous releases - ## MultiMC 0.6.6 This release is mostly the smaller things that have accumulated over time, along with a big change in linux packaging. @@ -70,7 +116,7 @@ MultiMC on linux is built with Qt 5.4 and older versions of Qt will not work. This should be a massive improvement to system integration on linux and resolves GH-1784, GH-2605, GH-1979, GH-2271, GH-1992, GH-1816 and their many duplicates. -#### New or changed features +### New or changed features - GH-2487: No is now the default button when deleting instances. @@ -100,7 +146,7 @@ This should be a massive improvement to system integration on linux and resolves You can now drag the purple download buttons from CurseForge into MultiMC and get a modpack out of it. Much easier! -#### Bugfixes +### Bugfixes - Translation folder is now created sooner, making first launch translation fetch work again. @@ -134,7 +180,7 @@ This should be a massive improvement to system integration on linux and resolves Finalizing the translation workflow improvements and adding fixes for sounds missing in old game versions. -#### New or changed features +### New or changed features - UI for the language settings has been unified across the application From d58481e0de15ec4f1eb60de9a42c6cc9ec330d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 9 Feb 2020 00:03:20 +0100 Subject: [PATCH 02/18] NOISSUE fix some changelog wording --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f61f76311..ef7c8dc65 100644 --- a/changelog.md +++ b/changelog.md @@ -42,7 +42,7 @@ MultiMC also migrated to a new continuous deployment system, which makes everyth - GH-2787: "Download All" button works again. -- When dependencies are customized, the la ncher will not try to update them in an infinite loop when something else requires a different version. +- When a component is customized, the launcher will not try to update it in an infinite loop when something else requires a different version. # Previous releases From 47fa7b3f8c7a92fa14aa50d6bd7d7a3c100a06f3 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Wed, 5 Feb 2020 00:29:23 +0100 Subject: [PATCH 03/18] GH-2988 add --import command-line option When specified, opens the "Import from zip" dialog as soon as the main window is shown, with the URL field prefilled with the argument given to the option. Closes #2998 --- application/MainWindow.h | 4 ++-- application/MultiMC.cpp | 50 ++++++++++++++++++++++++++++++++++++---- application/MultiMC.h | 2 ++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/application/MainWindow.h b/application/MainWindow.h index a415b5e85..00b8e043e 100644 --- a/application/MainWindow.h +++ b/application/MainWindow.h @@ -57,6 +57,8 @@ public: void checkInstancePathForProblems(); void updatesAllowedChanged(bool allowed); + + void droppedURLs(QList urls); signals: void isClosing(); @@ -180,8 +182,6 @@ private slots: */ void downloadUpdates(GoUpdate::Status status); - void droppedURLs(QList urls); - void konamiTriggered(); void globalSettingsClosed(); diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index c4e4392de..393ea0467 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +174,10 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) // --alive parser.addSwitch("alive"); parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after MultiMC starts"); + // --import + parser.addOption("import"); + parser.addShortOpt("import", 'I'); + parser.addDocumentation("import", "Import instance from specified zip (local path or URL)"); // parse the arguments try @@ -207,6 +212,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) } m_instanceIdToLaunch = args["launch"].toString(); m_liveCheck = args["alive"].toBool(); + m_zipToImport = args["import"].toUrl(); QString origcwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -278,13 +284,20 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) connect(m_peerInstance, &LocalPeer::messageReceived, this, &MultiMC::messageReceived); if(m_peerInstance->isClient()) { + int timeout = 2000; + if(m_instanceIdToLaunch.isEmpty()) { - m_peerInstance->sendMessage("activate", 2000); + m_peerInstance->sendMessage("activate", timeout); + + if(!m_zipToImport.isEmpty()) + { + m_peerInstance->sendMessage("import " + m_zipToImport.toString(), timeout); + } } else { - m_peerInstance->sendMessage(m_instanceIdToLaunch, 2000); + m_peerInstance->sendMessage("launch " + m_instanceIdToLaunch, timeout); } m_status = MultiMC::Succeeded; return; @@ -812,6 +825,11 @@ void MultiMC::performMainStartupAction() showMainWindow(false); qDebug() << "<> Main window shown."; } + if(!m_zipToImport.isEmpty()) + { + qDebug() << "<> Importing instance from zip:" << m_zipToImport; + m_mainWindow->droppedURLs({ m_zipToImport }); + } } void MultiMC::showFatalErrorMessage(const QString& title, const QString& content) @@ -848,18 +866,40 @@ void MultiMC::messageReceived(const QString& message) qDebug() << "Received message" << message << "while still initializing. It will be ignored."; return; } - if(message == "activate") + + QStringList args = message.split(' '); + QString command = args.takeFirst(); + + if(command == "activate") { showMainWindow(); } - else + else if(command == "import") { - auto inst = instances()->getInstanceById(message); + if(args.isEmpty()) + { + qWarning() << "Received" << command << "message without a zip path/URL."; + return; + } + m_mainWindow->droppedURLs({ QUrl(args.takeFirst()) }); + } + else if(command == "launch") + { + if(args.isEmpty()) + { + qWarning() << "Received" << command << "message without an instance ID."; + return; + } + auto inst = instances()->getInstanceById(args.takeFirst()); if(inst) { launch(inst, true, nullptr); } } + else + { + qWarning() << "Received invalid message" << message; + } } void MultiMC::analyticsSettingChanged(const Setting&, QVariant value) diff --git a/application/MultiMC.h b/application/MultiMC.h index d7c727e02..e6588a147 100644 --- a/application/MultiMC.h +++ b/application/MultiMC.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -221,5 +222,6 @@ private: public: QString m_instanceIdToLaunch; bool m_liveCheck = false; + QUrl m_zipToImport; std::unique_ptr logFile; }; From 6cb956b45b97ba685d9cef96484747544615e724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 24 Feb 2020 18:59:36 +0100 Subject: [PATCH 04/18] NOISSUE Nice. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d02896430..b3b451e16 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 8) +set(MultiMC_VERSION_HOTFIX 9) # Build number set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") From 95a09ba09948e1d2d1cbda24dcddd1d4da0236d5 Mon Sep 17 00:00:00 2001 From: Brottweiler Date: Fri, 31 Jan 2020 13:17:22 +0100 Subject: [PATCH 05/18] GH-2979 Tweak GenericName --- application/package/linux/multimc.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/package/linux/multimc.desktop b/application/package/linux/multimc.desktop index 770f24f17..c25be047f 100755 --- a/application/package/linux/multimc.desktop +++ b/application/package/linux/multimc.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Version=1.0 Name=MultiMC -GenericName=MultiMC +GenericName=Minecraft Launcher Comment=Free, open source launcher and instance manager for Minecraft. Type=Application Terminal=false From 164bb6c78cd52239c8449211512be2908c77d333 Mon Sep 17 00:00:00 2001 From: Ghalid <24234921+Ghalid@users.noreply.github.com> Date: Sun, 22 Mar 2020 12:17:56 -0400 Subject: [PATCH 06/18] NOISSUE change directory before running git submodule commands in mac instructions --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index d347ba149..cc7ec9c38 100644 --- a/BUILD.md +++ b/BUILD.md @@ -169,9 +169,9 @@ Pick an installation path - this is where the final `.app` will be constructed w ``` git clone https://github.com/MultiMC/MultiMC5.git +cd MultiMC5 git submodule init git submodule update -cd MultiMC5 mkdir build cd build export CMAKE_PREFIX_PATH=/usr/local/opt/qt5 From ebb17cb5f8b4b7771c28bb0286846aa52d29d6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 26 Mar 2020 03:38:31 +0100 Subject: [PATCH 07/18] NOISSUE no means no. --- api/logic/minecraft/MojangVersionFormat.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/logic/minecraft/MojangVersionFormat.cpp b/api/logic/minecraft/MojangVersionFormat.cpp index 33d3c54cb..0f6cf6a67 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()) { From e6cc65cf69b3fb2c1fa08e6768669f826048af20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 26 Mar 2020 10:38:13 +0100 Subject: [PATCH 08/18] 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(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b3b451e16..ede713075 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 0f6cf6a67..33d3c54cb 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 6f3b926ba..3d3cf9166 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); } }; From 0281845fc840fae131856b2fda47be9b8ca1affc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 27 Mar 2020 02:23:15 +0100 Subject: [PATCH 09/18] 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(-) diff --git a/api/logic/minecraft/LaunchProfile.cpp b/api/logic/minecraft/LaunchProfile.cpp index c39bdf044..41705187f 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 771740799..c17525319 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 3d3cf9166..a0b6fd0e7 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 cfb9e5047..5bad57e97 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 3dc9db96f..29ddd0bc9 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 912f492bf..a000f77fb 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()); From c9e851f12f501657629e41339ad604c3cfba82e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 28 Mar 2020 15:55:12 +0100 Subject: [PATCH 10/18] GH-2544 enable Forge install button for >= 1.13 --- application/pages/instance/VersionPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/pages/instance/VersionPage.cpp b/application/pages/instance/VersionPage.cpp index 20298117b..60ff83011 100644 --- a/application/pages/instance/VersionPage.cpp +++ b/application/pages/instance/VersionPage.cpp @@ -206,7 +206,7 @@ void VersionPage::updateVersionControls() bool newCraft = controlsEnabled && (minecraftVersion >= Version("1.14")); bool oldCraft = controlsEnabled && (minecraftVersion <= Version("1.12.2")); ui->actionInstall_Fabric->setEnabled(newCraft); - ui->actionInstall_Forge->setEnabled(oldCraft); + ui->actionInstall_Forge->setEnabled(true); ui->actionInstall_LiteLoader->setEnabled(oldCraft); ui->actionReload->setEnabled(true); updateButtons(); From 3ad9ea507e945709b01a5fcc51a60f7c45ad4c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 29 Mar 2020 03:12:57 +0200 Subject: [PATCH 11/18] NOISSUE update version number, changelog and credits in about dialog --- CMakeLists.txt | 2 +- application/dialogs/AboutDialog.cpp | 1 + changelog.md | 21 +++++++++++++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ede713075..c4a7e52e3 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 10) +set(MultiMC_VERSION_HOTFIX 11) # Build number set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") diff --git a/application/dialogs/AboutDialog.cpp b/application/dialogs/AboutDialog.cpp index a556ec056..c4e4ee24c 100644 --- a/application/dialogs/AboutDialog.cpp +++ b/application/dialogs/AboutDialog.cpp @@ -46,6 +46,7 @@ QString getCreditsHtml(QStringList patrons) stream << "

TakSuyu <taksuyu@gmail.com>

\n"; stream << "

Kilobyte <stiepen22@gmx.de>

\n"; stream << "

Rootbear75 <@rootbear75>

\n"; + stream << "

Zeker Zhayard <@Zeker_Zhayard>

\n"; stream << "
\n"; if(!patrons.isEmpty()) { diff --git a/changelog.md b/changelog.md index ef7c8dc65..e315950c3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,22 @@ -# MultiMC 0.6.8 +# MultiMC 0.6.11 + +This adds Forge 1.13+ support using [ForgeWrapper](https://github.com/ZekerZhayard/ForgeWrapper) by ZekerZhayard. + +### New or changed features + +- GH-2988: You can now import instances and curse modpacks from the command line with the `--import` option followed by either an URL or a local file path. + +- GH-2544: MultiMC now supports downloading library files without including them on the Java classpath. + + This is done by adding them to the `mavenFiles` list instead of the `libraries` list. + + Such downloads are not deduplicated or version upgraded like libraries are. + + This enables ForgeWrapper to work - MultiMC downloads all the files, and ForgeWrapper runs the Forge installer during instance start when needed. + +# Previous releases + +## MultiMC 0.6.8 This is mostly about removal of the 'curse URL' related features, because they were of low quality and generally unreliable. @@ -44,7 +62,6 @@ MultiMC also migrated to a new continuous deployment system, which makes everyth - When a component is customized, the launcher will not try to update it in an infinite loop when something else requires a different version. -# Previous releases ## MultiMC 0.6.7 From 21ac860e2771d2710e13c5694f18c3a244f20523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 29 Mar 2020 04:03:04 +0200 Subject: [PATCH 12/18] Bump dev version to 0.6.12 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4a7e52e3..d02dfc8b7 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 11) +set(MultiMC_VERSION_HOTFIX 12) # Build number set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") From 3ff93a42161a5fe9301db1054dcb62c7d79ac77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 31 Mar 2020 03:13:19 +0200 Subject: [PATCH 13/18] 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 + .../pages/modplatform/twitch/TwitchData.h | 38 +++ .../pages/modplatform/twitch/TwitchModel.cpp | 253 ++++++++++++++++++ .../pages/modplatform/twitch/TwitchModel.h | 65 +++++ .../pages/modplatform/twitch/TwitchPage.cpp | 86 ++++++ .../pages/modplatform/twitch/TwitchPage.h | 77 ++++++ .../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 diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp index 77546bbcd..0d496d4e5 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 94ba56d05..99ec8b8f5 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 804340bca..511f991e0 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 c86ab73f4..0b8b2fb87 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 000000000..dd000b846 --- /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 000000000..210341dc0 --- /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 000000000..1241a079e --- /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 000000000..80d831339 --- /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 000000000..04e3a1c6c --- /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 000000000..7a8203b10 --- /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 + + + From 5e951299b2071377bc83dc7d2bf9bd7aa9d887e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 1 Apr 2020 00:59:18 +0200 Subject: [PATCH 14/18] NOISSUE fix build --- application/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index 99ec8b8f5..53c218668 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -257,7 +257,7 @@ SET(MULTIMC_UIS pages/modplatform/VanillaPage.ui pages/modplatform/legacy_ftb/Page.ui pages/modplatform/twitch/TwitchPage.ui - pages/modplatform/twitch/ImportPage.ui + pages/modplatform/ImportPage.ui # Dialogs dialogs/CopyInstanceDialog.ui From 296ff6de96f8228ae6de8967d6e34436001b3d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 1 Apr 2020 02:08:11 +0200 Subject: [PATCH 15/18] NOISSUE Add pagination support to twitch pack search Try searching for 'craft'. Now it gives ALL the results, not just the first page of 25. --- .../pages/modplatform/twitch/TwitchModel.cpp | 87 ++++++++++++++++--- .../pages/modplatform/twitch/TwitchModel.h | 11 +++ 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/application/pages/modplatform/twitch/TwitchModel.cpp b/application/pages/modplatform/twitch/TwitchModel.cpp index 210341dc0..d9358941f 100644 --- a/application/pages/modplatform/twitch/TwitchModel.cpp +++ b/application/pages/modplatform/twitch/TwitchModel.cpp @@ -142,23 +142,36 @@ Qt::ItemFlags ListModel::flags(const QModelIndex &index) const return QAbstractListModel::flags(index); } -void ListModel::searchWithTerm(const QString& term) +bool ListModel::canFetchMore(const QModelIndex& parent) const { - if(currentSearchTerm == term) { + return searchState == CanPossiblyFetchMore; +} + +void ListModel::fetchMore(const QModelIndex& parent) +{ + if (parent.isValid()) + return; + if(nextSearchOffset == 0) { + qWarning() << "fetchMore with 0 offset is wrong..."; return; } + performPaginatedSearch(); +} + +void ListModel::performPaginatedSearch() +{ 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&" + "index=%1&" "pageSize=25&" - "searchFilter=%1&" + "searchFilter=%2&" "sectionId=4471&" "sort=0" - ).arg(term); + ).arg(nextSearchOffset).arg(currentSearchTerm); netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response)); jobPtr = netJob; jobPtr->start(); @@ -166,6 +179,27 @@ void ListModel::searchWithTerm(const QString& term) QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed); } +void ListModel::searchWithTerm(const QString& term) +{ + if(currentSearchTerm == term) { + return; + } + currentSearchTerm = term; + if(jobPtr) { + jobPtr->abort(); + searchState = ResetRequested; + return; + } + else { + beginResetModel(); + modpacks.clear(); + endResetModel(); + searchState = None; + } + nextSearchOffset = 0; + performPaginatedSearch(); +} + void Twitch::ListModel::searchRequestFinished() { jobPtr.reset(); @@ -185,20 +219,27 @@ void Twitch::ListModel::searchRequestFinished() auto project = projectIter.toObject(); pack.addonId = project.value("id").toInt(0); if (pack.addonId == 0) { + qWarning() << "Pack without an ID, skipping: " << pack.name; continue; } pack.name = project.value("name").toString(); pack.websiteUrl = project.value("websiteUrl").toString(); pack.description = project.value("summary").toString(); + bool thumbnailFound = false; auto attachments = project.value("attachments").toArray(); for(auto attachmentIter: attachments) { auto attachment = attachmentIter.toObject(); bool isDefault = attachment.value("isDefault").toBool(false); - if(!isDefault) { - continue; + if(isDefault) { + thumbnailFound = true; + pack.logoName = attachment.value("title").toString(); + pack.logoUrl = attachment.value("thumbnailUrl").toString(); + break; } - pack.logoName = attachment.value("title").toString(); - pack.logoUrl = attachment.value("thumbnailUrl").toString(); + } + if(!thumbnailFound) { + qWarning() << "Pack without an icon, skipping: " << pack.name; + continue; } auto authors = project.value("authors").toArray(); for(auto authorIter: authors) { @@ -210,6 +251,7 @@ void Twitch::ListModel::searchRequestFinished() } int defaultFileId = project.value("defaultFileId").toInt(0); if(defaultFileId == 0) { + qWarning() << "Pack without default file, skipping: " << pack.name; continue; } bool found = false; @@ -235,19 +277,38 @@ void Twitch::ListModel::searchRequestFinished() break; } if(!found) { - return; + qWarning() << "Pack with no good file, skipping: " << pack.name; + continue; } pack.broken = false; newList.append(pack); } - beginResetModel(); - newList.swap(modpacks); - endResetModel(); + if(objs.size() < 25) { + searchState = Finished; + } else { + nextSearchOffset += 25; + searchState = CanPossiblyFetchMore; + } + beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1); + modpacks.append(newList); + endInsertRows(); } void Twitch::ListModel::searchRequestFailed(QString reason) { jobPtr.reset(); + + if(searchState == ResetRequested) { + beginResetModel(); + modpacks.clear(); + endResetModel(); + + nextSearchOffset = 0; + performPaginatedSearch(); + } else { + searchState = Finished; + } } } + diff --git a/application/pages/modplatform/twitch/TwitchModel.h b/application/pages/modplatform/twitch/TwitchModel.h index 1241a079e..ad355c64a 100644 --- a/application/pages/modplatform/twitch/TwitchModel.h +++ b/application/pages/modplatform/twitch/TwitchModel.h @@ -36,11 +36,15 @@ public: int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; + bool canFetchMore(const QModelIndex & parent) const override; + void fetchMore(const QModelIndex & parent) override; void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback); void searchWithTerm(const QString & term); private slots: + void performPaginatedSearch(); + void logoFailed(QString logo); void logoLoaded(QString logo, QIcon out); @@ -58,6 +62,13 @@ private: QMap waitingCallbacks; QString currentSearchTerm; + int nextSearchOffset = 0; + enum SearchState { + None, + CanPossiblyFetchMore, + ResetRequested, + Finished + } searchState = None; NetJobPtr jobPtr; QByteArray response; }; 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: [PATCH 16/18] 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(-) diff --git a/api/logic/Version.cpp b/api/logic/Version.cpp index 42eac6690..6392a50fa 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)); } From 1f9378af9f04af18a95ce275c3005e4c5ad64f50 Mon Sep 17 00:00:00 2001 From: Moresteck Date: Sun, 19 Apr 2020 15:24:18 +0100 Subject: [PATCH 17/18] NOISSUE Fixed online saving in early Classic versions --- libraries/launcher/net/minecraft/Launcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java index dd7044840..29ddbd3ec 100644 --- a/libraries/launcher/net/minecraft/Launcher.java +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -143,7 +143,7 @@ public class Launcher extends Applet implements AppletStub public URL getDocumentBase() { try { - return new URL("http://www.minecraft.net/game/"); + return new URL("http", "www.minecraft.net", 80, "/game/", null); } catch (MalformedURLException e) { e.printStackTrace(); } From 5ca5661c23050d738f1d5f9ced5e7fb71eef3fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 29 Apr 2020 21:17:51 +0200 Subject: [PATCH 18/18] NOISSUE expose twitch pack url, description and author list --- .../pages/modplatform/twitch/TwitchPage.cpp | 25 +++++++++++++++++++ .../pages/modplatform/twitch/TwitchPage.ui | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/application/pages/modplatform/twitch/TwitchPage.cpp b/application/pages/modplatform/twitch/TwitchPage.cpp index 80d831339..1e9f9dbbf 100644 --- a/application/pages/modplatform/twitch/TwitchPage.cpp +++ b/application/pages/modplatform/twitch/TwitchPage.cpp @@ -59,9 +59,34 @@ void TwitchPage::onSelectionChanged(QModelIndex first, QModelIndex second) { dialog->setSuggestedPack(); } + ui->frame->clear(); return; } + current = model->data(first, Qt::UserRole).value(); + QString text = ""; + QString name = current.name; + + if (current.websiteUrl.isEmpty()) + text = name; + else + text = "" + name + ""; + if (!current.authors.empty()) { + auto authorToStr = [](Twitch::ModpackAuthor & author) { + if(author.url.isEmpty()) { + return author.name; + } + return QString("%2").arg(author.url, author.name); + }; + QStringList authorStrs; + for(auto & author: current.authors) { + authorStrs.push_back(authorToStr(author)); + } + text += tr(" by ") + authorStrs.join(", "); + } + + ui->frame->setModText(text); + ui->frame->setModDescription(current.description); suggestCurrent(); } diff --git a/application/pages/modplatform/twitch/TwitchPage.ui b/application/pages/modplatform/twitch/TwitchPage.ui index 7a8203b10..29bdc7278 100644 --- a/application/pages/modplatform/twitch/TwitchPage.ui +++ b/application/pages/modplatform/twitch/TwitchPage.ui @@ -52,12 +52,37 @@ + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + MCModInfoFrame + QFrame +
widgets/MCModInfoFrame.h
+ 1 +
+
searchEdit searchButton packView +