summaryrefslogtreecommitdiff
path: root/archived/projt-launcher/launcher/minecraft/Library.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'archived/projt-launcher/launcher/minecraft/Library.cpp')
-rw-r--r--archived/projt-launcher/launcher/minecraft/Library.cpp511
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);
+}