diff options
Diffstat (limited to 'archived/projt-launcher/launcher/minecraft/Library.cpp')
| -rw-r--r-- | archived/projt-launcher/launcher/minecraft/Library.cpp | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/minecraft/Library.cpp b/archived/projt-launcher/launcher/minecraft/Library.cpp new file mode 100644 index 0000000000..551f572cc8 --- /dev/null +++ b/archived/projt-launcher/launcher/minecraft/Library.cpp @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * === Upstream License Block (Do Not Modify) ============================== + * + * + * + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * 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 "Library.h" +#include "MinecraftInstance.h" +#include "net/NetRequest.h" + +#include <BuildConfig.h> +#include <FileSystem.h> +#include <net/ApiDownload.h> +#include <net/ChecksumValidator.h> +#include <QUrl> + +static QString normalizeNeoForgedMavenUrl(const QString& url) +{ + const QUrl parsed(url); + if (!parsed.isValid() || parsed.host().compare("maven.neoforged.net", Qt::CaseInsensitive) != 0) + { + return url; + } + + const auto path = parsed.path(); + if (!path.startsWith("/net/neoforged/") || path.startsWith("/releases/")) + { + return url; + } + + QUrl fixed(parsed); + fixed.setPath("/releases" + path); + return fixed.toString(QUrl::FullyEncoded); +} + +/** + * @brief Collect applicable files for the library. + * + * Depending on whether the library is native or not, it adds paths to the + * appropriate lists for jar files, native libraries for 32-bit, and native + * libraries for 64-bit. + * + * @param runtimeContext The current runtime context. + * @param jar List to store paths for jar files. + * @param native List to store paths for native libraries. + * @param native32 List to store paths for 32-bit native libraries. + * @param native64 List to store paths for 64-bit native libraries. + * @param overridePath Optional path to override the default storage path. + */ +void Library::getApplicableFiles(const RuntimeContext& runtimeContext, + QStringList& jar, + QStringList& native, + QStringList& native32, + QStringList& native64, + const QString& overridePath) const +{ + bool local = isLocal(); + // Lambda function to get the absolute file path + auto actualPath = [this, local, overridePath](QString relPath) + { + relPath = FS::RemoveInvalidPathChars(relPath); + QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); + if (local && !overridePath.isEmpty()) + { + QString fileName = out.fileName(); + return QFileInfo(FS::PathCombine(overridePath, fileName)).absoluteFilePath(); + } + return out.absoluteFilePath(); + }; + + QString raw_storage = storageSuffix(runtimeContext); + if (isNative()) + { + if (raw_storage.contains("${arch}")) + { + auto nat32Storage = raw_storage; + nat32Storage.replace("${arch}", "32"); + auto nat64Storage = raw_storage; + nat64Storage.replace("${arch}", "64"); + native32 += actualPath(nat32Storage); + native64 += actualPath(nat64Storage); + } + else + { + native += actualPath(raw_storage); + } + } + else + { + jar += actualPath(raw_storage); + } +} + +/** + * @brief Get download requests for the library files. + * + * Depending on whether the library is native or not, and the current runtime context, + * this function prepares download requests for the necessary files. It handles both local + * and remote files, checks for stale cache entries, and adds checksummed downloads. + * + * @param runtimeContext The current runtime context. + * @param cache Pointer to the HTTP meta cache. + * @param failedLocalFiles List to store paths for failed local files. + * @param overridePath Optional path to override the default storage path. + * @return QList<Net::NetRequest::Ptr> List of download requests. + */ +QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeContext, + class HttpMetaCache* cache, + QStringList& failedLocalFiles, + const QString& overridePath) const +{ + QList<Net::NetRequest::Ptr> out; + bool stale = isAlwaysStale(); + bool local = isLocal(); + + // Lambda function to check if a local file exists + auto check_local_file = [overridePath, &failedLocalFiles](QString storage) + { + QFileInfo fileinfo(storage); + QString fileName = fileinfo.fileName(); + auto fullPath = FS::PathCombine(overridePath, fileName); + QFileInfo localFileInfo(fullPath); + if (!localFileInfo.exists()) + { + failedLocalFiles.append(localFileInfo.filePath()); + return false; + } + return true; + }; + + // Lambda function to add a download request + auto add_download = [this, local, check_local_file, cache, stale, &out](QString storage, QString url, QString sha1) + { + if (local) + { + return check_local_file(storage); + } + url = normalizeNeoForgedMavenUrl(url); + auto entry = cache->resolveEntry("libraries", storage); + if (stale) + { + entry->setStale(true); + } + if (!entry->isStale()) + return true; + Net::Download::Options options; + if (stale) + { + options |= Net::Download::Option::AcceptLocalFiles; + } + + // Don't add a time limit for the libraries cache entry validity + options |= Net::Download::Option::MakeEternal; + + if (sha1.size()) + { + auto dl = Net::ApiDownload::makeCached(url, entry, options); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, sha1)); + qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; + out.append(dl); + } + else + { + out.append(Net::ApiDownload::makeCached(url, entry, options)); + qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; + } + return true; + }; + + QString raw_storage = storageSuffix(runtimeContext); + if (m_mojangDownloads) + { + if (isNative()) + { + auto nativeClassifier = getCompatibleNative(runtimeContext); + if (!nativeClassifier.isNull()) + { + if (nativeClassifier.contains("${arch}")) + { + auto nat32Classifier = nativeClassifier; + nat32Classifier.replace("${arch}", "32"); + auto nat64Classifier = nativeClassifier; + nat64Classifier.replace("${arch}", "64"); + auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); + if (nat32info) + { + auto cooked_storage = raw_storage; + cooked_storage.replace("${arch}", "32"); + add_download(cooked_storage, nat32info->url, nat32info->sha1); + } + auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); + if (nat64info) + { + auto cooked_storage = raw_storage; + cooked_storage.replace("${arch}", "64"); + add_download(cooked_storage, nat64info->url, nat64info->sha1); + } + } + else + { + auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); + if (info) + { + add_download(raw_storage, info->url, info->sha1); + } + } + } + else + { + qDebug() << "Ignoring native library" << m_name.serialize() + << "because it has no classifier for current OS"; + } + } + else + { + if (m_mojangDownloads->artifact) + { + auto artifact = m_mojangDownloads->artifact; + add_download(raw_storage, artifact->url, artifact->sha1); + } + else + { + qDebug() << "Ignoring java library" << m_name.serialize() << "because it has no artifact"; + } + } + } + else + { + auto raw_dl = [this, raw_storage]() + { + if (!m_absoluteURL.isEmpty()) + { + return m_absoluteURL; + } + + if (m_repositoryURL.isEmpty()) + { + return BuildConfig.LIBRARY_BASE + raw_storage; + } + + if (m_repositoryURL.endsWith('/')) + { + return m_repositoryURL + raw_storage; + } + else + { + return m_repositoryURL + QChar('/') + raw_storage; + } + }(); + if (raw_storage.contains("${arch}")) + { + QString cooked_storage = raw_storage; + QString cooked_dl = raw_dl; + add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"), QString()); + cooked_storage = raw_storage; + cooked_dl = raw_dl; + add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"), QString()); + } + else + { + add_download(raw_storage, raw_dl, QString()); + } + } + return out; +} + +/** + * @brief Check if the library is active in the given runtime context. + * + * This function evaluates rules to determine if the library should be active, + * considering both general rules and native compatibility. + * + * @param runtimeContext The current runtime context. + * @return bool True if the library is active, false otherwise. + */ +bool Library::isActive(const RuntimeContext& runtimeContext) const +{ + bool result = true; + if (m_rules.empty()) + { + result = true; + } + else + { + Rule::Action ruleResult = Rule::Disallow; + for (auto rule : m_rules) + { + Rule::Action temp = rule.apply(runtimeContext); + if (temp != Rule::Defer) + ruleResult = temp; + } + result = result && (ruleResult == Rule::Allow); + } + if (isNative()) + { + result = result && !getCompatibleNative(runtimeContext).isNull(); + } + return result; +} + +/** + * @brief Check if the library is considered local. + * + * @return bool True if the library is local, false otherwise. + */ +bool Library::isLocal() const +{ + return m_hint == "local"; +} + +/** + * @brief Check if the library is always considered stale. + * + * @return bool True if the library is always stale, false otherwise. + */ +bool Library::isAlwaysStale() const +{ + return m_hint == "always-stale"; +} + +/** + * @brief Get the compatible native classifier for the current runtime context. + * + * This function attempts to match the current runtime context with the appropriate + * native classifier. + * + * @param runtimeContext The current runtime context. + * @return QString The compatible native classifier, or an empty string if none is found. + */ +QString Library::getCompatibleNative(const RuntimeContext& runtimeContext) const +{ + // try to match precise classifier "[os]-[arch]" + auto entry = m_nativeClassifiers.constFind(runtimeContext.getClassifier()); + // try to match imprecise classifier on legacy architectures "[os]" + if (entry == m_nativeClassifiers.constEnd() && runtimeContext.isLegacyArch()) + entry = m_nativeClassifiers.constFind(runtimeContext.system); + + if (entry == m_nativeClassifiers.constEnd()) + return QString(); + + return entry.value(); +} + +/** + * @brief Set the storage prefix for the library. + * + * @param prefix The storage prefix to set. + */ +void Library::setStoragePrefix(QString prefix) +{ + m_storagePrefix = prefix; +} + +/** + * @brief Get the default storage prefix for libraries. + * + * @return QString The default storage prefix. + */ +QString Library::defaultStoragePrefix() +{ + return "libraries/"; +} + +/** + * @brief Get the current storage prefix for the library. + * + * @return QString The current storage prefix. + */ +QString Library::storagePrefix() const +{ + if (m_storagePrefix.isEmpty()) + { + return defaultStoragePrefix(); + } + return m_storagePrefix; +} + +/** + * @brief Get the filename for the library in the current runtime context. + * + * This function determines the appropriate filename for the library, taking into + * account native classifiers if applicable. + * + * @param runtimeContext The current runtime context. + * @return QString The filename of the library. + */ +QString Library::filename(const RuntimeContext& runtimeContext) const +{ + if (!m_filename.isEmpty()) + { + return m_filename; + } + // non-native? use only the gradle specifier + if (!isNative()) + { + return m_name.getFileName(); + } + + // otherwise native, override classifiers. Mojang HACK! + GradleSpecifier nativeSpec = m_name; + QString nativeClassifier = getCompatibleNative(runtimeContext); + if (!nativeClassifier.isNull()) + { + nativeSpec.setClassifier(nativeClassifier); + } + else + { + nativeSpec.setClassifier("INVALID"); + } + return nativeSpec.getFileName(); +} + +/** + * @brief Get the display name for the library in the current runtime context. + * + * This function returns the display name for the library, defaulting to the filename + * if no display name is set. + * + * @param runtimeContext The current runtime context. + * @return QString The display name of the library. + */ +QString Library::displayName(const RuntimeContext& runtimeContext) const +{ + if (!m_displayname.isEmpty()) + return m_displayname; + return filename(runtimeContext); +} + +/** + * @brief Get the storage suffix for the library in the current runtime context. + * + * This function determines the appropriate storage suffix for the library, taking into + * account native classifiers if applicable. + * + * @param runtimeContext The current runtime context. + * @return QString The storage suffix of the library. + */ +QString Library::storageSuffix(const RuntimeContext& runtimeContext) const +{ + // non-native? use only the gradle specifier + if (!isNative()) + { + return m_name.toPath(m_filename); + } + + // otherwise native, override classifiers. Mojang HACK! + GradleSpecifier nativeSpec = m_name; + QString nativeClassifier = getCompatibleNative(runtimeContext); + if (!nativeClassifier.isNull()) + { + nativeSpec.setClassifier(nativeClassifier); + } + else + { + nativeSpec.setClassifier("INVALID"); + } + return nativeSpec.toPath(m_filename); +} |
