diff options
Diffstat (limited to 'meshmc/launcher/modplatform/technic/SolderPackInstallTask.cpp')
| -rw-r--r-- | meshmc/launcher/modplatform/technic/SolderPackInstallTask.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/meshmc/launcher/modplatform/technic/SolderPackInstallTask.cpp b/meshmc/launcher/modplatform/technic/SolderPackInstallTask.cpp new file mode 100644 index 0000000000..e5943ffdd4 --- /dev/null +++ b/meshmc/launcher/modplatform/technic/SolderPackInstallTask.cpp @@ -0,0 +1,236 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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/>. + * + * 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" + +#include <FileSystem.h> +#include <Json.h> +#include <QtConcurrentRun> +#include <MMCZip.h> +#include "TechnicPackProcessor.h" + +Technic::SolderPackInstallTask::SolderPackInstallTask( + shared_qobject_ptr<QNetworkAccessManager> network, const QUrl& sourceUrl, + const QString& minecraftVersion) +{ + m_sourceUrl = sourceUrl; + m_minecraftVersion = minecraftVersion; + m_network = network; +} + +bool Technic::SolderPackInstallTask::abort() +{ + if (m_abortable) { + return m_filesNetJob->abort(); + } + return false; +} + +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:\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 job = m_filesNetJob.get(); + connect(job, &NetJob::succeeded, this, + &Technic::SolderPackInstallTask::fileListSucceeded); + connect(job, &NetJob::failed, this, + &Technic::SolderPackInstallTask::downloadFailed); + m_filesNetJob->start(); +} + +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'")); + } + } catch (const JSONValidationError& e) { + emitFailed(e.cause()); + m_filesNetJob.reset(); + return; + } + m_filesNetJob = new NetJob(tr("Downloading modpack"), m_network); + int i = 0; + for (auto& modUrl : modUrls) { + auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); + m_filesNetJob->addNetAction(Net::Download::makeFile(modUrl, path)); + i++; + } + + m_modCount = modUrls.size(); + + connect(m_filesNetJob.get(), &NetJob::succeeded, this, + &Technic::SolderPackInstallTask::downloadSucceeded); + connect(m_filesNetJob.get(), &NetJob::progress, this, + &Technic::SolderPackInstallTask::downloadProgressChanged); + connect(m_filesNetJob.get(), &NetJob::failed, this, + &Technic::SolderPackInstallTask::downloadFailed); + m_filesNetJob->start(); +} + +void Technic::SolderPackInstallTask::downloadSucceeded() +{ + m_abortable = false; + + setStatus(tr("Extracting modpack")); + m_filesNetJob.reset(); + m_extractFuture = QtConcurrent::run([this]() { + int i = 0; + QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft"); + FS::ensureFolderPathExists(extractDir); + + while (m_modCount > i) { + auto path = + FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); + if (!MMCZip::extractDir(path, extractDir)) { + return false; + } + i++; + } + return true; + }); + connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, + this, &Technic::SolderPackInstallTask::extractFinished); + connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, + this, &Technic::SolderPackInstallTask::extractAborted); + m_extractFutureWatcher.setFuture(m_extractFuture); +} + +void Technic::SolderPackInstallTask::downloadFailed(QString reason) +{ + m_abortable = false; + emitFailed(reason); + m_filesNetJob.reset(); +} + +void Technic::SolderPackInstallTask::downloadProgressChanged(qint64 current, + qint64 total) +{ + m_abortable = true; + setProgress(current / 2, total); +} + +void Technic::SolderPackInstallTask::extractFinished() +{ + if (!m_extractFuture.result()) { + emitFailed(tr("Failed to extract modpack")); + return; + } + QDir extractDir(m_stagingPath); + + qDebug() << "Fixing permissions for extracted pack files..."; + QDirIterator it(extractDir, QDirIterator::Subdirectories); + while (it.hasNext()) { + auto filepath = it.next(); + QFileInfo file(filepath); + auto permissions = QFile::permissions(filepath); + auto origPermissions = permissions; + if (file.isDir()) { + // Folder +rwx for current user + permissions |= QFileDevice::Permission::ReadUser | + QFileDevice::Permission::WriteUser | + QFileDevice::Permission::ExeUser; + } else { + // File +rw for current user + permissions |= QFileDevice::Permission::ReadUser | + QFileDevice::Permission::WriteUser; + } + if (origPermissions != permissions) { + if (!QFile::setPermissions(filepath, permissions)) { + logWarning( + tr("Could not fix permissions for %1").arg(filepath)); + } else { + qDebug() << "Fixed" << filepath; + } + } + } + + shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = + new Technic::TechnicPackProcessor(); + connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, + this, &Technic::SolderPackInstallTask::emitSucceeded); + connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, + &Technic::SolderPackInstallTask::emitFailed); + packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath, + m_minecraftVersion, true); +} + +void Technic::SolderPackInstallTask::extractAborted() +{ + emitFailed(tr("Instance import has been aborted.")); + return; +} |
