/* 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 #include #include #include #include "TechnicPackProcessor.h" Technic::SolderPackInstallTask::SolderPackInstallTask( shared_qobject_ptr 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::requireValueObject(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::finished, this, &Technic::SolderPackInstallTask::extractFinished); connect(&m_extractFutureWatcher, &QFutureWatcher::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 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; }