aboutsummaryrefslogtreecommitdiff
path: root/launcher
diff options
context:
space:
mode:
authorSefa Eyeoglu <contact@scrumplex.net>2022-04-06 10:52:27 +0200
committerGitHub <noreply@github.com>2022-04-06 10:52:27 +0200
commit99d569ed0e982291f167155a9eb86d1c6e659376 (patch)
tree8fd9cebf676ca745d743724b3568a2e75d3c32f9 /launcher
parenta1a7b9c15171c09131ee8b8a3d9e81355728cfb1 (diff)
parentb6e722a048aabd35d6e2d8db3ad26e50b1f5a7fb (diff)
downloadPrismLauncher-99d569ed0e982291f167155a9eb86d1c6e659376.tar.gz
PrismLauncher-99d569ed0e982291f167155a9eb86d1c6e659376.tar.bz2
PrismLauncher-99d569ed0e982291f167155a9eb86d1c6e659376.zip
Merge pull request #384 from jamierocks/technic-improvements
Diffstat (limited to 'launcher')
-rw-r--r--launcher/CMakeLists.txt2
-rw-r--r--launcher/modplatform/technic/SolderPackInstallTask.cpp137
-rw-r--r--launcher/modplatform/technic/SolderPackInstallTask.h47
-rw-r--r--launcher/modplatform/technic/SolderPackManifest.cpp58
-rw-r--r--launcher/modplatform/technic/SolderPackManifest.h49
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicData.h46
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicModel.cpp130
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicModel.h44
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicPage.cpp143
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicPage.h11
-rw-r--r--launcher/ui/pages/modplatform/technic/TechnicPage.ui132
11 files changed, 580 insertions, 219 deletions
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 13085c4c..05af3503 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -539,6 +539,8 @@ set(TECHNIC_SOURCES
modplatform/technic/SingleZipPackInstallTask.cpp
modplatform/technic/SolderPackInstallTask.h
modplatform/technic/SolderPackInstallTask.cpp
+ modplatform/technic/SolderPackManifest.h
+ modplatform/technic/SolderPackManifest.cpp
modplatform/technic/TechnicPackProcessor.h
modplatform/technic/TechnicPackProcessor.cpp
)
diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp
index b5c91582..89dbf4ca 100644
--- a/launcher/modplatform/technic/SolderPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp
@@ -1,16 +1,36 @@
-/* Copyright 2013-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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.
*/
#include "SolderPackInstallTask.h"
@@ -19,16 +39,23 @@
#include <Json.h>
#include <QtConcurrentRun>
#include <MMCZip.h>
+
#include "TechnicPackProcessor.h"
+#include "SolderPackManifest.h"
+#include "net/ChecksumValidator.h"
Technic::SolderPackInstallTask::SolderPackInstallTask(
shared_qobject_ptr<QNetworkAccessManager> network,
- const QUrl &sourceUrl,
+ const QUrl &solderUrl,
+ const QString &pack,
+ const QString &version,
const QString &minecraftVersion
) {
- m_sourceUrl = sourceUrl;
- m_minecraftVersion = minecraftVersion;
+ m_solderUrl = solderUrl;
+ m_pack = pack;
+ m_version = version;
m_network = network;
+ m_minecraftVersion = minecraftVersion;
}
bool Technic::SolderPackInstallTask::abort() {
@@ -41,34 +68,12 @@ bool Technic::SolderPackInstallTask::abort() {
void Technic::SolderPackInstallTask::executeTask()
{
- setStatus(tr("Finding recommended version:\n%1").arg(m_sourceUrl.toString()));
- m_filesNetJob = new NetJob(tr("Finding recommended version"), m_network);
- m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
- auto job = m_filesNetJob.get();
- connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::versionSucceeded);
- connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
- m_filesNetJob->start();
-}
-
-void Technic::SolderPackInstallTask::versionSucceeded()
-{
- try
- {
- QJsonDocument doc = Json::requireDocument(m_response);
- QJsonObject obj = Json::requireObject(doc);
- QString version = Json::requireString(obj, "recommended", "__placeholder__");
- m_sourceUrl = m_sourceUrl.toString() + '/' + version;
- }
- catch (const JSONValidationError &e)
- {
- emitFailed(e.cause());
- m_filesNetJob.reset();
- return;
- }
+ setStatus(tr("Resolving modpack files"));
- setStatus(tr("Resolving modpack files:\n%1").arg(m_sourceUrl.toString()));
m_filesNetJob = new NetJob(tr("Resolving modpack files"), m_network);
- m_filesNetJob->addNetAction(Net::Download::makeByteArray(m_sourceUrl, &m_response));
+ auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version);
+ m_filesNetJob->addNetAction(Net::Download::makeByteArray(sourceUrl, &m_response));
+
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SolderPackInstallTask::fileListSucceeded);
connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
@@ -77,38 +82,47 @@ void Technic::SolderPackInstallTask::versionSucceeded()
void Technic::SolderPackInstallTask::fileListSucceeded()
{
- setStatus(tr("Downloading modpack:"));
- QStringList modUrls;
- try
- {
- QJsonDocument doc = Json::requireDocument(m_response);
- QJsonObject obj = Json::requireObject(doc);
- QString minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
- if (!minecraftVersion.isEmpty())
- m_minecraftVersion = minecraftVersion;
- QJsonArray mods = Json::requireArray(obj, "mods", "'mods'");
- for (auto mod: mods)
- {
- QJsonObject modObject = Json::requireObject(mod);
- modUrls.append(Json::requireString(modObject, "url", "'url'"));
- }
+ setStatus(tr("Downloading modpack"));
+
+ QJsonParseError parse_error {};
+ QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << m_response;
+ return;
}
- catch (const JSONValidationError &e)
- {
- emitFailed(e.cause());
+ auto obj = doc.object();
+
+ TechnicSolder::PackBuild build;
+ try {
+ TechnicSolder::loadPackBuild(build, obj);
+ }
+ catch (const JSONValidationError& e) {
+ emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
m_filesNetJob.reset();
return;
}
+
+ if (!build.minecraft.isEmpty())
+ m_minecraftVersion = build.minecraft;
+
m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network);
+
int i = 0;
- for (auto &modUrl: modUrls)
- {
+ for (const auto &mod : build.mods) {
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
- m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, path));
+
+ auto dl = Net::Download::makeFile(mod.url, path);
+ if (!mod.md5.isEmpty()) {
+ auto rawMd5 = QByteArray::fromHex(mod.md5.toLatin1());
+ dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
+ }
+ m_filesNetJob->addNetAction(dl);
+
i++;
}
- m_modCount = modUrls.size();
+ m_modCount = build.mods.size();
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
@@ -206,6 +220,5 @@ void Technic::SolderPackInstallTask::extractFinished()
void Technic::SolderPackInstallTask::extractAborted()
{
emitFailed(tr("Instance import has been aborted."));
- return;
}
diff --git a/launcher/modplatform/technic/SolderPackInstallTask.h b/launcher/modplatform/technic/SolderPackInstallTask.h
index 9b2058d8..117a7bd6 100644
--- a/launcher/modplatform/technic/SolderPackInstallTask.h
+++ b/launcher/modplatform/technic/SolderPackInstallTask.h
@@ -1,16 +1,36 @@
-/* Copyright 2013-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 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
@@ -27,7 +47,7 @@ namespace Technic
{
Q_OBJECT
public:
- explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &sourceUrl, const QString &minecraftVersion);
+ explicit SolderPackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, const QUrl &solderUrl, const QString& pack, const QString& version, const QString &minecraftVersion);
bool canAbort() const override { return true; }
bool abort() override;
@@ -37,7 +57,6 @@ namespace Technic
virtual void executeTask() override;
private slots:
- void versionSucceeded();
void fileListSucceeded();
void downloadSucceeded();
void downloadFailed(QString reason);
@@ -51,7 +70,9 @@ namespace Technic
shared_qobject_ptr<QNetworkAccessManager> m_network;
NetJob::Ptr m_filesNetJob;
- QUrl m_sourceUrl;
+ QUrl m_solderUrl;
+ QString m_pack;
+ QString m_version;
QString m_minecraftVersion;
QByteArray m_response;
QTemporaryDir m_outputDir;
diff --git a/launcher/modplatform/technic/SolderPackManifest.cpp b/launcher/modplatform/technic/SolderPackManifest.cpp
new file mode 100644
index 00000000..16fe0b0e
--- /dev/null
+++ b/launcher/modplatform/technic/SolderPackManifest.cpp
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "SolderPackManifest.h"
+
+#include "Json.h"
+
+namespace TechnicSolder {
+
+void loadPack(Pack& v, QJsonObject& obj)
+{
+ v.recommended = Json::requireString(obj, "recommended");
+ v.latest = Json::requireString(obj, "latest");
+
+ auto builds = Json::requireArray(obj, "builds");
+ for (const auto buildRaw : builds) {
+ auto build = Json::requireString(buildRaw);
+ v.builds.append(build);
+ }
+}
+
+static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj)
+{
+ b.name = Json::requireString(obj, "name");
+ b.version = Json::requireString(obj, "version");
+ b.md5 = Json::requireString(obj, "md5");
+ b.url = Json::requireString(obj, "url");
+}
+
+void loadPackBuild(PackBuild& v, QJsonObject& obj)
+{
+ v.minecraft = Json::requireString(obj, "minecraft");
+
+ auto mods = Json::requireArray(obj, "mods");
+ for (const auto modRaw : mods) {
+ auto modObj = Json::requireObject(modRaw);
+ PackBuildMod mod;
+ loadPackBuildMod(mod, modObj);
+ v.mods.append(mod);
+ }
+}
+
+}
diff --git a/launcher/modplatform/technic/SolderPackManifest.h b/launcher/modplatform/technic/SolderPackManifest.h
new file mode 100644
index 00000000..09f18df0
--- /dev/null
+++ b/launcher/modplatform/technic/SolderPackManifest.h
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QString>
+#include <QVector>
+#include <QJsonObject>
+
+namespace TechnicSolder {
+
+struct Pack {
+ QString recommended;
+ QString latest;
+ QVector<QString> builds;
+};
+
+void loadPack(Pack& v, QJsonObject& obj);
+
+struct PackBuildMod {
+ QString name;
+ QString version;
+ QString md5;
+ QString url;
+};
+
+struct PackBuild {
+ QString minecraft;
+ QVector<PackBuildMod> mods;
+};
+
+void loadPackBuild(PackBuild& v, QJsonObject& obj);
+
+}
diff --git a/launcher/ui/pages/modplatform/technic/TechnicData.h b/launcher/ui/pages/modplatform/technic/TechnicData.h
index 50fd75e8..cd2ea8e1 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicData.h
+++ b/launcher/ui/pages/modplatform/technic/TechnicData.h
@@ -1,22 +1,43 @@
-/* Copyright 2020-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 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 <QList>
#include <QString>
+#include <QVector>
namespace Technic {
struct Modpack {
@@ -36,6 +57,11 @@ struct Modpack {
QString websiteUrl;
QString author;
QString description;
+ QString currentVersion;
+
+ bool versionsLoaded = false;
+ QString recommended;
+ QVector<QString> versions;
};
}
diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
index 0167f746..9c9d1e75 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp
@@ -1,20 +1,41 @@
-/* Copyright 2020-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 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.
*/
#include "TechnicModel.h"
#include "Application.h"
+#include "BuildConfig.h"
#include "Json.h"
#include <QIcon>
@@ -94,13 +115,24 @@ void Technic::ListModel::performSearch()
NetJob *netJob = new NetJob("Technic::Search", APPLICATION->network());
QString searchUrl = "";
if (currentSearchTerm.isEmpty()) {
- searchUrl = "https://api.technicpack.net/trending?build=multimc";
+ searchUrl = QString("%1trending?build=%2")
+ .arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD);
+ searchMode = List;
}
- else
- {
+ else if (currentSearchTerm.startsWith("http://api.technicpack.net/modpack/")) {
+ searchUrl = QString("https://%1?build=%2")
+ .arg(currentSearchTerm.mid(7), BuildConfig.TECHNIC_API_BUILD);
+ searchMode = Single;
+ }
+ else if (currentSearchTerm.startsWith("https://api.technicpack.net/modpack/")) {
+ searchUrl = QString("%1?build=%2").arg(currentSearchTerm, BuildConfig.TECHNIC_API_BUILD);
+ searchMode = Single;
+ }
+ else {
searchUrl = QString(
- "https://api.technicpack.net/search?build=multimc&q=%1"
- ).arg(currentSearchTerm);
+ "%1search?build=%2&q=%3"
+ ).arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
+ searchMode = List;
}
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
jobPtr = netJob;
@@ -125,26 +157,58 @@ void Technic::ListModel::searchRequestFinished()
QList<Modpack> newList;
try {
auto root = Json::requireObject(doc);
- auto objs = Json::requireArray(root, "modpacks");
- for (auto technicPack: objs) {
- Modpack pack;
- auto technicPackObject = Json::requireObject(technicPack);
- pack.name = Json::requireString(technicPackObject, "name");
- pack.slug = Json::requireString(technicPackObject, "slug");
- if (pack.slug == "vanilla")
- continue;
-
- auto rawURL = Json::ensureString(technicPackObject, "iconUrl", "null");
- if(rawURL == "null") {
- pack.logoUrl = "null";
- pack.logoName = "null";
+
+ switch (searchMode) {
+ case List: {
+ auto objs = Json::requireArray(root, "modpacks");
+ for (auto technicPack: objs) {
+ Modpack pack;
+ auto technicPackObject = Json::requireObject(technicPack);
+ pack.name = Json::requireString(technicPackObject, "name");
+ pack.slug = Json::requireString(technicPackObject, "slug");
+ if (pack.slug == "vanilla")
+ continue;
+
+ auto rawURL = Json::ensureString(technicPackObject, "iconUrl", "null");
+ if(rawURL == "null") {
+ pack.logoUrl = "null";
+ pack.logoName = "null";
+ }
+ else {
+ pack.logoUrl = rawURL;
+ pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
+ }
+ pack.broken = false;
+ newList.append(pack);
+ }
+ break;
}
- else {
- pack.logoUrl = rawURL;
- pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
+ case Single: {
+ if (root.contains("error")) {
+ // Invalid API url
+ break;
+ }
+
+ Modpack pack;
+ pack.name = Json::requireString(root, "displayName");
+ pack.slug = Json::requireString(root, "name");
+
+ if (root.contains("icon")) {
+ auto iconObj = Json::requireObject(root, "icon");
+ auto iconUrl = Json::requireString(iconObj, "url");
+
+ pack.logoUrl = iconUrl;
+ pack.logoName = iconUrl.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
+ }
+ else {
+ pack.logoUrl = "null";
+ pack.logoName = "null";
+ }
+
+ pack.broken = false;
+ newList.append(pack);
+ break;
}
- pack.broken = false;
- newList.append(pack);
}
}
catch (const JSONValidationError &err)
diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.h b/launcher/ui/pages/modplatform/technic/TechnicModel.h
index e80e6e7c..5eea124c 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicModel.h
+++ b/launcher/ui/pages/modplatform/technic/TechnicModel.h
@@ -1,16 +1,36 @@
-/* Copyright 2020-2021 MultiMC Contributors
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2021 Jamie Mansfield <jmansfield@cadixdev.org>
*
- * 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2020-2021 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
@@ -63,6 +83,10 @@ private:
ResetRequested,
Finished
} searchState = None;
+ enum SearchMode {
+ List,
+ Single,
+ } searchMode = List;
NetJob::Ptr jobPtr;
QByteArray response;
};
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
index c3807269..b8c1e00a 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,12 +40,14 @@
#include "ui/dialogs/NewInstanceDialog.h"
+#include "BuildConfig.h"
#include "TechnicModel.h"
#include "modplatform/technic/SingleZipPackInstallTask.h"
#include "modplatform/technic/SolderPackInstallTask.h"
#include "Json.h"
#include "Application.h"
+#include "modplatform/technic/SolderPackManifest.h"
TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
@@ -55,7 +57,9 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
ui->searchEdit->installEventFilter(this);
model = new Technic::ListModel(this);
ui->packView->setModel(model);
+
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged);
+ connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged);
}
bool TechnicPage::eventFilter(QObject* watched, QEvent* event)
@@ -98,13 +102,14 @@ void TechnicPage::triggerSearch() {
void TechnicPage::onSelectionChanged(QModelIndex first, QModelIndex second)
{
+ ui->versionSelectionBox->clear();
+
if(!first.isValid())
{
if(isOpened)
{
dialog->setSuggestedPack();
}
- //ui->frame->clear();
return;
}
@@ -137,17 +142,19 @@ void TechnicPage::suggestCurrent()
}
NetJob *netJob = new NetJob(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network());
- std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
QString slug = current.slug;
- netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.technicpack.net/modpack/%1?build=multimc").arg(slug), response.get()));
- QObject::connect(netJob, &NetJob::succeeded, this, [this, response, slug]
+ netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response));
+ QObject::connect(netJob, &NetJob::succeeded, this, [this, slug]
{
+ jobPtr.reset();
+
if (current.slug != slug)
{
return;
}
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
+
+ QJsonParseError parse_error {};
+ QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
QJsonObject obj = doc.object();
if(parse_error.error != QJsonParseError::NoError)
{
@@ -189,10 +196,14 @@ void TechnicPage::suggestCurrent()
current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__");
current.author = Json::ensureString(obj, "user", QString(), "__placeholder__");
current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
+ current.currentVersion = Json::ensureString(obj, "version", QString(), "__placeholder__");
current.metadataLoaded = true;
+
metadataLoaded();
});
- netJob->start();
+
+ jobPtr = netJob;
+ jobPtr->start();
}
// expects current.metadataLoaded to be true
@@ -202,25 +213,119 @@ void TechnicPage::metadataLoaded()
QString name = current.name;
if (current.websiteUrl.isEmpty())
- // This allows injecting HTML here.
- text = name;
+ text = name.toHtmlEscaped();
else
- // URL not properly escaped for inclusion in HTML. The name allows for injecting HTML.
- text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
+ text = "<a href=\"" + current.websiteUrl.toHtmlEscaped() + "\">" + name.toHtmlEscaped() + "</a>";
+
if (!current.author.isEmpty()) {
- // This allows injecting HTML here
- text += tr(" by ") + current.author;
+ text += "<br>" + tr(" by ") + current.author.toHtmlEscaped();
+ }
+
+ text += "<br><br>";
+
+ ui->packDescription->setHtml(text + current.description);
+
+ // Strip trailing forward-slashes from Solder URL's
+ if (current.isSolder) {
+ while (current.url.endsWith('/')) current.url.chop(1);
+ }
+
+ // Display versions from Solder
+ if (!current.isSolder) {
+ // If the pack isn't a Solder pack, it only has the single version
+ ui->versionSelectionBox->addItem(current.currentVersion);
+ }
+ else if (current.versionsLoaded) {
+ // reverse foreach, so that the newest versions are first
+ for (auto i = current.versions.size(); i--;) {
+ ui->versionSelectionBox->addItem(current.versions.at(i));
+ }
+ ui->versionSelectionBox->setCurrentText(current.recommended);
+ }
+ else {
+ // For now, until the versions are pulled from the Solder instance, display the current
+ // version so we can display something quicker
+ ui->versionSelectionBox->addItem(current.currentVersion);
+
+ auto* netJob = new NetJob(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network());
+ auto url = QString("%1/modpack/%2").arg(current.url, current.slug);
+ netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
+
+ QObject::connect(netJob, &NetJob::succeeded, this, &TechnicPage::onSolderLoaded);
+
+ jobPtr = netJob;
+ jobPtr->start();
+ }
+
+ selectVersion();
+}
+
+void TechnicPage::selectVersion() {
+ if (!isOpened) {
+ return;
+ }
+ if (current.broken) {
+ dialog->setSuggestedPack();
+ return;
}
- ui->frame->setModText(text);
- ui->frame->setModDescription(current.description);
if (!current.isSolder)
{
- dialog->setSuggestedPack(current.name, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));
+ dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));
}
else
{
- while (current.url.endsWith('/')) current.url.chop(1);
- dialog->setSuggestedPack(current.name, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url + "/modpack/" + current.slug, current.minecraftVersion));
+ dialog->setSuggestedPack(current.name + " " + selectedVersion, new Technic::SolderPackInstallTask(APPLICATION->network(), current.url, current.slug, selectedVersion, current.minecraftVersion));
+ }
+}
+
+void TechnicPage::onSolderLoaded() {
+ jobPtr.reset();
+
+ auto fallback = [this]() {
+ current.versionsLoaded = true;
+
+ current.versions.clear();
+ current.versions.append(current.currentVersion);
+ };
+
+ current.versions.clear();
+
+ QJsonParseError parse_error {};
+ auto doc = QJsonDocument::fromJson(response, &parse_error);
+ if (parse_error.error != QJsonParseError::NoError) {
+ qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
+ qWarning() << response;
+ fallback();
+ return;
+ }
+ auto obj = doc.object();
+
+ TechnicSolder::Pack pack;
+ try {
+ TechnicSolder::loadPack(pack, obj);
+ }
+ catch (const JSONValidationError& err) {
+ qCritical() << "Couldn't parse Solder pack metadata:" << err.cause();
+ fallback();
+ return;
+ }
+
+ current.versionsLoaded = true;
+ current.recommended = pack.recommended;
+ current.versions.append(pack.builds);
+
+ // Finally, let's reload :)
+ ui->versionSelectionBox->clear();
+ metadataLoaded();
+}
+
+void TechnicPage::onVersionSelectionChanged(QString data) {
+ if (data.isNull() || data.isEmpty()) {
+ selectedVersion = "";
+ return;
}
+
+ selectedVersion = data;
+ selectVersion();
}
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.h b/launcher/ui/pages/modplatform/technic/TechnicPage.h
index bf4baa58..f4a3b61d 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.h
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
+ * Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@
#include "ui/pages/BasePage.h"
#include <Application.h>
+#include "net/NetJob.h"
#include "tasks/Task.h"
#include "TechnicData.h"
@@ -86,14 +87,22 @@ public:
private:
void suggestCurrent();
void metadataLoaded();
+ void selectVersion();
private slots:
void triggerSearch();
void onSelectionChanged(QModelIndex first, QModelIndex second);
+ void onSolderLoaded();
+ void onVersionSelectionChanged(QString data);
private:
Ui::TechnicPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
Technic::ListModel* model = nullptr;
+
Technic::Modpack current;
+ QString selectedVersion;
+
+ NetJob::Ptr jobPtr;
+ QByteArray response;
};
diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.ui b/launcher/ui/pages/modplatform/technic/TechnicPage.ui
index 62ab6154..ca6a9b7e 100644
--- a/launcher/ui/pages/modplatform/technic/TechnicPage.ui
+++ b/launcher/ui/pages/modplatform/technic/TechnicPage.ui
@@ -10,86 +10,76 @@
<height>405</height>
</rect>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QWidget" name="widget" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QLineEdit" name="searchEdit">
- <property name="placeholderText">
- <string>Search and filter...</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="searchButton">
- <property name="text">
- <string>Search</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="3" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="2">
+ <widget class="QComboBox" name="versionSelectionBox"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Version selected:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
</item>
- <item>
- <widget class="QListView" name="packView">
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="alternatingRowColors">
- <bool>true</bool>
- </property>
- <property name="iconSize">
- <size>
- <width>48</width>
- <height>48</height>
- </size>
+ <item row="2" column="0" colspan="2">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QListView" name="packView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QTextBrowser" name="packDescription"/>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLineEdit" name="searchEdit">
+ <property name="placeholderText">
+ <string>Search and filter...</string>
</property>
</widget>
</item>
- <item>
- <widget class="MCModInfoFrame" name="frame">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="searchButton">
+ <property name="text">
+ <string>Search</string>
</property>
</widget>
</item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>MCModInfoFrame</class>
- <extends>QFrame</extends>
- <header>ui/widgets/MCModInfoFrame.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <tabstops>
- <tabstop>searchEdit</tabstop>
- <tabstop>searchButton</tabstop>
- <tabstop>packView</tabstop>
- </tabstops>
<resources/>
<connections/>
</ui>