From 09d258670aba47a2a0783ee8c443083a89342336 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 28 Jun 2024 19:26:11 +0300 Subject: [PATCH 01/32] create mod meta information when importing curseforge pack Signed-off-by: Trial97 --- .../minecraft/mod/tasks/LocalModUpdateTask.h | 4 +- launcher/modplatform/EnsureMetadataTask.h | 4 +- .../modplatform/flame/FileResolvingTask.cpp | 294 ++++++++++-------- .../modplatform/flame/FileResolvingTask.h | 39 ++- .../flame/FlameInstanceCreationTask.cpp | 77 +++-- .../flame/FlameInstanceCreationTask.h | 2 +- launcher/modplatform/flame/PackManifest.cpp | 32 -- launcher/modplatform/flame/PackManifest.h | 14 +- 8 files changed, 243 insertions(+), 223 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h index 0809992940..5447083ba6 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h @@ -42,6 +42,6 @@ class LocalModUpdateTask : public Task { private: QDir m_index_dir; - ModPlatform::IndexedPack& m_mod; - ModPlatform::IndexedVersion& m_mod_version; + ModPlatform::IndexedPack m_mod; + ModPlatform::IndexedVersion m_mod_version; }; diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index 2f276e5a02..a32a9fce15 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -1,14 +1,14 @@ #pragma once #include "ModIndex.h" -#include "net/NetJob.h" #include "modplatform/helpers/HashUtils.h" #include "tasks/ConcurrentTask.h" +#include + class Mod; -class QDir; class EnsureMetadataTask : public Task { Q_OBJECT diff --git a/launcher/modplatform/flame/FileResolvingTask.cpp b/launcher/modplatform/flame/FileResolvingTask.cpp index 8d23896d9e..7a6c80f95d 100644 --- a/launcher/modplatform/flame/FileResolvingTask.cpp +++ b/launcher/modplatform/flame/FileResolvingTask.cpp @@ -1,86 +1,103 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * 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, see . + */ + #include "FileResolvingTask.h" +#include #include "Json.h" +#include "QObjectPtr.h" #include "modplatform/ModIndex.h" -#include "net/ApiDownload.h" -#include "net/ApiUpload.h" -#include "net/Upload.h" +#include "modplatform/flame/FlameAPI.h" +#include "modplatform/flame/FlameModIndex.h" +#include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" +#include "net/NetJob.h" +#include "tasks/Task.h" + +static const FlameAPI flameAPI; +static ModrinthAPI modrinthAPI; Flame::FileResolvingTask::FileResolvingTask(const shared_qobject_ptr& network, Flame::Manifest& toProcess) - : m_network(network), m_toProcess(toProcess) + : m_network(network), m_manifest(toProcess) {} bool Flame::FileResolvingTask::abort() { bool aborted = true; - if (m_dljob) - aborted &= m_dljob->abort(); - if (m_checkJob) - aborted &= m_checkJob->abort(); + if (m_task) { + aborted = m_task->abort(); + } return aborted ? Task::abort() : false; } void Flame::FileResolvingTask::executeTask() { - if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately + if (m_manifest.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately emitSucceeded(); return; } setStatus(tr("Resolving mod IDs...")); setProgress(0, 3); - m_dljob.reset(new NetJob("Mod id resolver", m_network)); - result.reset(new QByteArray()); - // build json data to send - QJsonObject object; - - object["fileIds"] = QJsonArray::fromVariantList( - std::accumulate(m_toProcess.files.begin(), m_toProcess.files.end(), QVariantList(), [](QVariantList& l, const File& s) { - l.push_back(s.fileId); - return l; - })); - QByteArray data = Json::toText(object); - auto dl = Net::ApiUpload::makeByteArray(QUrl("https://api.curseforge.com/v1/mods/files"), result, data); - m_dljob->addNetAction(dl); + m_result.reset(new QByteArray()); + + QStringList fileIds; + for (auto file : m_manifest.files) { + fileIds.push_back(QString::number(file.fileId)); + } + m_task = flameAPI.getFiles(fileIds, m_result); auto step_progress = std::make_shared(); - connect(m_dljob.get(), &NetJob::finished, this, [this, step_progress]() { + connect(m_task.get(), &Task::finished, this, [this, step_progress]() { step_progress->state = TaskStepState::Succeeded; stepProgress(*step_progress); netJobFinished(); }); - connect(m_dljob.get(), &NetJob::failed, this, [this, step_progress](QString reason) { + connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) { step_progress->state = TaskStepState::Failed; stepProgress(*step_progress); emitFailed(reason); }); - connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); - connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { + connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress); + connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); stepProgress(*step_progress); }); - connect(m_dljob.get(), &NetJob::status, this, [this, step_progress](QString status) { + connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) { step_progress->status = status; stepProgress(*step_progress); }); - m_dljob->start(); + m_task->start(); } void Flame::FileResolvingTask::netJobFinished() { setProgress(1, 3); // job to check modrinth for blocked projects - m_checkJob.reset(new NetJob("Modrinth check", m_network)); - blockedProjects = QMap>(); + auto checkJob = makeShared("Modrinth check", m_network); QJsonDocument doc; QJsonArray array; try { - doc = Json::requireDocument(*result); + doc = Json::requireDocument(*m_result); array = Json::requireArray(doc.object()["data"]); } catch (Json::JsonException& e) { qCritical() << "Non-JSON data returned from the CF API"; @@ -91,125 +108,156 @@ void Flame::FileResolvingTask::netJobFinished() return; } + QStringList hashes; for (QJsonValueRef file : array) { - auto fileid = Json::requireInteger(Json::requireObject(file)["id"]); - auto& out = m_toProcess.files[fileid]; try { - out.parseFromObject(Json::requireObject(file)); - } catch ([[maybe_unused]] const JSONValidationError& e) { - qDebug() << "Blocked mod on curseforge" << out.fileName; - auto hash = out.hash; - if (!hash.isEmpty()) { - auto url = QString("https://api.modrinth.com/v2/version_file/%1?algorithm=sha1").arg(hash); - auto output = std::make_shared(); - auto dl = Net::ApiDownload::makeByteArray(QUrl(url), output); - QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [&out]() { out.resolved = true; }); - - m_checkJob->addNetAction(dl); - blockedProjects.insert(&out, output); + auto obj = Json::requireObject(file); + auto version = FlameMod::loadIndexedPackVersion(obj); + auto fileid = version.fileId.toInt(); + m_manifest.files[fileid].version = version; + auto url = QUrl(version.downloadUrl, QUrl::TolerantMode); + if (!url.isValid() && "sha1" == version.hash_type && !version.hash.isEmpty()) { + hashes.push_back(version.hash); } + } catch (Json::JsonException& e) { + qCritical() << "Non-JSON data returned from the CF API"; + qCritical() << e.cause(); + + emitFailed(tr("Invalid data returned from the API.")); + + return; } } + if (hashes.isEmpty()) { + getFlameProjects(); + return; + } + m_result.reset(new QByteArray()); + m_task = modrinthAPI.currentVersions(hashes, "sha1", m_result); auto step_progress = std::make_shared(); - connect(m_checkJob.get(), &NetJob::finished, this, [this, step_progress]() { + connect(m_task.get(), &Task::finished, this, [this, step_progress]() { step_progress->state = TaskStepState::Succeeded; stepProgress(*step_progress); - modrinthCheckFinished(); + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*m_result, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *m_result; + + failed(parse_error.errorString()); + return; + } + + try { + auto entries = Json::requireObject(doc); + for (auto& out : m_manifest.files) { + auto url = QUrl(out.version.downloadUrl, QUrl::TolerantMode); + if (!url.isValid() && "sha1" == out.version.hash_type && !out.version.hash.isEmpty()) { + try { + auto entry = Json::requireObject(entries, out.version.hash); + + auto file = Modrinth::loadIndexedPackVersion(entry); + + // If there's more than one mod loader for this version, we can't know for sure + // which file is relative to each loader, so it's best to not use any one and + // let the user download it manually. + if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) { + out.version.downloadUrl = file.downloadUrl; + qDebug() << "Found alternative on modrinth " << out.version.fileName; + } + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << entries; + } + } + } + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << doc; + } + getFlameProjects(); }); - connect(m_checkJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) { + connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) { step_progress->state = TaskStepState::Failed; stepProgress(*step_progress); }); - connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); - connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { + connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress); + connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); stepProgress(*step_progress); }); - connect(m_checkJob.get(), &NetJob::status, this, [this, step_progress](QString status) { + connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) { step_progress->status = status; stepProgress(*step_progress); }); - m_checkJob->start(); + m_task->start(); } -void Flame::FileResolvingTask::modrinthCheckFinished() +void Flame::FileResolvingTask::getFlameProjects() { setProgress(2, 3); - qDebug() << "Finished with blocked mods : " << blockedProjects.size(); + m_result.reset(new QByteArray()); + QStringList addonIds; + for (auto file : m_manifest.files) { + addonIds.push_back(QString::number(file.projectId)); + } - for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) { - auto& out = *it; - auto bytes = blockedProjects[out]; - if (!out->resolved) { - continue; - } + m_task = flameAPI.getProjects(addonIds, m_result); - QJsonDocument doc = QJsonDocument::fromJson(*bytes); - auto obj = doc.object(); - auto file = Modrinth::loadIndexedPackVersion(obj); - - // If there's more than one mod loader for this version, we can't know for sure - // which file is relative to each loader, so it's best to not use any one and - // let the user download it manually. - if (!file.loaders || hasSingleModLoaderSelected(file.loaders)) { - out->url = file.downloadUrl; - qDebug() << "Found alternative on modrinth " << out->fileName; - } else { - out->resolved = false; + auto step_progress = std::make_shared(); + connect(m_task.get(), &Task::succeeded, this, [this, step_progress] { + QJsonParseError parse_error{}; + auto doc = QJsonDocument::fromJson(*m_result, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Modrinth projects task at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *m_result; + return; } - } - // copy to an output list and filter out projects found on modrinth - auto block = std::make_shared>(); - auto it = blockedProjects.keys(); - std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; }); - // Display not found mods early - if (!block->empty()) { - // blocked mods found, we need the slug for displaying.... we need another job :D ! - m_slugJob.reset(new NetJob("Slug Job", m_network)); - int index = 0; - for (auto mod : *block) { - auto projectId = mod->projectId; - auto output = std::make_shared(); - auto url = QString("https://api.curseforge.com/v1/mods/%1").arg(projectId); - auto dl = Net::ApiDownload::makeByteArray(url, output); - qDebug() << "Fetching url slug for file:" << mod->fileName; - QObject::connect(dl.get(), &Net::ApiDownload::succeeded, [block, index, output]() { - auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done - auto json = QJsonDocument::fromJson(*output); - auto base = - Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl"); - auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId)); - mod->websiteUrl = link; - }); - m_slugJob->addNetAction(dl); - index++; + + try { + QJsonArray entries; + entries = Json::requireArray(Json::requireObject(doc), "data"); + + for (auto entry : entries) { + auto entry_obj = Json::requireObject(entry); + auto id = Json::requireInteger(entry_obj, "id"); + auto file = std::find_if(m_manifest.files.begin(), m_manifest.files.end(), + [id](const Flame::File& file) { return file.projectId == id; }); + if (file == m_manifest.files.end()) { + continue; + } + + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(file->version.fileName)); + FlameMod::loadIndexedPack(file->pack, entry_obj); + } + } catch (Json::JsonException& e) { + qDebug() << e.cause(); + qDebug() << doc; } - auto step_progress = std::make_shared(); - connect(m_slugJob.get(), &NetJob::succeeded, this, [this, step_progress]() { - step_progress->state = TaskStepState::Succeeded; - stepProgress(*step_progress); - emitSucceeded(); - }); - connect(m_slugJob.get(), &NetJob::failed, this, [this, step_progress](QString reason) { - step_progress->state = TaskStepState::Failed; - stepProgress(*step_progress); - emitFailed(reason); - }); - connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress); - connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) { - qDebug() << "Resolve slug progress" << current << total; - step_progress->update(current, total); - stepProgress(*step_progress); - }); - connect(m_slugJob.get(), &NetJob::status, this, [this, step_progress](QString status) { - step_progress->status = status; - stepProgress(*step_progress); - }); - - m_slugJob->start(); - } else { + step_progress->state = TaskStepState::Succeeded; + stepProgress(*step_progress); emitSucceeded(); - } + }); + + connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) { + step_progress->state = TaskStepState::Failed; + stepProgress(*step_progress); + emitFailed(reason); + }); + connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress); + connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) { + qDebug() << "Resolve slug progress" << current << total; + step_progress->update(current, total); + stepProgress(*step_progress); + }); + connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) { + step_progress->status = status; + stepProgress(*step_progress); + }); + + m_task->start(); } diff --git a/launcher/modplatform/flame/FileResolvingTask.h b/launcher/modplatform/flame/FileResolvingTask.h index c280827afe..edd9fce9a5 100644 --- a/launcher/modplatform/flame/FileResolvingTask.h +++ b/launcher/modplatform/flame/FileResolvingTask.h @@ -1,7 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2024 Trial97 + * + * 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, see . + */ #pragma once +#include + #include "PackManifest.h" -#include "net/NetJob.h" #include "tasks/Task.h" namespace Flame { @@ -9,12 +27,12 @@ class FileResolvingTask : public Task { Q_OBJECT public: explicit FileResolvingTask(const shared_qobject_ptr& network, Flame::Manifest& toProcess); - virtual ~FileResolvingTask(){}; + virtual ~FileResolvingTask() = default; bool canAbort() const override { return true; } bool abort() override; - const Flame::Manifest& getResults() const { return m_toProcess; } + const Flame::Manifest& getResults() const { return m_manifest; } protected: virtual void executeTask() override; @@ -22,16 +40,13 @@ class FileResolvingTask : public Task { protected slots: void netJobFinished(); + private: + void getFlameProjects(); + private: /* data */ shared_qobject_ptr m_network; - Flame::Manifest m_toProcess; - std::shared_ptr result; - NetJob::Ptr m_dljob; - NetJob::Ptr m_checkJob; - NetJob::Ptr m_slugJob; - - void modrinthCheckFinished(); - - QMap> blockedProjects; + Flame::Manifest m_manifest; + std::shared_ptr m_result; + Task::Ptr m_task; }; } // namespace Flame diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index a629cc15b6..b8c40ee429 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -35,8 +35,11 @@ #include "FlameInstanceCreationTask.h" +#include "QObjectPtr.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "modplatform/flame/FileResolvingTask.h" #include "modplatform/flame/FlameAPI.h" +#include "modplatform/flame/FlameModIndex.h" #include "modplatform/flame/PackManifest.h" #include "Application.h" @@ -51,6 +54,7 @@ #include "settings/INISettingsObject.h" +#include "tasks/ConcurrentTask.h" #include "ui/dialogs/BlockedModsDialog.h" #include "ui/dialogs/CustomMessageBox.h" @@ -58,7 +62,6 @@ #include #include "meta/Index.h" -#include "meta/VersionList.h" #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" #include "net/ApiDownload.h" @@ -208,8 +211,7 @@ bool FlameCreationTask::updateInstance() Flame::File file; // We don't care about blocked mods, we just need local data to delete the file - file.parseFromObject(entry_obj, false); - + file.version = FlameMod::loadIndexedPackVersion(entry_obj); auto id = Json::requireInteger(entry_obj, "id"); old_files.insert(id, file); } @@ -219,10 +221,10 @@ bool FlameCreationTask::updateInstance() // Delete the files for (auto& file : old_files) { - if (file.fileName.isEmpty() || file.targetFolder.isEmpty()) + if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty()) continue; - QString relative_path(FS::PathCombine(file.targetFolder, file.fileName)); + QString relative_path(FS::PathCombine(file.targetFolder, file.version.fileName)); qDebug() << "Scheduling" << relative_path << "for removal"; m_files_to_remove.append(old_minecraft_dir.absoluteFilePath(relative_path)); } @@ -471,15 +473,15 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) QList blocked_mods; auto anyBlocked = false; for (const auto& result : results.files.values()) { - if (result.fileName.endsWith(".zip")) { - m_ZIP_resources.append(std::make_pair(result.fileName, result.targetFolder)); + if (result.version.fileName.endsWith(".zip")) { + m_ZIP_resources.append(std::make_pair(result.version.fileName, result.targetFolder)); } - if (!result.resolved || result.url.isEmpty()) { + if (result.version.downloadUrl.isEmpty()) { BlockedMod blocked_mod; - blocked_mod.name = result.fileName; - blocked_mod.websiteUrl = result.websiteUrl; - blocked_mod.hash = result.hash; + blocked_mod.name = result.version.fileName; + blocked_mod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId)); + blocked_mod.hash = result.version.hash; blocked_mod.matched = false; blocked_mod.localPath = ""; blocked_mod.targetFolder = result.targetFolder; @@ -521,7 +523,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) QStringList optionalFiles; for (auto& result : results) { if (!result.required) { - optionalFiles << FS::PathCombine(result.targetFolder, result.fileName); + optionalFiles << FS::PathCombine(result.targetFolder, result.version.fileName); } } @@ -537,7 +539,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) selectedOptionalMods = optionalModDialog.getResult(); } for (const auto& result : results) { - auto fileName = result.fileName; + auto fileName = result.version.fileName; fileName = FS::RemoveInvalidPathChars(fileName); auto relpath = FS::PathCombine(result.targetFolder, fileName); @@ -548,36 +550,16 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) relpath = FS::PathCombine("minecraft", relpath); auto path = FS::PathCombine(m_stagingPath, relpath); - switch (result.type) { - case Flame::File::Type::Folder: { - logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath)); - // fallthrough intentional, we treat these as plain old mods and dump them wherever. - } - /* fallthrough */ - case Flame::File::Type::SingleFile: - case Flame::File::Type::Mod: { - if (!result.url.isEmpty()) { - qDebug() << "Will download" << result.url << "to" << path; - auto dl = Net::ApiDownload::makeFile(result.url, path); - m_files_job->addNetAction(dl); - } - break; - } - case Flame::File::Type::Modpack: - logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath)); - break; - case Flame::File::Type::Cmod2: - case Flame::File::Type::Ctoc: - case Flame::File::Type::Unknown: - logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath)); - break; + if (!result.version.downloadUrl.isEmpty()) { + qDebug() << "Will download" << result.version.downloadUrl << "to" << path; + auto dl = Net::ApiDownload::makeFile(result.version.downloadUrl, path); + m_files_job->addNetAction(dl); } } - m_mod_id_resolver.reset(); - connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { + connect(m_files_job.get(), &NetJob::finished, this, [this, &loop]() { m_files_job.reset(); - validateZIPResources(); + validateZIPResources(loop); }); connect(m_files_job.get(), &NetJob::failed, [&](QString reason) { m_files_job.reset(); @@ -588,7 +570,6 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) setProgress(current, total); }); connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress); - connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); setStatus(tr("Downloading mods...")); m_files_job->start(); @@ -626,9 +607,10 @@ void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) setAbortable(true); } -void FlameCreationTask::validateZIPResources() +void FlameCreationTask::validateZIPResources(QEventLoop& loop) { qDebug() << "Validating whether resources stored as .zip are in the right place"; + QStringList zipMods; for (auto [fileName, targetFolder] : m_ZIP_resources) { qDebug() << "Checking" << fileName << "..."; auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName); @@ -668,6 +650,7 @@ void FlameCreationTask::validateZIPResources() switch (type) { case PackedResourceType::Mod: validatePath(fileName, targetFolder, "mods"); + zipMods.push_back(fileName); break; case PackedResourceType::ResourcePack: validatePath(fileName, targetFolder, "resourcepacks"); @@ -693,4 +676,16 @@ void FlameCreationTask::validateZIPResources() break; } } + auto task = makeShared(this, "CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); + auto results = m_mod_id_resolver->getResults().files; + auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index"); + for (auto file : results) { + if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) { + continue; + } + task->addTask(makeShared(folder, file.pack, file.version)); + } + connect(task.get(), &Task::finished, &loop, &QEventLoop::quit); + m_process_update_file_info_job = task; + task->start(); } diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 02ad48f2e0..28ab176c2f 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -74,7 +74,7 @@ class FlameCreationTask final : public InstanceCreationTask { void idResolverSucceeded(QEventLoop&); void setupDownloadJob(QEventLoop&); void copyBlockedMods(QList const& blocked_mods); - void validateZIPResources(); + void validateZIPResources(QEventLoop& loop); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); private: diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp index 40a523d314..e576a6a84b 100644 --- a/launcher/modplatform/flame/PackManifest.cpp +++ b/launcher/modplatform/flame/PackManifest.cpp @@ -68,35 +68,3 @@ void Flame::loadManifest(Flame::Manifest& m, const QString& filepath) } loadManifestV1(m, obj); } - -bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked) -{ - fileName = Json::requireString(obj, "fileName"); - // This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience - // It is also optional - type = File::Type::SingleFile; - - targetFolder = "mods"; - - // get the hash - hash = QString(); - auto hashes = Json::ensureArray(obj, "hashes"); - for (QJsonValueRef item : hashes) { - auto hobj = Json::requireObject(item); - auto algo = Json::requireInteger(hobj, "algo"); - auto value = Json::requireString(hobj, "value"); - if (algo == 1) { - hash = value; - } - } - - // may throw, if the project is blocked - QString rawUrl = Json::ensureString(obj, "downloadUrl"); - url = QUrl(rawUrl, QUrl::TolerantMode); - if (!url.isValid() && throw_on_blocked) { - throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl)); - } - - resolved = true; - return true; -} diff --git a/launcher/modplatform/flame/PackManifest.h b/launcher/modplatform/flame/PackManifest.h index 4417c24309..49a0b2d684 100644 --- a/launcher/modplatform/flame/PackManifest.h +++ b/launcher/modplatform/flame/PackManifest.h @@ -40,26 +40,20 @@ #include #include #include +#include "modplatform/ModIndex.h" namespace Flame { struct File { - // NOTE: throws JSONValidationError - bool parseFromObject(const QJsonObject& object, bool throw_on_blocked = true); - int projectId = 0; int fileId = 0; // NOTE: the opposite to 'optional' bool required = true; - QString hash; - // NOTE: only set on blocked files ! Empty otherwise. - QString websiteUrl; + + ModPlatform::IndexedPack pack; + ModPlatform::IndexedVersion version; // our - bool resolved = false; - QString fileName; - QUrl url; QString targetFolder = QStringLiteral("mods"); - enum class Type { Unknown, Folder, Ctoc, SingleFile, Cmod2, Modpack, Mod } type = Type::Mod; }; struct Modloader { From 1021222c955b4b5f7416e91f51c619af118a2bf4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 28 Jun 2024 20:28:52 +0300 Subject: [PATCH 02/32] create mod meta information when importing modrinth pack Signed-off-by: Trial97 --- launcher/modplatform/EnsureMetadataTask.cpp | 3 + launcher/modplatform/EnsureMetadataTask.h | 1 + .../modrinth/ModrinthInstanceCreationTask.cpp | 67 ++++++++++++++----- .../modrinth/ModrinthInstanceCreationTask.h | 9 +-- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 43acea1a29..f6f49f38d2 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -42,6 +42,9 @@ EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform: m_hashing_task->addTask(hash_task); } } +EnsureMetadataTask::EnsureMetadataTask(QHash& mods, QDir dir, ModPlatform::ResourceProvider prov) + : Task(nullptr), m_mods(mods), m_index_dir(dir), m_provider(prov), m_current_task(nullptr) +{} Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Mod* mod) { diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index a32a9fce15..631b32ae73 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -16,6 +16,7 @@ class EnsureMetadataTask : public Task { public: EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); EnsureMetadataTask(QList&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); + EnsureMetadataTask(QHash&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); ~EnsureMetadataTask() = default; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index c0806a638f..acbc7376c4 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -5,8 +5,12 @@ #include "InstanceList.h" #include "Json.h" +#include "QObjectPtr.h" +#include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "minecraft/mod/Mod.h" +#include "modplatform/EnsureMetadataTask.h" #include "modplatform/helpers/OverrideUtils.h" #include "modplatform/modrinth/ModrinthPackManifest.h" @@ -21,6 +25,7 @@ #include #include +#include #include bool ModrinthCreationTask::abort() @@ -29,8 +34,8 @@ bool ModrinthCreationTask::abort() return false; m_abort = true; - if (m_files_job) - m_files_job->abort(); + if (m_task) + m_task->abort(); return Task::abort(); } @@ -234,11 +239,11 @@ bool ModrinthCreationTask::createInstance() instance.setName(name()); instance.saveNow(); - m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network())); + auto downloadMods = makeShared(tr("Mod Download Modrinth"), APPLICATION->network()); auto root_modpack_path = FS::PathCombine(m_stagingPath, m_root_path); auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path); - + QHash mods; for (auto file : m_files) { auto fileName = file.path; fileName = FS::RemoveInvalidPathChars(fileName); @@ -249,20 +254,27 @@ bool ModrinthCreationTask::createInstance() .arg(fileName)); return false; } + if (fileName.startsWith("mods/")) { + auto mod = new Mod(file_path); + ModDetails d; + d.mod_id = file_path; + mod->setDetails(d); + mods[file.hash.toHex()] = mod; + } qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path; auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); - m_files_job->addNetAction(dl); + downloadMods->addNetAction(dl); if (!file.downloads.empty()) { // FIXME: This really needs to be put into a ConcurrentTask of // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); - connect(dl.get(), &Task::failed, [this, &file, file_path, param] { + connect(dl.get(), &Task::failed, [&file, file_path, param, downloadMods] { auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); - m_files_job->addNetAction(ndl); + downloadMods->addNetAction(ndl); if (auto shared = param.lock()) shared->succeeded(); }); @@ -271,23 +283,44 @@ bool ModrinthCreationTask::createInstance() bool ended_well = false; - connect(m_files_job.get(), &NetJob::succeeded, this, [&]() { ended_well = true; }); - connect(m_files_job.get(), &NetJob::failed, [&](const QString& reason) { + connect(downloadMods.get(), &NetJob::succeeded, this, [&]() { ended_well = true; }); + connect(downloadMods.get(), &NetJob::failed, [&](const QString& reason) { ended_well = false; setError(reason); }); - connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); - connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { + connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit); + connect(downloadMods.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); - connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); + connect(downloadMods.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); setStatus(tr("Downloading mods...")); - m_files_job->start(); + downloadMods->start(); + m_task = downloadMods; loop.exec(); + QEventLoop ensureMetaLoop; + QDir folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index"); + auto ensureMetadataTask = makeShared(mods, folder, ModPlatform::ResourceProvider::MODRINTH); + connect(ensureMetadataTask.get(), &Task::succeeded, this, [&]() { ended_well = true; }); + connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit); + connect(ensureMetadataTask.get(), &Task::progress, [&](qint64 current, qint64 total) { + setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); + setProgress(current, total); + }); + connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); + + ensureMetadataTask->start(); + m_task = ensureMetadataTask; + + ensureMetaLoop.exec(); + for (auto m : mods) { + delete m; + } + mods.clear(); + // Update information of the already installed instance, if any. if (m_instance && ended_well) { setAbortable(false); @@ -348,11 +381,11 @@ bool ModrinthCreationTask::parseManifest(const QString& index_path, QJsonObject hashes = Json::requireObject(modInfo, "hashes"); QString hash; QCryptographicHash::Algorithm hashAlgorithm; - hash = Json::ensureString(hashes, "sha1"); - hashAlgorithm = QCryptographicHash::Sha1; + hash = Json::ensureString(hashes, "sha512"); + hashAlgorithm = QCryptographicHash::Sha512; if (hash.isEmpty()) { - hash = Json::ensureString(hashes, "sha512"); - hashAlgorithm = QCryptographicHash::Sha512; + hash = Json::ensureString(hashes, "sha1"); + hashAlgorithm = QCryptographicHash::Sha1; if (hash.isEmpty()) { hash = Json::ensureString(hashes, "sha256"); hashAlgorithm = QCryptographicHash::Sha256; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h index f07734a586..0879474cf3 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h @@ -1,15 +1,10 @@ #pragma once +#include "BaseInstance.h" #include "InstanceCreationTask.h" -#include - -#include "minecraft/MinecraftInstance.h" - #include "modplatform/modrinth/ModrinthPackManifest.h" -#include "net/NetJob.h" - class ModrinthCreationTask final : public InstanceCreationTask { Q_OBJECT @@ -43,7 +38,7 @@ class ModrinthCreationTask final : public InstanceCreationTask { QString m_managed_id, m_managed_version_id, m_managed_name; std::vector m_files; - NetJob::Ptr m_files_job; + Task::Ptr m_task; std::optional m_instance; From fe23e76714598f134f07ba04b482bdb2bf1ee34c Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 5 Aug 2024 12:04:08 +0300 Subject: [PATCH 03/32] fix codeql Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h index 0879474cf3..ddfa7ae951 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h @@ -1,5 +1,6 @@ #pragma once +#include #include "BaseInstance.h" #include "InstanceCreationTask.h" From 973b43ca7a12cdcb6891d069b3bb6782eda6f28a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 13 Sep 2024 20:01:45 +0300 Subject: [PATCH 04/32] Add techinc client ID Signed-off-by: Trial97 --- launcher/Application.cpp | 3 ++ launcher/ui/pages/global/APIPage.cpp | 2 ++ launcher/ui/pages/global/APIPage.ui | 34 +++++++++++++++++-- .../modplatform/technic/TechnicModel.cpp | 4 +++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 9cd0445e62..ba9a9caa9c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -777,6 +777,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // FTBApp instances m_settings->registerSetting("FTBAppInstancesPath", ""); + // Custom Techinc Client ID + m_settings->registerSetting("TechincClientID", ""); + // Init page provider { m_globalSettingsProvider = std::make_shared(tr("Settings")); diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 82aa76a4ff..4beb0b8866 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -143,6 +143,7 @@ void APIPage::loadSettings() ui->modrinthToken->setText(modrinthToken); QString customUserAgent = s->get("UserAgentOverride").toString(); ui->userAgentLineEdit->setText(customUserAgent); + ui->techicClientID->setText(s->get("TechincClientID").toString()); } void APIPage::applySettings() @@ -172,6 +173,7 @@ void APIPage::applySettings() QString modrinthToken = ui->modrinthToken->text(); s->set("ModrinthToken", modrinthToken); s->set("UserAgentOverride", ui->userAgentLineEdit->text()); + s->set("TechincClientID", ui->techicClientID->text()); } bool APIPage::apply() diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index a7f3f3f720..f52d41d236 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -6,8 +6,8 @@ 0 0 - 800 - 600 + 841 + 620 @@ -288,6 +288,36 @@ + + + + Techinc Client ID + + + + + + <html><head/><body><p>Note: you only need to set this to access private data.</p></body></html> + + + + + + + (None) + + + + + + + Enter a custom GUID client ID for Techinc here. + + + + + + diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 4181edab60..55a8b254c7 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -154,6 +154,10 @@ void Technic::ListModel::performSearch() QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); searchMode = List; } + auto clientId = APPLICATION->settings()->get("TechincClientID").toString(); + if (!clientId.isEmpty()) { + searchUrl += "?cid=" + clientId; + } netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(searchUrl), response)); jobPtr = netJob; jobPtr->start(); From d20f24d96fa35311935b58aa6cbae5623f437c6a Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Fri, 13 Sep 2024 21:13:21 +0300 Subject: [PATCH 05/32] Apply suggestions from code review Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- launcher/Application.cpp | 4 ++-- launcher/ui/pages/global/APIPage.cpp | 4 ++-- launcher/ui/pages/global/APIPage.ui | 6 +++--- launcher/ui/pages/modplatform/technic/TechnicModel.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ba9a9caa9c..815f842c59 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -777,8 +777,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // FTBApp instances m_settings->registerSetting("FTBAppInstancesPath", ""); - // Custom Techinc Client ID - m_settings->registerSetting("TechincClientID", ""); + // Custom Technic Client ID + m_settings->registerSetting("TechnicClientID", ""); // Init page provider { diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 4beb0b8866..0af34bcd66 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -143,7 +143,7 @@ void APIPage::loadSettings() ui->modrinthToken->setText(modrinthToken); QString customUserAgent = s->get("UserAgentOverride").toString(); ui->userAgentLineEdit->setText(customUserAgent); - ui->techicClientID->setText(s->get("TechincClientID").toString()); + ui->techicClientID->setText(s->get("TechnicClientID").toString()); } void APIPage::applySettings() @@ -173,7 +173,7 @@ void APIPage::applySettings() QString modrinthToken = ui->modrinthToken->text(); s->set("ModrinthToken", modrinthToken); s->set("UserAgentOverride", ui->userAgentLineEdit->text()); - s->set("TechincClientID", ui->techicClientID->text()); + s->set("TechincClientID", ui->technicClientID->text()); } bool APIPage::apply() diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index f52d41d236..9c713aa791 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -291,7 +291,7 @@ - Techinc Client ID + Technic Client ID @@ -302,7 +302,7 @@ - + (None) @@ -311,7 +311,7 @@ - Enter a custom GUID client ID for Techinc here. + Enter a custom GUID client ID for Technic here. diff --git a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp index 55a8b254c7..f7e7f44337 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicModel.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicModel.cpp @@ -154,7 +154,7 @@ void Technic::ListModel::performSearch() QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm); searchMode = List; } - auto clientId = APPLICATION->settings()->get("TechincClientID").toString(); + auto clientId = APPLICATION->settings()->get("TechnicClientID").toString(); if (!clientId.isEmpty()) { searchUrl += "?cid=" + clientId; } From 1db122f1a29b90f2b556aeaddb7336233c0c4769 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 13 Sep 2024 21:37:50 +0300 Subject: [PATCH 06/32] fix typo Signed-off-by: Trial97 --- launcher/ui/pages/global/APIPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 0af34bcd66..5ec6fbce50 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -143,7 +143,7 @@ void APIPage::loadSettings() ui->modrinthToken->setText(modrinthToken); QString customUserAgent = s->get("UserAgentOverride").toString(); ui->userAgentLineEdit->setText(customUserAgent); - ui->techicClientID->setText(s->get("TechnicClientID").toString()); + ui->technicClientID->setText(s->get("TechnicClientID").toString()); } void APIPage::applySettings() From e59f90eca8db7f60328ee2ca93c6b20053572ea6 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sat, 14 Sep 2024 00:19:48 +0300 Subject: [PATCH 07/32] Update launcher/ui/pages/global/APIPage.cpp Co-authored-by: TheKodeToad Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/global/APIPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index 5ec6fbce50..a137c4cde1 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -173,7 +173,7 @@ void APIPage::applySettings() QString modrinthToken = ui->modrinthToken->text(); s->set("ModrinthToken", modrinthToken); s->set("UserAgentOverride", ui->userAgentLineEdit->text()); - s->set("TechincClientID", ui->technicClientID->text()); + s->set("TechnicClientID", ui->technicClientID->text()); } bool APIPage::apply() From 312009513d0b3b3a8e08d386bd249ea85aa8bdef Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 14 Sep 2024 18:27:12 +0300 Subject: [PATCH 08/32] add open java folder action Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/LaunchController.cpp | 1 + launcher/ui/MainWindow.cpp | 7 +++++++ launcher/ui/MainWindow.h | 2 +- launcher/ui/MainWindow.ui | 15 ++++++++++++++- launcher/ui/themes/ThemeManager.h | 4 +++- 6 files changed, 27 insertions(+), 3 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 35eb6e91e6..3bed11db2e 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1870,6 +1870,7 @@ QUrl Application::normalizeImportUrl(QString const& url) return QUrl::fromUserInput(url); } } + const QString Application::javaPath() { return m_settings->get("JavaDir").toString(); diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 73800574f1..687da13220 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include "BuildConfig.h" diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index a5ccbc19a2..f19ac85d22 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -233,6 +233,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") { ui->mainToolBar->addAction(ui->actionCloseWindow); } + + ui->actionViewJavaFolder->setVisible(BuildConfig.JAVA_DOWNLOADER_ENABLED); } // add the toolbar toggles to the view menu @@ -1223,6 +1225,11 @@ void MainWindow::on_actionViewLogsFolder_triggered() DesktopServices::openPath("logs", true); } +void MainWindow::on_actionViewJavaFolder_triggered() +{ + DesktopServices::openPath(APPLICATION->javaPath(), true); +} + void MainWindow::refreshInstances() { APPLICATION->instances()->loadList(); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 41bef99805..0e692eda7d 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -48,7 +48,6 @@ #include "BaseInstance.h" #include "minecraft/auth/MinecraftAccount.h" -#include "net/NetJob.h" class LaunchController; class NewsChecker; @@ -119,6 +118,7 @@ class MainWindow : public QMainWindow { void on_actionViewCatPackFolder_triggered(); void on_actionViewIconsFolder_triggered(); void on_actionViewLogsFolder_triggered(); + void on_actionViewJavaFolder_triggered(); void on_actionViewSkinsFolder_triggered(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index bad8762ad6..819a1efe70 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -131,7 +131,7 @@ 0 0 800 - 22 + 27 @@ -192,6 +192,7 @@ + @@ -788,6 +789,18 @@ Open the cat packs folder in a file browser. + + + + .. + + + Java + + + Open the java folder in a file browser. + + diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index c7e32bc8c9..a9036107c8 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -18,10 +18,12 @@ */ #pragma once +#include +#include #include +#include #include "IconTheme.h" -#include "ui/MainWindow.h" #include "ui/themes/CatPack.h" #include "ui/themes/ITheme.h" From 0afdce8a31b94fca64fe064d83a07223b6a490a6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 14 Sep 2024 21:18:20 +0300 Subject: [PATCH 09/32] Add extra step to macos java install Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 2 + launcher/java/download/SymlinkTask.cpp | 81 +++++++++++++++++++ launcher/java/download/SymlinkTask.h | 36 +++++++++ launcher/minecraft/launch/AutoInstallJava.cpp | 9 +++ launcher/ui/java/InstallJavaDialog.cpp | 8 ++ 5 files changed, 136 insertions(+) create mode 100644 launcher/java/download/SymlinkTask.cpp create mode 100644 launcher/java/download/SymlinkTask.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 975946740f..bd6dfc5b63 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -439,6 +439,8 @@ set(JAVA_SOURCES java/download/ArchiveDownloadTask.h java/download/ManifestDownloadTask.cpp java/download/ManifestDownloadTask.h + java/download/SymlinkTask.cpp + java/download/SymlinkTask.h ui/java/InstallJavaDialog.h ui/java/InstallJavaDialog.cpp diff --git a/launcher/java/download/SymlinkTask.cpp b/launcher/java/download/SymlinkTask.cpp new file mode 100644 index 0000000000..9841c5015a --- /dev/null +++ b/launcher/java/download/SymlinkTask.cpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023-2024 Trial97 + * + * 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, see . + */ +#include "java/download/SymlinkTask.h" +#include + +#include "FileSystem.h" + +namespace Java { +SymlinkTask::SymlinkTask(QString final_path) : m_path(final_path) {} + +QString findBinPath(QString root, QString pattern) +{ + auto path = FS::PathCombine(root, pattern); + if (QFileInfo::exists(path)) { + return path; + } + + auto entries = QDir(root).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (auto& entry : entries) { + path = FS::PathCombine(entry.absoluteFilePath(), pattern); + if (QFileInfo::exists(path)) { + return path; + } + } + + return {}; +} + +void SymlinkTask::executeTask() +{ + setStatus(tr("Check Java bin")); + const auto binPath = FS::PathCombine("bin", "java"); + const auto wantedPath = FS::PathCombine(m_path, binPath); + if (QFileInfo::exists(wantedPath)) { + emitSucceeded(); + return; + } + + setStatus(tr("Search for Java bin")); + const auto contentsPartialPath = FS::PathCombine("Contents", "Home", binPath); + const auto relativePathToBin = findBinPath(m_path, contentsPartialPath); + if (relativePathToBin.isEmpty()) { + emitFailed(tr("Failed to find bin java")); + return; + } + const auto folderToLink = relativePathToBin.chopped(binPath.length()); + + setStatus(tr("Collect folders to symlink")); + auto entries = QDir(folderToLink).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries); + QList files; + setProgress(0, entries.length()); + for (auto& entry : entries) { + files.append({ entry.absoluteFilePath(), FS::PathCombine(m_path, entry.fileName()) }); + } + + setStatus(tr("Symlink Java")); + FS::create_link folderLink(files); + connect(&folderLink, &FS::create_link::fileLinked, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); + if (!folderLink()) { + emitFailed(folderLink.getOSError().message().c_str()); + } else { + emitSucceeded(); + } +} + +} // namespace Java \ No newline at end of file diff --git a/launcher/java/download/SymlinkTask.h b/launcher/java/download/SymlinkTask.h new file mode 100644 index 0000000000..88cb20dd7e --- /dev/null +++ b/launcher/java/download/SymlinkTask.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023-2024 Trial97 + * + * 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, see . + */ + +#pragma once + +#include "tasks/Task.h" +namespace Java { + +class SymlinkTask : public Task { + Q_OBJECT + public: + SymlinkTask(QString final_path); + virtual ~SymlinkTask() = default; + + void executeTask() override; + + protected: + QString m_path; + Task::Ptr m_task; +}; +} // namespace Java \ No newline at end of file diff --git a/launcher/minecraft/launch/AutoInstallJava.cpp b/launcher/minecraft/launch/AutoInstallJava.cpp index 5daf89382f..4fad6f15ff 100644 --- a/launcher/minecraft/launch/AutoInstallJava.cpp +++ b/launcher/minecraft/launch/AutoInstallJava.cpp @@ -41,6 +41,7 @@ #include "Application.h" #include "FileSystem.h" #include "MessageLevel.h" +#include "QObjectPtr.h" #include "SysInfo.h" #include "java/JavaInstall.h" #include "java/JavaInstallList.h" @@ -48,10 +49,12 @@ #include "java/JavaVersion.h" #include "java/download/ArchiveDownloadTask.h" #include "java/download/ManifestDownloadTask.h" +#include "java/download/SymlinkTask.h" #include "meta/Index.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "net/Mode.h" +#include "tasks/SequentialTask.h" AutoInstallJava::AutoInstallJava(LaunchTask* parent) : LaunchStep(parent) @@ -175,6 +178,12 @@ void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName) emitFailed(tr("Could not determine Java download type!")); return; } +#if defined(Q_OS_MACOS) + auto seq = makeShared(this, tr("Install Java")); + seq->addTask(m_current_task); + seq->addTask(makeShared(final_path)); + m_current_task = seq; +#endif auto deletePath = [final_path] { FS::deletePath(final_path); }; connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) { deletePath(); diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp index 4fb9fc2d29..f01edc5e56 100644 --- a/launcher/ui/java/InstallJavaDialog.cpp +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -31,10 +31,12 @@ #include "Filter.h" #include "java/download/ArchiveDownloadTask.h" #include "java/download/ManifestDownloadTask.h" +#include "java/download/SymlinkTask.h" #include "meta/Index.h" #include "meta/VersionList.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" +#include "tasks/SequentialTask.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/java/VersionList.h" @@ -313,6 +315,12 @@ void InstallDialog::done(int result) CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); deletePath(); } +#if defined(Q_OS_MACOS) + auto seq = makeShared(this, tr("Install Java")); + seq->addTask(task); + seq->addTask(makeShared(final_path)); + task = seq; +#endif connect(task.get(), &Task::failed, this, [this, &deletePath](QString reason) { QString error = QString("Java download failed: %1").arg(reason); CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); From 1c6bd74ed8d4818231dd153993ed1643dc4e4543 Mon Sep 17 00:00:00 2001 From: QazCetelic Date: Sat, 14 Sep 2024 22:04:18 +0200 Subject: [PATCH 10/32] Disable folder action instead of hiding it and add additional information in the tooltip. Signed-off-by: QazCetelic --- launcher/ui/MainWindow.cpp | 3 ++- launcher/ui/MainWindow.ui | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index f19ac85d22..3f0bb3281e 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -234,7 +234,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi ui->mainToolBar->addAction(ui->actionCloseWindow); } - ui->actionViewJavaFolder->setVisible(BuildConfig.JAVA_DOWNLOADER_ENABLED); + ui->actionViewJavaFolder->setEnabled(BuildConfig.JAVA_DOWNLOADER_ENABLED); + } // add the toolbar toggles to the view menu diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 819a1efe70..89e536b01f 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -798,7 +798,7 @@ Java - Open the java folder in a file browser. + Open the java folder in a file browser. Only available if the built-in Java downloader is used. From d38e7fa1424c406b86c5556e9c78c870eaa6b317 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 16 Sep 2024 12:41:33 +0300 Subject: [PATCH 11/32] Add more null protection for skin management Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 6 +++++- launcher/minecraft/skins/SkinModel.cpp | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index fd883ad524..017cb8dc2e 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -336,7 +336,11 @@ void SkinList::save() arr << s.toJSON(); } doc["skins"] = arr; - Json::write(doc, m_dir.absoluteFilePath("index.json")); + try { + Json::write(doc, m_dir.absoluteFilePath("index.json")); + } catch (const FS::FileSystemException& e) { + qCritical() << "Failed to write skin index file :" << e.cause(); + } } int SkinList::getSelectedAccountSkin() diff --git a/launcher/minecraft/skins/SkinModel.cpp b/launcher/minecraft/skins/SkinModel.cpp index d53b9e762d..937864e2cb 100644 --- a/launcher/minecraft/skins/SkinModel.cpp +++ b/launcher/minecraft/skins/SkinModel.cpp @@ -41,7 +41,7 @@ SkinModel::SkinModel(QDir skinDir, QJsonObject obj) QString SkinModel::name() const { - return QFileInfo(m_path).baseName(); + return QFileInfo(m_path).completeBaseName(); } bool SkinModel::rename(QString newName) diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index a947af632c..6c85ffa964 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -116,7 +116,7 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, QItemSelection return; m_selected_skin = key; auto skin = m_list.skin(key); - if (!skin) + if (!skin || !skin->isValid()) return; ui->selectedModel->setPixmap(skin->getTexture().scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation)); ui->capeCombo->setCurrentIndex(m_capes_idx.value(skin->getCapeId())); @@ -212,7 +212,10 @@ void SkinManageDialog::setupCapes() void SkinManageDialog::on_capeCombo_currentIndexChanged(int index) { auto id = ui->capeCombo->currentData(); - ui->capeImage->setPixmap(m_capes.value(id.toString(), {}).scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation)); + auto cape = m_capes.value(id.toString(), {}); + if (!cape.isNull()) { + ui->capeImage->setPixmap(cape.scaled(size() * (1. / 3), Qt::KeepAspectRatio, Qt::FastTransformation)); + } if (auto skin = m_list.skin(m_selected_skin); skin) { skin->setCapeId(id.toString()); } @@ -505,8 +508,13 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event) QSize s = size() * (1. / 3); if (auto skin = m_list.skin(m_selected_skin); skin) { - ui->selectedModel->setPixmap(skin->getTexture().scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation)); + if (skin->isValid()) { + ui->selectedModel->setPixmap(skin->getTexture().scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation)); + } } auto id = ui->capeCombo->currentData(); - ui->capeImage->setPixmap(m_capes.value(id.toString(), {}).scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation)); + auto cape = m_capes.value(id.toString(), {}); + if (!cape.isNull()) { + ui->capeImage->setPixmap(cape.scaled(s, Qt::KeepAspectRatio, Qt::FastTransformation)); + } } From d2f0d1d8d5529dc1bd462c4795c4746b13a3fb65 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 16 Sep 2024 12:42:09 +0300 Subject: [PATCH 12/32] Wrap FS::write in try catch Signed-off-by: Trial97 --- launcher/InstanceCopyTask.cpp | 6 +++++- launcher/ui/dialogs/ExportToModListDialog.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index ff2d377236..0220a41449 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -173,7 +173,11 @@ void InstanceCopyTask::copyFinished() allowed_symlinks_file .filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link. - FS::write(allowed_symlinks_file.filePath(), allowed_symlinks); + try { + FS::write(allowed_symlinks_file.filePath(), allowed_symlinks); + } catch (const FS::FileSystemException& e) { + qCritical() << "Failed to write symlink :" << e.cause(); + } } emitSucceeded(); diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index 1e0ae87a3e..908743ab01 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -164,7 +164,12 @@ void ExportToModListDialog::done(int result) if (output.isEmpty()) return; - FS::write(output, ui->finalText->toPlainText().toUtf8()); + + try { + FS::write(output, ui->finalText->toPlainText().toUtf8()); + } catch (const FS::FileSystemException& e) { + qCritical() << "Failed to save mod list file :" << e.cause(); + } } QDialog::done(result); From ca6b62c773554213e507e915034d3a68933d0a1e Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 16 Sep 2024 15:51:51 +0300 Subject: [PATCH 13/32] move some URLs in cmake Signed-off-by: Trial97 --- CMakeLists.txt | 3 +++ buildconfig/BuildConfig.cpp.in | 3 +++ buildconfig/BuildConfig.h | 9 +++++++-- launcher/minecraft/auth/steps/MSAStep.cpp | 9 +++++---- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7445e574ab..6b9cb8ea8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,8 @@ endif() set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.") set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'") set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help") +set(Launcher_SUCCESSFULL_LOGIN_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user succesfully logins.") +set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.") ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 9) @@ -205,6 +207,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss # Translations Platform URL set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.") +set(Launcher_TRANSLATIONS_BASE_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.") # Matrix Space set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index a2b5c21871..121ce07297 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -116,16 +116,19 @@ Config::Config() NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@"; NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@"; HELP_URL = "@Launcher_HELP_URL@"; + SUCCESSFULL_LOGIN_URL = "@Launcher_SUCCESSFULL_LOGIN_URL@"; IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@"; MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@"; FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@"; META_URL = "@Launcher_META_URL@"; + FMLLIBS_BASE_URL = "@Launcher_FMLLIBS_BASE_URL@"; GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@"; OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@"; BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@"; TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@"; + TRANSLATIONS_BASE_URL = "@Launcher_TRANSLATIONS_BASE_URL@"; MATRIX_URL = "@Launcher_MATRIX_URL@"; DISCORD_URL = "@Launcher_DISCORD_URL@"; SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@"; diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index bb633f297b..4684f5709a 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -133,6 +133,11 @@ class Config { */ QString HELP_URL; + /** + * URL that gets opened when the user succesfully logins. + */ + QString SUCCESSFULL_LOGIN_URL; + /** * Client ID you can get from Imgur when you register an application */ @@ -165,8 +170,8 @@ class Config { QString RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; - QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists - QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists + QString FMLLIBS_BASE_URL; + QString TRANSLATIONS_BASE_URL; QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 3db04bf2f3..916f84e4a0 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -90,15 +90,16 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile { auto replyHandler = new QOAuthHttpServerReplyHandler(this); - replyHandler->setCallbackText(R"XXX( + replyHandler->setCallbackText(QString(R"XXX( Login Successful, redirecting... - )XXX"); + )XXX") + .arg(BuildConfig.SUCCESSFULL_LOGIN_URL)); oauth2.setReplyHandler(replyHandler); } else { oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this)); From 75756b49c3f81bd4426728307dcf99b7a4ad31f1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 16 Sep 2024 16:15:36 +0300 Subject: [PATCH 14/32] reshow login dialog in case account login fails Signed-off-by: Trial97 --- launcher/ui/setupwizard/LoginWizardPage.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/launcher/ui/setupwizard/LoginWizardPage.cpp b/launcher/ui/setupwizard/LoginWizardPage.cpp index 6be24a2f7c..f53e319084 100644 --- a/launcher/ui/setupwizard/LoginWizardPage.cpp +++ b/launcher/ui/setupwizard/LoginWizardPage.cpp @@ -35,11 +35,10 @@ void LoginWizardPage::on_pushButton_clicked() if (account) { APPLICATION->accounts()->addAccount(account); APPLICATION->accounts()->setDefaultAccount(account); - } - - if (wizard()->currentId() == wizard()->pageIds().last()) { - wizard()->accept(); - } else { - wizard()->next(); + if (wizard()->currentId() == wizard()->pageIds().last()) { + wizard()->accept(); + } else { + wizard()->next(); + } } } From db3c7d01fd23b42e7f70a2809f6beb5a297375ec Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Mon, 16 Sep 2024 16:36:05 +0300 Subject: [PATCH 15/32] Apply suggestions from code review Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- CMakeLists.txt | 2 +- buildconfig/BuildConfig.cpp.in | 2 +- buildconfig/BuildConfig.h | 2 +- launcher/minecraft/auth/steps/MSAStep.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b9cb8ea8e..ecca591cbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,7 +176,7 @@ endif() set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.") set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'") set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help") -set(Launcher_SUCCESSFULL_LOGIN_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user succesfully logins.") +set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.") set(Launcher_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for FML Libraries.") ######## Set version numbers ######## diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index 121ce07297..a758122d9d 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -116,7 +116,7 @@ Config::Config() NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@"; NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@"; HELP_URL = "@Launcher_HELP_URL@"; - SUCCESSFULL_LOGIN_URL = "@Launcher_SUCCESSFULL_LOGIN_URL@"; + LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@"; IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@"; MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@"; FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@"; diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 4684f5709a..0626c1d50a 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -136,7 +136,7 @@ class Config { /** * URL that gets opened when the user succesfully logins. */ - QString SUCCESSFULL_LOGIN_URL; + QString LOGIN_CALLBACK_URL; /** * Client ID you can get from Imgur when you register an application diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 916f84e4a0..74999414c8 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -99,7 +99,7 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile window.location.replace("%1"); )XXX") - .arg(BuildConfig.SUCCESSFULL_LOGIN_URL)); + .arg(BuildConfig.LOGIN_CALLBACK_URL)); oauth2.setReplyHandler(replyHandler); } else { oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this)); From 8d14060ad14e6463825210c2d28c266e1563e3e1 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Mon, 16 Sep 2024 16:46:01 +0300 Subject: [PATCH 16/32] Apply suggestions from code review Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- launcher/java/download/SymlinkTask.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/launcher/java/download/SymlinkTask.cpp b/launcher/java/download/SymlinkTask.cpp index 9841c5015a..843c7caa9c 100644 --- a/launcher/java/download/SymlinkTask.cpp +++ b/launcher/java/download/SymlinkTask.cpp @@ -43,7 +43,7 @@ QString findBinPath(QString root, QString pattern) void SymlinkTask::executeTask() { - setStatus(tr("Check Java bin")); + setStatus(tr("Checking for Java binary path")); const auto binPath = FS::PathCombine("bin", "java"); const auto wantedPath = FS::PathCombine(m_path, binPath); if (QFileInfo::exists(wantedPath)) { @@ -51,16 +51,16 @@ void SymlinkTask::executeTask() return; } - setStatus(tr("Search for Java bin")); + setStatus(tr("Searching for Java binary path")); const auto contentsPartialPath = FS::PathCombine("Contents", "Home", binPath); const auto relativePathToBin = findBinPath(m_path, contentsPartialPath); if (relativePathToBin.isEmpty()) { - emitFailed(tr("Failed to find bin java")); + emitFailed(tr("Failed to find Java binary path")); return; } const auto folderToLink = relativePathToBin.chopped(binPath.length()); - setStatus(tr("Collect folders to symlink")); + setStatus(tr("Collecting folders to symlink")); auto entries = QDir(folderToLink).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries); QList files; setProgress(0, entries.length()); @@ -68,7 +68,7 @@ void SymlinkTask::executeTask() files.append({ entry.absoluteFilePath(), FS::PathCombine(m_path, entry.fileName()) }); } - setStatus(tr("Symlink Java")); + setStatus(tr("Symlinking Java binary path")); FS::create_link folderLink(files); connect(&folderLink, &FS::create_link::fileLinked, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); if (!folderLink()) { From 2e537e55de415d04d4eebda244c609e2c16ad894 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Mon, 16 Sep 2024 17:47:58 +0300 Subject: [PATCH 17/32] Apply suggestions from code review Co-authored-by: seth Signed-off-by: Alexandru Ionut Tripon --- CMakeLists.txt | 2 +- buildconfig/BuildConfig.cpp.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecca591cbe..cc01672c17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,7 +207,7 @@ set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/iss # Translations Platform URL set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.") -set(Launcher_TRANSLATIONS_BASE_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.") +set(Launcher_TRANSLATION_FILES_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.") # Matrix Space set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space") diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index a758122d9d..6585ebe312 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -128,7 +128,7 @@ Config::Config() BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@"; TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@"; - TRANSLATIONS_BASE_URL = "@Launcher_TRANSLATIONS_BASE_URL@"; + TRANSLATION_FILES_URL = "@Launcher_TRANSLATION_FILES_URL@"; MATRIX_URL = "@Launcher_MATRIX_URL@"; DISCORD_URL = "@Launcher_DISCORD_URL@"; SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@"; From e53a5b3eca40788c4acc6e2e806e0fd77ca92638 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 16 Sep 2024 17:51:35 +0300 Subject: [PATCH 18/32] replace TRANSLATIONS_BASE_URL with TRANSLATION_FILES_URL Signed-off-by: Trial97 --- buildconfig/BuildConfig.h | 2 +- launcher/translations/TranslationsModel.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 0626c1d50a..ae705d0981 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -171,7 +171,7 @@ class Config { QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL; - QString TRANSLATIONS_BASE_URL; + QString TRANSLATION_FILES_URL; QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp index 39719b1252..e8dc01873e 100644 --- a/launcher/translations/TranslationsModel.cpp +++ b/launcher/translations/TranslationsModel.cpp @@ -550,7 +550,7 @@ void TranslationsModel::downloadIndex() d->m_index_job.reset(new NetJob("Translations Index", APPLICATION->network())); MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json"); entry->setStale(true); - auto task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + "index_v2.json"), entry); + auto task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + "index_v2.json"), entry); d->m_index_task = task.get(); d->m_index_job->addNetAction(task); d->m_index_job->setAskRetry(false); @@ -591,7 +591,7 @@ void TranslationsModel::downloadTranslation(QString key) MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "mmc_" + key + ".qm"); entry->setStale(true); - auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATIONS_BASE_URL + lang->file_name), entry); + auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + lang->file_name), entry); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->file_sha1)); dl->setProgress(dl->getProgress(), lang->file_size); From 77463c6d33e8cafc66ac2b3fab59e167941af819 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 18 Sep 2024 21:17:15 +0300 Subject: [PATCH 19/32] display minecraft log before instance run Signed-off-by: Trial97 --- launcher/ui/pages/instance/LogPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/LogPage.cpp b/launcher/ui/pages/instance/LogPage.cpp index 0c22d1de66..6a4888f9ce 100644 --- a/launcher/ui/pages/instance/LogPage.cpp +++ b/launcher/ui/pages/instance/LogPage.cpp @@ -234,7 +234,7 @@ bool LogPage::apply() bool LogPage::shouldDisplay() const { - return m_instance->isRunning() || m_proxy->rowCount() > 0; + return true; } void LogPage::on_btnPaste_clicked() From 1243328876c7b655e15ab7211cc9162c32e16f30 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Sep 2024 16:53:36 +0300 Subject: [PATCH 20/32] fix folder duplication for modrinth Signed-off-by: Trial97 --- launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 7a3f71c3b6..ba97c441fc 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -302,7 +302,7 @@ bool ModrinthCreationTask::createInstance() loop.exec(); QEventLoop ensureMetaLoop; - QDir folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index"); + QDir folder = FS::PathCombine(instance.modsRoot(), ".index"); auto ensureMetadataTask = makeShared(mods, folder, ModPlatform::ResourceProvider::MODRINTH); connect(ensureMetadataTask.get(), &Task::succeeded, this, [&]() { ended_well = true; }); connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit); From 3e1cf6b2a7a13a85d412ee5b7f06032f17fa4635 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Sep 2024 16:58:22 +0300 Subject: [PATCH 21/32] fix updater typo Signed-off-by: Trial97 --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 6b97548642..8bf8cb473d 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -1210,7 +1210,7 @@ std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) QProcess proc = QProcess(); proc.start(cmd, args); if (!proc.waitForStarted(5000)) { // wait 5 seconds to start - auto msg = tr("Failed to launcher child process \"%1 %2\".").arg(cmd).arg(args.join(" ")); + auto msg = tr("Failed to launch child process \"%1 %2\".").arg(cmd).arg(args.join(" ")); logUpdate(msg); showFatalErrorMessage(tr("Failed extract archive"), msg); return std::nullopt; @@ -1241,7 +1241,7 @@ bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) proc.setReadChannel(QProcess::StandardOutput); proc.start(exe_path, { "--version" }); if (!proc.waitForStarted(5000)) { - showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read version.")); + showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launch child process to read version.")); return false; } // wait 5 seconds to start if (!proc.waitForFinished(5000)) { From 9567d763c1b0ed99370c1097abdc2db0a57d9fea Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Sep 2024 22:17:44 +0300 Subject: [PATCH 22/32] fix java archive typo Signed-off-by: Trial97 --- launcher/java/download/ArchiveDownloadTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index ba1c96fafc..6d6ab0cef0 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -95,7 +95,7 @@ void ArchiveDownloadTask::extractJava(QString input) } auto files = zip->getFileNameList(); if (files.isEmpty()) { - emitFailed(tr("No files were found in the supplied zip file,")); + emitFailed(tr("No files were found in the supplied zip file.")); return; } m_task = makeShared(zip, m_final_path, files[0]); From 1b2e9b95e2dd62f844c5d019d9b0b503084dd382 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 25 Sep 2024 19:37:25 +0300 Subject: [PATCH 23/32] add prefix to non-standard packwiz fields Signed-off-by: Trial97 --- launcher/modplatform/packwiz/Packwiz.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 77a0935f39..325b0a6e48 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -208,9 +208,9 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, { "side", sideToString(mod.side).toStdString() }, - { "loaders", loaders }, - { "mcVersions", mcVersions }, - { "releaseType", mod.releaseType.toString().toStdString() }, + { "x-prismlauncher-loaders", loaders }, + { "x-prismlauncher-mc-versions", mcVersions }, + { "x-prismlauncher-release-type", mod.releaseType.toString().toStdString() }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -295,15 +295,15 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); mod.side = stringToSide(stringEntry(table, "side")); - mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "releaseType")); - if (auto loaders = table["loaders"]; loaders && loaders.is_array()) { + mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "x-prismlauncher-release-type")); + if (auto loaders = table["x-prismlauncher-loaders"]; loaders && loaders.is_array()) { for (auto&& loader : *loaders.as_array()) { if (loader.is_string()) { mod.loaders |= ModPlatform::getModLoaderFromString(QString::fromStdString(loader.as_string()->value_or(""))); } } } - if (auto versions = table["mcVersions"]; versions && versions.is_array()) { + if (auto versions = table["x-prismlauncher-mc-versions"]; versions && versions.is_array()) { for (auto&& version : *versions.as_array()) { if (version.is_string()) { auto ver = QString::fromStdString(version.as_string()->value_or("")); From 6475dc5786600ee19d512f9b11f662a240922fa1 Mon Sep 17 00:00:00 2001 From: Cart <81428538+cartrigger@users.noreply.github.com> Date: Thu, 26 Sep 2024 01:58:21 -0400 Subject: [PATCH 24/32] Lots of J's for Java needed to be uppercased --- launcher/VersionProxyModel.cpp | 4 ++-- launcher/java/download/ArchiveDownloadTask.cpp | 2 +- launcher/ui/MainWindow.ui | 2 +- launcher/ui/java/InstallJavaDialog.cpp | 8 ++++---- launcher/ui/pages/global/JavaPage.cpp | 4 ++-- launcher/ui/setupwizard/AutoJavaWizardPage.ui | 2 +- launcher/ui/setupwizard/JavaWizardPage.cpp | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/launcher/VersionProxyModel.cpp b/launcher/VersionProxyModel.cpp index 552900d35c..12a82f73d5 100644 --- a/launcher/VersionProxyModel.cpp +++ b/launcher/VersionProxyModel.cpp @@ -140,9 +140,9 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, case Path: return tr("Filesystem path to this version"); case JavaName: - return tr("The alternative name of the java version"); + return tr("The alternative name of the Java version"); case JavaMajor: - return tr("The java major version"); + return tr("The Java major version"); case Time: return tr("Release date of this version"); } diff --git a/launcher/java/download/ArchiveDownloadTask.cpp b/launcher/java/download/ArchiveDownloadTask.cpp index 6d6ab0cef0..bb7cc568d9 100644 --- a/launcher/java/download/ArchiveDownloadTask.cpp +++ b/launcher/java/download/ArchiveDownloadTask.cpp @@ -65,7 +65,7 @@ void ArchiveDownloadTask::executeTask() void ArchiveDownloadTask::extractJava(QString input) { - setStatus(tr("Extracting java")); + setStatus(tr("Extracting Java")); if (input.endsWith("tar")) { setStatus(tr("Extracting Java (Progress is not reported for tar archives)")); QFile in(input); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 89e536b01f..f20c342066 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -798,7 +798,7 @@ Java - Open the java folder in a file browser. Only available if the built-in Java downloader is used. + Open the Java folder in a file browser. Only available if the built-in Java downloader is used. diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp index f01edc5e56..0ece3220bc 100644 --- a/launcher/ui/java/InstallJavaDialog.cpp +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -57,13 +57,13 @@ class InstallJavaPage : public QWidget, public BasePage { majorVersionSelect = new VersionSelectWidget(this); majorVersionSelect->selectCurrent(); - majorVersionSelect->setEmptyString(tr("No java versions are currently available in the meta.")); - majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + majorVersionSelect->setEmptyString(tr("No Java versions are currently available in the meta.")); + majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!")); horizontalLayout->addWidget(majorVersionSelect, 1); javaVersionSelect = new VersionSelectWidget(this); - javaVersionSelect->setEmptyString(tr("No java versions are currently available for your OS.")); - javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the java version lists!")); + javaVersionSelect->setEmptyString(tr("No Java versions are currently available for your OS.")); + javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!")); horizontalLayout->addWidget(javaVersionSelect, 4); connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion); connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged); diff --git a/launcher/ui/pages/global/JavaPage.cpp b/launcher/ui/pages/global/JavaPage.cpp index 6699b00c0a..0ae2968152 100644 --- a/launcher/ui/pages/global/JavaPage.cpp +++ b/launcher/ui/pages/global/JavaPage.cpp @@ -67,8 +67,8 @@ JavaPage::JavaPage(QWidget* parent) : QWidget(parent), ui(new Ui::JavaPage) ui->managedJavaList->initialize(new JavaInstallList(this, true)); ui->managedJavaList->setResizeOn(2); ui->managedJavaList->selectCurrent(); - ui->managedJavaList->setEmptyString(tr("No managed java versions are installed")); - ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed java list!")); + ui->managedJavaList->setEmptyString(tr("No managed Java versions are installed")); + ui->managedJavaList->setEmptyErrorString(tr("Couldn't load the managed Java list!")); connect(ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] { ui->autodownloadCheckBox->setEnabled(ui->autodetectJavaCheckBox->isChecked()); if (!ui->autodetectJavaCheckBox->isChecked()) diff --git a/launcher/ui/setupwizard/AutoJavaWizardPage.ui b/launcher/ui/setupwizard/AutoJavaWizardPage.ui index bd72cf6957..a862524b01 100644 --- a/launcher/ui/setupwizard/AutoJavaWizardPage.ui +++ b/launcher/ui/setupwizard/AutoJavaWizardPage.ui @@ -30,7 +30,7 @@ - We've added a feature to automatically download the correct Java version for each version of Minecraft(this can be changed in the Java Settings). Would you like to enable or disable this feature? + We've added a feature to automatically download the correct Java version for each version of Minecraft (this can be changed in the Java Settings). Would you like to enable or disable this feature? true diff --git a/launcher/ui/setupwizard/JavaWizardPage.cpp b/launcher/ui/setupwizard/JavaWizardPage.cpp index 47718d6da6..8caae173cb 100644 --- a/launcher/ui/setupwizard/JavaWizardPage.cpp +++ b/launcher/ui/setupwizard/JavaWizardPage.cpp @@ -83,6 +83,6 @@ void JavaWizardPage::retranslate() { setTitle(tr("Java")); setSubTitle( - tr("Please select how much memory to allocate to instances and if Prism Launcher should manage java automatically or manually.")); + tr("Please select how much memory to allocate to instances and if Prism Launcher should manage Java automatically or manually.")); m_java_widget->retranslate(); } From b9c19fd3f73877f80fb2363617d893f91e5037e2 Mon Sep 17 00:00:00 2001 From: Mason Rocha Date: Fri, 27 Sep 2024 16:42:59 -0500 Subject: [PATCH 25/32] fix: don't annoy when java tmpdir jvmarg is set & /tmp is noexec Signed-off-by: Mason Rocha Got annoyed, didn't want to see it anymore. Java tmpdir noexec warning modified to include information about possible remedies available --- launcher/Application.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6379678c9c..4a66271a31 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1025,7 +1025,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // notify user if /tmp is mounted with `noexec` (#1693) - { + QString jvmArgs = m_settings->get("JvmArgs").toString(); + if(jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */ bool is_tmp_noexec = false; #if defined(Q_OS_LINUX) @@ -1045,7 +1046,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) if (is_tmp_noexec) { auto infoMsg = tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n" - "Some versions of Minecraft may not launch.\n"); + "Some versions of Minecraft may not launch.\n" + "\n" + "You may solve this issue by remounting /tmp as 'exec' or setting " + "the java.io.tmpdir JVM argument to a writeable directory in a " + "filesystem where the 'exec' flag is set (e.g., /home/user/.local/tmp)\n"); auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok); msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setAttribute(Qt::WA_DeleteOnClose); From 732008db203d1ff6cc7c5d4957cb394d016eaf79 Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 19 Sep 2024 16:27:06 -0400 Subject: [PATCH 26/32] ci: drop garnix Signed-off-by: seth --- garnix.yaml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 garnix.yaml diff --git a/garnix.yaml b/garnix.yaml deleted file mode 100644 index a7c1b48a9f..0000000000 --- a/garnix.yaml +++ /dev/null @@ -1,10 +0,0 @@ -builds: - exclude: - # Currently broken on Garnix's end - - "*.x86_64-darwin.*" - include: - - "checks.x86_64-linux.*" - - "packages.x86_64-linux.*" - - "packages.aarch64-linux.*" - - "packages.x86_64-darwin.*" - - "packages.aarch64-darwin.*" From 2663ac5405cbfc3c29a38b32a755b7c7ebda0779 Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 19 Sep 2024 16:34:59 -0400 Subject: [PATCH 27/32] build(nix): add debug builds This will lead to build times on par with our other CI jobs, as now we're not always building for release Signed-off-by: seth --- flake.nix | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/flake.nix b/flake.nix index 987fc0eda6..d07834b4c0 100644 --- a/flake.nix +++ b/flake.nix @@ -118,5 +118,24 @@ # Only output them if they're available on the current system lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages ); + + # We put these under legacyPackages as they are meant for CI, not end user consumption + legacyPackages = forAllSystems ( + system: + let + prismPackages = self.packages.${system}; + legacyPackages = self.legacyPackages.${system}; + in + { + prismlauncher-debug = prismPackages.prismlauncher.override { + prismlauncher-unwrapped = legacyPackages.prismlauncher-unwrapped-debug; + }; + + prismlauncher-unwrapped-debug = prismPackages.prismlauncher-unwrapped.overrideAttrs { + cmakeBuildType = "Debug"; + dontStrip = true; + }; + } + ); }; } From 5adca58337e9fe2a71f7bdd0951494ceddb34659 Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 19 Sep 2024 16:44:19 -0400 Subject: [PATCH 28/32] ci: add nix job Signed-off-by: seth --- .github/workflows/build.yml | 53 +++++++++++++++++++++++++++ .github/workflows/trigger_builds.yml | 1 + .github/workflows/trigger_release.yml | 1 + 3 files changed, 55 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ccba625411..f26884c2c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,9 @@ on: APPLE_NOTARIZE_PASSWORD: description: Password used for notarizing macOS builds required: false + CACHIX_AUTH_TOKEN: + description: Private token for authenticating against Cachix cache + required: false GPG_PRIVATE_KEY: description: Private key for AppImage signing required: false @@ -631,3 +634,53 @@ jobs: with: bundle: "Prism Launcher.flatpak" manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml + + nix: + name: Nix (${{ matrix.system }}) + + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + system: x86_64-linux + + - os: macos-13 + system: x86_64-darwin + + - os: macos-14 + system: aarch64-darwin + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v27 + + # For PRs + - name: Setup Nix Magic Cache + uses: DeterminateSystems/magic-nix-cache-action@v8 + + # For in-tree builds + - name: Setup Cachix + uses: cachix/cachix-action@v15 + with: + name: prismlauncher + authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + + - name: Run flake checks + run: | + nix flake check --print-build-logs --show-trace + + - name: Build debug package + if: ${{ inputs.build_type == 'Debug' }} + run: | + nix build --print-build-logs .#prismlauncher-debug + + - name: Build release package + if: ${{ inputs.build_type != 'Debug' }} + run: | + nix build --print-build-logs .#prismlauncher diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml index 9efafc8cc2..0b8386d697 100644 --- a/.github/workflows/trigger_builds.yml +++ b/.github/workflows/trigger_builds.yml @@ -38,5 +38,6 @@ jobs: APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }} APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 134281b2c9..e800653e3c 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -22,6 +22,7 @@ jobs: APPLE_NOTARIZE_APPLE_ID: ${{ secrets.APPLE_NOTARIZE_APPLE_ID }} APPLE_NOTARIZE_TEAM_ID: ${{ secrets.APPLE_NOTARIZE_TEAM_ID }} APPLE_NOTARIZE_PASSWORD: ${{ secrets.APPLE_NOTARIZE_PASSWORD }} + CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }} From 68bf500f7ee48f17abf2a27e942d953a9962ef55 Mon Sep 17 00:00:00 2001 From: seth Date: Thu, 19 Sep 2024 16:50:45 -0400 Subject: [PATCH 29/32] chore: add nix reformat to .git-blame-ignore-revs Signed-off-by: seth --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 2163db45bb..528b128b17 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -2,3 +2,6 @@ # tabs -> spaces bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9 + +# (nix) alejandra -> nixfmt +4c81d8c53d09196426568c4a31a4e752ed05397a From bf432b5514d7c9c90761a285d17afbd736e4a687 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 30 Sep 2024 16:21:07 -0400 Subject: [PATCH 30/32] chore(nix): use cachix for binary cache Signed-off-by: seth --- flake.nix | 6 ++++-- nix/README.md | 20 ++++++++------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/flake.nix b/flake.nix index d07834b4c0..f4ca782ecb 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,10 @@ description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)"; nixConfig = { - extra-substituters = [ "https://cache.garnix.io" ]; - extra-trusted-public-keys = [ "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" ]; + extra-substituters = [ "https://prismlauncher.cachix.org" ]; + extra-trusted-public-keys = [ + "prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c=" + ]; }; inputs = { diff --git a/nix/README.md b/nix/README.md index 76cb8bf270..8bb658477c 100644 --- a/nix/README.md +++ b/nix/README.md @@ -8,8 +8,8 @@ See [Package variants](#package-variants) for a list of available packages. ## Installing a development release (flake) -We use [garnix](https://garnix.io/) to build and cache our development builds. -If you want to avoid rebuilds you may add the garnix cache to your substitutors, or use `--accept-flake-config` +We use [cachix](https://cachix.org/) to cache our development and release builds. +If you want to avoid rebuilds you may add the Cachix bucket to your substitutors, or use `--accept-flake-config` to temporarily enable it when using `nix` commands. Example (NixOS): @@ -17,12 +17,10 @@ Example (NixOS): ```nix { nix.settings = { - trusted-substituters = [ - "https://cache.garnix.io" - ]; + trusted-substituters = [ "https://prismlauncher.cachix.org" ]; trusted-public-keys = [ - "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" + "prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c=" ]; }; } @@ -137,20 +135,18 @@ nix profile install github:PrismLauncher/PrismLauncher ## Installing a development release (without flakes) -We use [garnix](https://garnix.io/) to build and cache our development builds. -If you want to avoid rebuilds you may add the garnix cache to your substitutors. +We use [Cachix](https://cachix.org/) to cache our development and release builds. +If you want to avoid rebuilds you may add the Cachix bucket to your substitutors. Example (NixOS): ```nix { nix.settings = { - trusted-substituters = [ - "https://cache.garnix.io" - ]; + trusted-substituters = [ "https://prismlauncher.cachix.org" ]; trusted-public-keys = [ - "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=" + "prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c=" ]; }; } From 988ef320193e5bfb7de600bc38f99f9ba122c379 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 30 Sep 2024 16:30:22 -0400 Subject: [PATCH 31/32] fix(nix): don't write to files in formatting check Signed-off-by: seth --- nix/checks.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/checks.nix b/nix/checks.nix index 40a2e272f0..ec219d6f84 100644 --- a/nix/checks.nix +++ b/nix/checks.nix @@ -23,7 +23,7 @@ cd ${self} echo "Running clang-format...." - clang-format -i --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp} + clang-format --dry-run --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp} echo "Running deadnix..." deadnix --fail From 38fa2e0cd494d400110496a35c7f112b2b801d97 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 30 Sep 2024 16:32:35 -0400 Subject: [PATCH 32/32] style: format with clang-format Signed-off-by: seth --- launcher/Application.cpp | 2 +- launcher/ui/MainWindow.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 4a66271a31..b8dcc10991 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1026,7 +1026,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // notify user if /tmp is mounted with `noexec` (#1693) QString jvmArgs = m_settings->get("JvmArgs").toString(); - if(jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */ + if (jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */ bool is_tmp_noexec = false; #if defined(Q_OS_LINUX) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 3f0bb3281e..09c47b6099 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -235,7 +235,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi } ui->actionViewJavaFolder->setEnabled(BuildConfig.JAVA_DOWNLOADER_ENABLED); - } // add the toolbar toggles to the view menu