diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:45:07 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:45:07 +0300 |
| commit | 31b9a8949ed0a288143e23bf739f2eb64fdc63be (patch) | |
| tree | 8a984fa143c38fccad461a77792d6864f3e82cd3 /meshmc/launcher/modplatform/technic | |
| parent | 934382c8a1ce738589dee9ee0f14e1cec812770e (diff) | |
| parent | fad6a1066616b69d7f5fef01178efdf014c59537 (diff) | |
| download | Project-Tick-31b9a8949ed0a288143e23bf739f2eb64fdc63be.tar.gz Project-Tick-31b9a8949ed0a288143e23bf739f2eb64fdc63be.zip | |
Add 'meshmc/' from commit 'fad6a1066616b69d7f5fef01178efdf014c59537'
git-subtree-dir: meshmc
git-subtree-mainline: 934382c8a1ce738589dee9ee0f14e1cec812770e
git-subtree-split: fad6a1066616b69d7f5fef01178efdf014c59537
Diffstat (limited to 'meshmc/launcher/modplatform/technic')
6 files changed, 862 insertions, 0 deletions
diff --git a/meshmc/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/meshmc/launcher/modplatform/technic/SingleZipPackInstallTask.cpp new file mode 100644 index 0000000000..e197ccbcd9 --- /dev/null +++ b/meshmc/launcher/modplatform/technic/SingleZipPackInstallTask.cpp @@ -0,0 +1,168 @@ +/* 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 "SingleZipPackInstallTask.h" + +#include <QtConcurrent> + +#include "MMCZip.h" +#include "TechnicPackProcessor.h" +#include "FileSystem.h" + +#include "Application.h" + +Technic::SingleZipPackInstallTask::SingleZipPackInstallTask( + const QUrl& sourceUrl, const QString& minecraftVersion) +{ + m_sourceUrl = sourceUrl; + m_minecraftVersion = minecraftVersion; +} + +bool Technic::SingleZipPackInstallTask::abort() +{ + if (m_abortable) { + return m_filesNetJob->abort(); + } + return false; +} + +void Technic::SingleZipPackInstallTask::executeTask() +{ + setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); + + const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path(); + auto entry = APPLICATION->metacache()->resolveEntry("general", path); + entry->setStale(true); + m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network()); + m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + m_archivePath = entry->getFullPath(); + auto job = m_filesNetJob.get(); + connect(job, &NetJob::succeeded, this, + &Technic::SingleZipPackInstallTask::downloadSucceeded); + connect(job, &NetJob::progress, this, + &Technic::SingleZipPackInstallTask::downloadProgressChanged); + connect(job, &NetJob::failed, this, + &Technic::SingleZipPackInstallTask::downloadFailed); + m_filesNetJob->start(); +} + +void Technic::SingleZipPackInstallTask::downloadSucceeded() +{ + m_abortable = false; + + setStatus(tr("Extracting modpack")); + QDir extractDir(FS::PathCombine(m_stagingPath, ".minecraft")); + qDebug() << "Attempting to create instance from" << m_archivePath; + + QString archivePath = m_archivePath; + QString extractPath = extractDir.absolutePath(); + m_extractFuture = QtConcurrent::run( + QThreadPool::globalInstance(), [archivePath, extractPath]() { + return MMCZip::extractSubDir(archivePath, QString(""), extractPath); + }); + connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, + this, &Technic::SingleZipPackInstallTask::extractFinished); + connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, + this, &Technic::SingleZipPackInstallTask::extractAborted); + m_extractFutureWatcher.setFuture(m_extractFuture); + m_filesNetJob.reset(); +} + +void Technic::SingleZipPackInstallTask::downloadFailed(QString reason) +{ + m_abortable = false; + emitFailed(reason); + m_filesNetJob.reset(); +} + +void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current, + qint64 total) +{ + m_abortable = true; + setProgress(current / 2, total); +} + +void Technic::SingleZipPackInstallTask::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::SingleZipPackInstallTask::emitSucceeded); + connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, + &Technic::SingleZipPackInstallTask::emitFailed); + packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath, + m_minecraftVersion); +} + +void Technic::SingleZipPackInstallTask::extractAborted() +{ + emitFailed(tr("Instance import has been aborted.")); +} diff --git a/meshmc/launcher/modplatform/technic/SingleZipPackInstallTask.h b/meshmc/launcher/modplatform/technic/SingleZipPackInstallTask.h new file mode 100644 index 0000000000..afad66f70b --- /dev/null +++ b/meshmc/launcher/modplatform/technic/SingleZipPackInstallTask.h @@ -0,0 +1,88 @@ +/* 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. + */ + +#pragma once + +#include "InstanceTask.h" +#include "net/NetJob.h" + +#include <QFutureWatcher> +#include <QStringList> +#include <QUrl> + +#include <nonstd/optional> + +namespace Technic +{ + + class SingleZipPackInstallTask : public InstanceTask + { + Q_OBJECT + + public: + SingleZipPackInstallTask(const QUrl& sourceUrl, + const QString& minecraftVersion); + + bool canAbort() const override + { + return true; + } + bool abort() override; + + protected: + void executeTask() override; + + private slots: + void downloadSucceeded(); + void downloadFailed(QString reason); + void downloadProgressChanged(qint64 current, qint64 total); + void extractFinished(); + void extractAborted(); + + private: + bool m_abortable = false; + + QUrl m_sourceUrl; + QString m_minecraftVersion; + QString m_archivePath; + NetJob::Ptr m_filesNetJob; + QFuture<nonstd::optional<QStringList>> m_extractFuture; + QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher; + }; + +} // namespace Technic 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; +} diff --git a/meshmc/launcher/modplatform/technic/SolderPackInstallTask.h b/meshmc/launcher/modplatform/technic/SolderPackInstallTask.h new file mode 100644 index 0000000000..8a80c37580 --- /dev/null +++ b/meshmc/launcher/modplatform/technic/SolderPackInstallTask.h @@ -0,0 +1,90 @@ +/* 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. + */ + +#pragma once + +#include <InstanceTask.h> +#include <net/NetJob.h> +#include <tasks/Task.h> + +#include <QUrl> + +namespace Technic +{ + class SolderPackInstallTask : public InstanceTask + { + Q_OBJECT + public: + explicit SolderPackInstallTask( + shared_qobject_ptr<QNetworkAccessManager> network, + const QUrl& sourceUrl, const QString& minecraftVersion); + + bool canAbort() const override + { + return true; + } + bool abort() override; + + protected: + //! Entry point for tasks. + virtual void executeTask() override; + + private slots: + void versionSucceeded(); + void fileListSucceeded(); + void downloadSucceeded(); + void downloadFailed(QString reason); + void downloadProgressChanged(qint64 current, qint64 total); + void extractFinished(); + void extractAborted(); + + private: + bool m_abortable = false; + + shared_qobject_ptr<QNetworkAccessManager> m_network; + + NetJob::Ptr m_filesNetJob; + QUrl m_sourceUrl; + QString m_minecraftVersion; + QByteArray m_response; + QTemporaryDir m_outputDir; + int m_modCount; + QFuture<bool> m_extractFuture; + QFutureWatcher<bool> m_extractFutureWatcher; + }; +} // namespace Technic diff --git a/meshmc/launcher/modplatform/technic/TechnicPackProcessor.cpp b/meshmc/launcher/modplatform/technic/TechnicPackProcessor.cpp new file mode 100644 index 0000000000..fe4e24efe9 --- /dev/null +++ b/meshmc/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -0,0 +1,218 @@ +/* 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 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 "TechnicPackProcessor.h" + +#include <FileSystem.h> +#include <Json.h> +#include <minecraft/MinecraftInstance.h> +#include <minecraft/PackProfile.h> +#include <MMCZip.h> +#include <settings/INISettingsObject.h> + +#include <memory> + +void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, + const QString& instName, + const QString& instIcon, + const QString& stagingPath, + const QString& minecraftVersion, + const bool isSolder) +{ + QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft"); + QString configPath = FS::PathCombine(stagingPath, "instance.cfg"); + auto instanceSettings = std::make_shared<INISettingsObject>(configPath); + instanceSettings->registerSetting("InstanceType", "Legacy"); + instanceSettings->set("InstanceType", "OneSix"); + MinecraftInstance instance(globalSettings, instanceSettings, stagingPath); + + instance.setName(instName); + + if (instIcon != "default") { + instance.setIconKey(instIcon); + } + + auto components = instance.getPackProfile(); + components->buildingFromScratch(); + + QByteArray data; + + QString modpackJar = FS::PathCombine(minecraftPath, "bin", "modpack.jar"); + QString versionJson = FS::PathCombine(minecraftPath, "bin", "version.json"); + QString fmlMinecraftVersion; + if (QFile::exists(modpackJar)) { + if (MMCZip::entryExists(modpackJar, "version.json")) { + if (MMCZip::entryExists(modpackJar, "fmlversion.properties")) { + QByteArray fmlVersionData = MMCZip::readFileFromZip( + modpackJar, "fmlversion.properties"); + if (fmlVersionData.isEmpty()) { + emit failed( + tr("Unable to open \"fmlversion.properties\"!")); + return; + } + INIFile iniFile; + iniFile.loadFile(fmlVersionData); + // If not present, this evaluates to a null string + fmlMinecraftVersion = iniFile["fmlbuild.mcversion"].toString(); + } + data = MMCZip::readFileFromZip(modpackJar, "version.json"); + if (data.isEmpty()) { + emit failed(tr("Unable to open \"version.json\"!")); + return; + } + } else { + if (minecraftVersion.isEmpty()) + emit failed(tr( + "Could not find \"version.json\" inside " + "\"bin/modpack.jar\", but minecraft version is unknown")); + components->setComponentVersion("net.minecraft", minecraftVersion, + true); + components->installJarMods({modpackJar}); + + // Forge for 1.4.7 and for 1.5.2 require extra libraries. + // Figure out the forge version and add it as a component + // (the code still comes from the jar mod installed above) + if (MMCZip::entryExists(modpackJar, "forgeversion.properties")) { + QByteArray forgeVersionData = MMCZip::readFileFromZip( + modpackJar, "forgeversion.properties"); + if (forgeVersionData.isEmpty()) { + // Really shouldn't happen, but error handling shall not be + // forgotten + emit failed( + tr("Unable to open \"forgeversion.properties\"")); + return; + } + INIFile iniFile; + iniFile.loadFile(forgeVersionData); + QString major, minor, revision, build; + major = iniFile["forge.major.number"].toString(); + minor = iniFile["forge.minor.number"].toString(); + revision = iniFile["forge.revision.number"].toString(); + build = iniFile["forge.build.number"].toString(); + + if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || + build.isEmpty()) { + emit failed(tr("Invalid \"forgeversion.properties\"!")); + return; + } + + components->setComponentVersion("net.minecraftforge", + major + '.' + minor + '.' + + revision + '.' + build); + } + + components->saveNow(); + emit succeeded(); + return; + } + } else if (QFile::exists(versionJson)) { + QFile file(versionJson); + if (!file.open(QIODevice::ReadOnly)) { + emit failed(tr("Unable to open \"version.json\"!")); + return; + } + data = file.readAll(); + file.close(); + } else { + // This is the "Vanilla" modpack, excluded by the search code + emit failed(tr("Unable to find a \"version.json\"!")); + return; + } + + try { + QJsonDocument doc = Json::requireDocument(data); + QJsonObject root = Json::requireObject(doc, "version.json"); + QString minecraftVersion = + Json::ensureString(root, "inheritsFrom", QString(), ""); + if (minecraftVersion.isEmpty()) { + if (fmlMinecraftVersion.isEmpty()) { + emit failed(tr("Could not understand " + "\"version.json\":\ninheritsFrom is missing")); + return; + } + minecraftVersion = fmlMinecraftVersion; + } + components->setComponentVersion("net.minecraft", minecraftVersion, + true); + for (auto library : Json::ensureArray(root, "libraries", {})) { + if (!library.isObject()) { + continue; + } + + auto libraryObject = Json::ensureObject(library, {}, ""); + auto libraryName = + Json::ensureString(libraryObject, "name", "", ""); + + if (libraryName.startsWith("net.minecraftforge:forge:") && + libraryName.contains('-')) { + QString libraryVersion = libraryName.section(':', 2); + if (!libraryVersion.startsWith("1.7.10-")) { + components->setComponentVersion( + "net.minecraftforge", libraryName.section('-', 1)); + } else { + // 1.7.10 versions sometimes look + // like 1.7.10-10.13.4.1614-1.7.10, this filters out + // the 10.13.4.1614 part + components->setComponentVersion( + "net.minecraftforge", libraryName.section('-', 1, 1)); + } + } else if (libraryName.startsWith( + "net.minecraftforge:minecraftforge:")) { + components->setComponentVersion("net.minecraftforge", + libraryName.section(':', 2)); + } else if (libraryName.startsWith("net.fabricmc:fabric-loader:")) { + components->setComponentVersion("net.fabricmc.fabric-loader", + libraryName.section(':', 2)); + } else if (libraryName.startsWith( + "net.neoforged.fancymodloader:loader:") || + libraryName.startsWith("net.neoforged:neoforge:")) { + components->setComponentVersion("net.neoforged", + libraryName.section(':', 2)); + } else if (libraryName.startsWith("org.quiltmc:quilt-loader:")) { + components->setComponentVersion("org.quiltmc.quilt-loader", + libraryName.section(':', 2)); + } + } + } catch (const JSONValidationError& e) { + emit failed(tr("Could not understand \"version.json\":\n") + e.cause()); + return; + } + + components->saveNow(); + emit succeeded(); +} diff --git a/meshmc/launcher/modplatform/technic/TechnicPackProcessor.h b/meshmc/launcher/modplatform/technic/TechnicPackProcessor.h new file mode 100644 index 0000000000..8b41a5e3f8 --- /dev/null +++ b/meshmc/launcher/modplatform/technic/TechnicPackProcessor.h @@ -0,0 +1,62 @@ +/* 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 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 <QString> +#include "settings/SettingsObject.h" + +namespace Technic +{ + // not exporting it, only used in SingleZipPackInstallTask, + // InstanceImportTask and SolderPackInstallTask + class TechnicPackProcessor : public QObject + { + Q_OBJECT + + signals: + void succeeded(); + void failed(QString reason); + + public: + void run(SettingsObjectPtr globalSettings, const QString& instName, + const QString& instIcon, const QString& stagingPath, + const QString& minecraftVersion = QString(), + const bool isSolder = false); + }; +} // namespace Technic |
