From 43cc04433d2bccd8c81bae56920bd464cbb27fe8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 19 Jul 2023 11:58:27 +0300 Subject: [PATCH 001/139] feat: refactored Instance ImportTask Signed-off-by: Trial97 --- launcher/InstanceImportTask.cpp | 201 +++++++++---------- launcher/InstanceImportTask.h | 50 ++--- launcher/MMCZip.cpp | 105 ++++++++++ launcher/MMCZip.h | 25 +++ launcher/ui/pages/modplatform/ImportPage.cpp | 1 + 5 files changed, 245 insertions(+), 137 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 352848f02..36c5b8f94 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -45,14 +45,15 @@ #include "icons/IconList.h" #include "icons/IconUtils.h" -#include "modplatform/technic/TechnicPackProcessor.h" -#include "modplatform/modrinth/ModrinthInstanceCreationTask.h" #include "modplatform/flame/FlameInstanceCreationTask.h" +#include "modplatform/modrinth/ModrinthInstanceCreationTask.h" +#include "modplatform/technic/TechnicPackProcessor.h" #include "settings/INISettingsObject.h" #include #include +#include #include @@ -65,15 +66,8 @@ bool InstanceImportTask::abort() if (!canAbort()) return false; - if (m_filesNetJob) - m_filesNetJob->abort(); - if (m_extractFuture.isRunning()) { - // NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled, - // but we can use this call to check the state when the extraction finishes. - m_extractFuture.cancel(); - m_extractFuture.waitForFinished(); - } - + if (task) + task->abort(); return Task::abort(); } @@ -86,7 +80,6 @@ void InstanceImportTask::executeTask() processZipPack(); } else { setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); - m_downloadRequired = true; const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); @@ -94,153 +87,153 @@ void InstanceImportTask::executeTask() entry->setStale(true); m_archivePath = entry->getFullPath(); - m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); + auto filesNetJob = makeShared(tr("Modpack download"), APPLICATION->network()); + filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry)); - connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); - connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); - connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); - connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); - connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); - - m_filesNetJob->start(); + connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack); + connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress); + connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); + connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed); + connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted); + task.reset(filesNetJob); + filesNetJob->start(); } } -void InstanceImportTask::downloadSucceeded() +QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root) { - processZipPack(); - m_filesNetJob.reset(); -} + if (!isRunning()) { + return {}; + } + QuaZipDir rootDir(zip, root); + for (auto&& fileName : rootDir.entryList(QDir::Files)) { + setDetails(fileName); + if (fileName == "instance.cfg") { + qDebug() << "MultiMC:" << true; + m_modpackType = ModpackType::MultiMC; + return root; + } + if (fileName == "manifest.json") { + qDebug() << "Flame:" << true; + m_modpackType = ModpackType::Flame; + return root; + } -void InstanceImportTask::downloadFailed(QString reason) -{ - emitFailed(reason); - m_filesNetJob.reset(); -} + QCoreApplication::processEvents(); + } -void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total) -{ - setProgress(current, total); -} + // Recurse the search to non-ignored subfolders + for (auto&& fileName : rootDir.entryList(QDir::Dirs)) { + if ("overrides/" == fileName) + continue; -void InstanceImportTask::downloadAborted() -{ - emitAborted(); - m_filesNetJob.reset(); + QString result = getRootFromZip(zip, root + fileName); + if (!result.isEmpty()) + return result; + } + + return {}; } void InstanceImportTask::processZipPack() { - setStatus(tr("Extracting modpack")); + setStatus(tr("Attempting to determine instance type")); QDir extractDir(m_stagingPath); qDebug() << "Attempting to create instance from" << m_archivePath; // open the zip and find relevant files in it - m_packZip.reset(new QuaZip(m_archivePath)); - if (!m_packZip->open(QuaZip::mdUnzip)) - { + auto packZip = std::make_shared(m_archivePath); + if (!packZip->open(QuaZip::mdUnzip)) { emitFailed(tr("Unable to open supplied modpack zip file.")); return; } - QuaZipDir packZipDir(m_packZip.get()); + QuaZipDir packZipDir(packZip.get()); + qDebug() << "Attempting to determine instance type"; - // https://docs.modrinth.com/docs/modpacks/format_definition/#storage - bool modrinthFound = packZipDir.exists("/modrinth.index.json"); - bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json"); QString root; // NOTE: Prioritize modpack platforms that aren't searched for recursively. // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example - if(modrinthFound) - { + // https://docs.modrinth.com/docs/modpacks/format_definition/#storage + if (packZipDir.exists("/modrinth.index.json")) { // process as Modrinth pack - qDebug() << "Modrinth:" << modrinthFound; + qDebug() << "Modrinth:" << true; m_modpackType = ModpackType::Modrinth; - } - else if (technicFound) - { + } else if (packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json")) { // process as Technic pack - qDebug() << "Technic:" << technicFound; + qDebug() << "Technic:" << true; extractDir.mkpath(".minecraft"); extractDir.cd(".minecraft"); m_modpackType = ModpackType::Technic; + } else { + root = getRootFromZip(packZip.get()); + setDetails(""); } - else - { - QStringList paths_to_ignore { "overrides/" }; - - if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) { - // process as MultiMC instance/pack - qDebug() << "MultiMC:" << mmcRoot; - root = mmcRoot; - m_modpackType = ModpackType::MultiMC; - } else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) { - // process as Flame pack - qDebug() << "Flame:" << flameRoot; - root = flameRoot; - m_modpackType = ModpackType::Flame; - } - } - if(m_modpackType == ModpackType::Unknown) - { + if (m_modpackType == ModpackType::Unknown) { emitFailed(tr("Archive does not contain a recognized modpack type.")); return; } + setStatus(tr("Extracting modpack")); // make sure we extract just the pack - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath()); - connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &InstanceImportTask::extractFinished); - m_extractFutureWatcher.setFuture(m_extractFuture); + auto zipTask = makeShared(packZip, extractDir, root); + + auto progressStep = std::make_shared(); + connect(zipTask.get(), &Task::finished, this, [this, progressStep] { + progressStep->state = TaskStepState::Succeeded; + stepProgress(*progressStep); + }); + + connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished); + connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); + connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { + progressStep->state = TaskStepState::Failed; + stepProgress(*progressStep); + emitFailed(reason); + }); + connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); + + connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { + progressStep->update(current, total); + stepProgress(*progressStep); + }); + connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { + progressStep->status = status; + stepProgress(*progressStep); + }); + task.reset(zipTask); + zipTask->start(); } void InstanceImportTask::extractFinished() { - m_packZip.reset(); - - if (m_extractFuture.isCanceled()) - return; - if (!m_extractFuture.result().has_value()) { - emitFailed(tr("Failed to extract modpack")); - return; - } - QDir extractDir(m_stagingPath); qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); - while (it.hasNext()) - { + while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; - if(file.isDir()) - { + if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; - } - else - { + } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } - if(origPermissions != permissions) - { - if(!QFile::setPermissions(filepath, permissions)) - { + if (origPermissions != permissions) { + if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); - } - else - { + } else { qDebug() << "Fixed" << filepath; } } } - switch(m_modpackType) - { + switch (m_modpackType) { case ModpackType::MultiMC: processMultiMC(); return; @@ -276,7 +269,8 @@ void InstanceImportTask::processFlame() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = + makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { // FIXME: Find a way to get IDs in directly imported ZIPs inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); @@ -286,7 +280,7 @@ void InstanceImportTask::processFlame() inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); - + connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] { setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); emitSucceeded(); @@ -362,7 +356,8 @@ void InstanceImportTask::processModrinth() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = + new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { QString pack_id; if (!m_sourceUrl.isEmpty()) { @@ -378,7 +373,7 @@ void InstanceImportTask::processModrinth() inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); - + connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] { setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID()); emitSucceeded(); diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index 7fda439fc..cf4025194 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -35,64 +35,46 @@ #pragma once -#include "InstanceTask.h" -#include "net/NetJob.h" -#include #include #include -#include "settings/SettingsObject.h" -#include "QObjectPtr.h" -#include "modplatform/flame/PackManifest.h" +#include +#include "InstanceTask.h" +#include #include class QuaZip; -namespace Flame -{ - class FileResolvingTask; +namespace Flame { +class FileResolvingTask; } -class InstanceImportTask : public InstanceTask -{ +class InstanceImportTask : public InstanceTask { Q_OBJECT -public: + public: explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); bool abort() override; - const QVector &getBlockedFiles() const - { - return m_blockedMods; - } -protected: + protected: //! Entry point for tasks. virtual void executeTask() override; -private: - void processZipPack(); + private: void processMultiMC(); void processTechnic(); void processFlame(); void processModrinth(); + QString getRootFromZip(QuaZip* zip, const QString& root = ""); -private slots: - void downloadSucceeded(); - void downloadFailed(QString reason); - void downloadProgressChanged(qint64 current, qint64 total); - void downloadAborted(); + private slots: + void processZipPack(); void extractFinished(); -private: /* data */ - NetJob::Ptr m_filesNetJob; - shared_qobject_ptr m_modIdResolver; + private: /* data */ QUrl m_sourceUrl; QString m_archivePath; - bool m_downloadRequired = false; - std::unique_ptr m_packZip; - QFuture> m_extractFuture; - QFutureWatcher> m_extractFutureWatcher; - QVector m_blockedMods; - enum class ModpackType{ + Task::Ptr task; + enum class ModpackType { Unknown, MultiMC, Technic, @@ -104,6 +86,6 @@ private: /* data */ // the source URL / the resource it points to alone. QMap m_extra_info; - //FIXME: nuke + // FIXME: nuke QWidget* m_parent; }; diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index acd6bf7e4..13ad96e4e 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -501,4 +501,109 @@ bool ExportToZipTask::abort() return false; } +void ExtractZipTask::executeTask() +{ + m_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); }); + connect(&m_zip_watcher, &QFutureWatcher::finished, this, &ExtractZipTask::finish); + m_zip_watcher.setFuture(m_zip_future); +} + +auto ExtractZipTask::extractZip() -> ZipResult +{ + auto target = m_output_dir.absolutePath(); + auto target_top_dir = QUrl::fromLocalFile(target); + + QStringList extracted; + + qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input->getZipName() << "to" << target; + auto numEntries = m_input->getEntriesCount(); + if (numEntries < 0) { + return ZipResult(tr("Failed to enumerate files in archive")); + } + if (numEntries == 0) { + logWarning(tr("Extracting empty archives seems odd...")); + return ZipResult(); + } + if (!m_input->goToFirstFile()) { + return ZipResult(tr("Failed to seek to first file in zip")); + } + + setStatus("Extracting files..."); + setProgress(0, numEntries); + do { + if (m_zip_future.isCanceled()) + return ZipResult(); + setProgress(m_progress + 1, m_progressTotal); + QString file_name = m_input->getCurrentFileName(); + if (!file_name.startsWith(m_subdirectory)) + continue; + + auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, m_subdirectory.size())); + auto original_name = relative_file_name; + setStatus("Unziping: " + relative_file_name); + + // Fix subdirs/files ending with a / getting transformed into absolute paths + if (relative_file_name.startsWith('/')) + relative_file_name = relative_file_name.mid(1); + + // Fix weird "folders with a single file get squashed" thing + QString sub_path; + if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) { + sub_path = relative_file_name.section('/', 0, -2) + '/'; + FS::ensureFolderPathExists(FS::PathCombine(target, sub_path)); + + relative_file_name = relative_file_name.split('/').last(); + } + + QString target_file_path; + if (relative_file_name.isEmpty()) { + target_file_path = target + '/'; + } else { + target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name); + if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/')) + target_file_path += '/'; + } + + if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { + return ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2") + .arg(relative_file_name, target)); + } + + if (!JlCompress::extractFile(m_input.get(), "", target_file_path)) { + JlCompress::removeFile(extracted); + return ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path)); + } + + extracted.append(target_file_path); + QFile::setPermissions(target_file_path, + QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + + qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; + } while (m_input->goToNextFile()); + + return ZipResult(); +} + +void ExtractZipTask::finish() +{ + if (m_zip_future.isCanceled()) { + emitAborted(); + } else if (auto result = m_zip_future.result(); result.has_value()) { + emitFailed(result.value()); + } else { + emitSucceeded(); + } +} + +bool ExtractZipTask::abort() +{ + if (m_zip_future.isRunning()) { + m_zip_future.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} + } // namespace MMCZip \ No newline at end of file diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index bc527ad1b..212be6f51 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -189,4 +189,29 @@ class ExportToZipTask : public Task { QFuture m_build_zip_future; QFutureWatcher m_build_zip_watcher; }; + +class ExtractZipTask : public Task { + public: + ExtractZipTask(std::shared_ptr input, QDir outputDir, QString subdirectory = "") + : m_input(input), m_output_dir(outputDir), m_subdirectory(subdirectory) + {} + virtual ~ExtractZipTask() = default; + + typedef std::optional ZipResult; + + protected: + virtual void executeTask() override; + bool abort() override; + + ZipResult extractZip(); + void finish(); + + private: + std::shared_ptr m_input; + QDir m_output_dir; + QString m_subdirectory; + + QFuture m_zip_future; + QFutureWatcher m_zip_watcher; +}; } // namespace MMCZip diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 30196aad6..a45f3a81c 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -38,6 +38,7 @@ #include "ui_ImportPage.h" #include +#include #include #include "ui/dialogs/NewInstanceDialog.h" From 907c2fd19c148230d58a3d02fd31ecd8bf7aae86 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Aug 2023 09:16:17 +0300 Subject: [PATCH 002/139] added missing header Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ImportPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 3cb161629..cc11d2c16 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -52,6 +52,7 @@ #include "Json.h" #include "InstanceImportTask.h" +#include "net/NetJob.h" class UrlValidator : public QValidator { public: From bf810053b720ac728affd45878147ce593b4f6fd Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 21 Aug 2023 17:21:11 +0300 Subject: [PATCH 003/139] updated instance copy Signed-off-by: Trial97 --- launcher/FileSystem.h | 1 + launcher/InstanceCopyTask.cpp | 83 +++++++++++++++++++++++------------ launcher/InstanceCopyTask.h | 1 + 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index bfed576c1..946e55a2c 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -218,6 +218,7 @@ class create_link : public QObject { bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); } int totalLinked() { return m_linked; } + int totalToLink() { return static_cast(m_links_to_make.size()); } void runPrivileged() { runPrivileged(QString()); } void runPrivileged(const QString& offset); diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index 8abf30640..3a40bc06d 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -1,10 +1,12 @@ #include "InstanceCopyTask.h" #include #include +#include #include "FileSystem.h" #include "NullInstance.h" #include "pathmatcher/RegexpMatcher.h" #include "settings/INISettingsObject.h" +#include "tasks/Task.h" InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs) { @@ -38,38 +40,50 @@ void InstanceCopyTask::executeTask() { setStatus(tr("Copying instance %1").arg(m_origInstance->name())); - auto copySaves = [&]() { - QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft")); - QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft")); - - QString staging_mc_dir; - if (mcDir.exists() && !dotMCDir.exists()) - staging_mc_dir = mcDir.filePath(); - else - staging_mc_dir = dotMCDir.filePath(); - - FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves")); - savesCopy.followSymlinks(true); - - return savesCopy(); - }; - - m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] { + m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { if (m_useClone) { FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath); folderClone.matcher(m_matcher.get()); + folderClone(true); + setProgress(0, folderClone.totalCloned()); + connect(&folderClone, &FS::clone::fileCloned, + [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); return folderClone(); - } else if (m_useLinks || m_useHardLinks) { + } + if (m_useLinks || m_useHardLinks) { + std::unique_ptr savesCopy; + if (m_copySaves) { + QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft")); + QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft")); + + QString staging_mc_dir; + if (mcDir.exists() && !dotMCDir.exists()) + staging_mc_dir = mcDir.filePath(); + else + staging_mc_dir = dotMCDir.filePath(); + + savesCopy = std::make_unique(FS::PathCombine(m_origInstance->gameRoot(), "saves"), + FS::PathCombine(staging_mc_dir, "saves")); + savesCopy->followSymlinks(true); + (*savesCopy)(true); + setProgress(0, savesCopy->totalCopied()); + connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); + } FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath); int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get()); + folderLink(true); + setProgress(0, m_progressTotal + folderLink.totalToLink()); + connect(&folderLink, &FS::create_link::fileLinked, + [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); bool there_were_errors = false; if (!folderLink()) { #if defined Q_OS_WIN32 if (!m_useHardLinks) { + setProgress(0, m_progressTotal); qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks"; qDebug() << "attempting to run with privelage"; @@ -94,13 +108,11 @@ void InstanceCopyTask::executeTask() } } - if (m_copySaves) { - there_were_errors |= !copySaves(); + if (savesCopy) { + there_were_errors |= !(*savesCopy)(); } return got_priv_results && !there_were_errors; - } else { - qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str(); } #else qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str(); @@ -108,17 +120,19 @@ void InstanceCopyTask::executeTask() return false; } - if (m_copySaves) { - there_were_errors |= !copySaves(); + if (savesCopy) { + there_were_errors |= !(*savesCopy)(); } return !there_were_errors; - } else { - FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); - folderCopy.followSymlinks(false).matcher(m_matcher.get()); - - return folderCopy(); } + FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); + folderCopy.followSymlinks(false).matcher(m_matcher.get()); + + folderCopy(true); + setProgress(0, folderCopy.totalCopied()); + connect(&folderCopy, &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); + return folderCopy(); }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &InstanceCopyTask::copyAborted); @@ -171,3 +185,14 @@ void InstanceCopyTask::copyAborted() emitFailed(tr("Instance folder copy has been aborted.")); return; } + +bool InstanceCopyTask::abort() +{ + if (m_copyFutureWatcher.isRunning()) { + m_copyFutureWatcher.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_copyFutureWatcher` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} \ No newline at end of file diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h index 357c6df0b..0f7f1020d 100644 --- a/launcher/InstanceCopyTask.h +++ b/launcher/InstanceCopyTask.h @@ -19,6 +19,7 @@ class InstanceCopyTask : public InstanceTask { protected: //! Entry point for tasks. virtual void executeTask() override; + bool abort() override; void copyFinished(); void copyAborted(); From 609eaa67abd9e9844f5cd52f81c70826689cd90a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 21:23:51 +0300 Subject: [PATCH 004/139] refactored skin apis Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 1 + launcher/minecraft/services/CapeChange.cpp | 89 +++++--------------- launcher/minecraft/services/CapeChange.h | 30 +++---- launcher/minecraft/services/SkinDelete.cpp | 59 ++++--------- launcher/minecraft/services/SkinDelete.h | 26 +++--- launcher/minecraft/services/SkinUpload.cpp | 84 +++++++----------- launcher/minecraft/services/SkinUpload.h | 31 +++---- launcher/net/Logging.cpp | 1 + launcher/net/Logging.h | 1 + launcher/net/NetRequest.cpp | 2 + launcher/net/StaticHeaderProxy.h | 39 +++++++++ launcher/ui/dialogs/SkinUploadDialog.cpp | 5 +- launcher/ui/pages/global/AccountListPage.cpp | 2 +- 13 files changed, 145 insertions(+), 225 deletions(-) create mode 100644 launcher/net/StaticHeaderProxy.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..3b6218b7f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -139,6 +139,7 @@ set(NET_SOURCES net/HeaderProxy.h net/RawHeaderProxy.h net/ApiHeaderProxy.h + net/StaticHeaderProxy.h net/ApiDownload.h net/ApiDownload.cpp net/ApiUpload.cpp diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp index 2ba38a6af..5a7820b54 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/services/CapeChange.cpp @@ -35,87 +35,38 @@ #include "CapeChange.h" -#include -#include +#include -#include "Application.h" +#include "net/ByteArraySink.h" +#include "net/StaticHeaderProxy.h" -CapeChange::CapeChange(QObject* parent, QString token, QString cape) : Task(parent), m_capeId(cape), m_token(token) {} - -void CapeChange::setCape([[maybe_unused]] QString& cape) +CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); - auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->put(request, requestString.toUtf8()); + logCat = taskMCServicesLogC; +}; - setStatus(tr("Equipping cape")); - - m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); - connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); -} - -void CapeChange::clearCape() -{ - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active")); - auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->deleteResource(request); - - setStatus(tr("Removing cape")); - - m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); - connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); -} - -void CapeChange::executeTask() +QNetworkReply* CapeChange::getReply(QNetworkRequest& request) { if (m_capeId.isEmpty()) { - clearCape(); + setStatus(tr("Removing cape")); + return m_network->deleteResource(request); } else { - setCape(m_capeId); + setStatus(tr("Equipping cape")); + return m_network->post(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); } } -void CapeChange::downloadError(QNetworkReply::NetworkError error) +void CapeChange::init() { - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); + addHeaderProxy(new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() }, + })); } -void CapeChange::sslErrors(const QList& errors) +CapeChange::Ptr CapeChange::make(QString token, QString capeId) { - int i = 1; - for (auto error : errors) { - qCritical() << "Cape change SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void CapeChange::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); + auto up = makeShared(token, capeId); + up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"); + up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); + return up; } diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h index d0c893c44..74805ef43 100644 --- a/launcher/minecraft/services/CapeChange.h +++ b/launcher/minecraft/services/CapeChange.h @@ -1,31 +1,21 @@ #pragma once -#include -#include -#include -#include "QObjectPtr.h" -#include "tasks/Task.h" +#include "net/NetRequest.h" -class CapeChange : public Task { +class CapeChange : public Net::NetRequest { Q_OBJECT public: - CapeChange(QObject* parent, QString token, QString capeId); - virtual ~CapeChange() {} + using Ptr = shared_qobject_ptr; + CapeChange(QString token, QString capeId); + virtual ~CapeChange() = default; - private: - void setCape(QString& cape); - void clearCape(); + static CapeChange::Ptr make(QString token, QString capeId); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; private: QString m_capeId; QString m_token; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - void downloadFinished(); }; diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp index 9e9020692..7944637f6 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/services/SkinDelete.cpp @@ -35,56 +35,31 @@ #include "SkinDelete.h" -#include -#include +#include "net/ByteArraySink.h" +#include "net/StaticHeaderProxy.h" -#include "Application.h" - -SkinDelete::SkinDelete(QObject* parent, QString token) : Task(parent), m_token(token) {} - -void SkinDelete::executeTask() +SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active")); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->deleteResource(request); - m_reply = shared_qobject_ptr(rep); + logCat = taskMCServicesLogC; +}; +QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) +{ setStatus(tr("Deleting skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &SkinDelete::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &SkinDelete::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinDelete::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &SkinDelete::sslErrors); - connect(rep, &QNetworkReply::finished, this, &SkinDelete::downloadFinished); + return m_network->deleteResource(request); } -void SkinDelete::downloadError(QNetworkReply::NetworkError error) +void SkinDelete::init() { - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); + addHeaderProxy(new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() }, + })); } -void SkinDelete::sslErrors(const QList& errors) +SkinDelete::Ptr SkinDelete::make(QString token) { - int i = 1; - for (auto error : errors) { - qCritical() << "Skin Delete SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void SkinDelete::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); + auto up = makeShared(token); + up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"); + up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); + return up; } diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h index d5b2e63db..b0fb866cd 100644 --- a/launcher/minecraft/services/SkinDelete.h +++ b/launcher/minecraft/services/SkinDelete.h @@ -1,26 +1,20 @@ #pragma once -#include -#include -#include "tasks/Task.h" +#include "net/NetRequest.h" -typedef shared_qobject_ptr SkinDeletePtr; - -class SkinDelete : public Task { +class SkinDelete : public Net::NetRequest { Q_OBJECT public: - SkinDelete(QObject* parent, QString token); + using Ptr = shared_qobject_ptr; + SkinDelete(QString token); virtual ~SkinDelete() = default; + static SkinDelete::Ptr make(QString token); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + private: QString m_token; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - void downloadFinished(); }; diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp index 163b481b1..0400fa0f4 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/services/SkinUpload.cpp @@ -36,30 +36,17 @@ #include "SkinUpload.h" #include -#include -#include "Application.h" +#include "net/ByteArraySink.h" +#include "net/StaticHeaderProxy.h" -QByteArray getVariant(SkinUpload::Model model) +SkinUpload::SkinUpload(QString token, QByteArray skin, SkinUpload::Model model) : NetRequest(), m_model(model), m_skin(skin), m_token(token) { - switch (model) { - default: - qDebug() << "Unknown skin type!"; - case SkinUpload::STEVE: - return "CLASSIC"; - case SkinUpload::ALEX: - return "SLIM"; - } -} + logCat = taskMCServicesLogC; +}; -SkinUpload::SkinUpload(QObject* parent, QString token, QByteArray skin, SkinUpload::Model model) - : Task(parent), m_model(model), m_skin(skin), m_token(token) -{} - -void SkinUpload::executeTask() +QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { - QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins")); - request.setRawHeader("Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit()); QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart skin; @@ -69,50 +56,37 @@ void SkinUpload::executeTask() QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - model.setBody(getVariant(m_model)); + + switch (m_model) { + default: + qDebug() << "Unknown skin type!"; + emitFailed("Unknown skin type!"); + return nullptr; + case SkinUpload::STEVE: + model.setBody("CLASSIC"); + break; + case SkinUpload::ALEX: + model.setBody("SLIM"); + break; + } multiPart->append(skin); multiPart->append(model); - - QNetworkReply* rep = APPLICATION->network()->post(request, multiPart); - m_reply = shared_qobject_ptr(rep); - setStatus(tr("Uploading skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &SkinUpload::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &SkinUpload::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinUpload::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &SkinUpload::sslErrors); - connect(rep, &QNetworkReply::finished, this, &SkinUpload::downloadFinished); + return m_network->post(request, multiPart); } -void SkinUpload::downloadError(QNetworkReply::NetworkError error) +void SkinUpload::init() { - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); + addHeaderProxy(new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Bearer %1").arg(m_token).toLocal8Bit() }, + })); } -void SkinUpload::sslErrors(const QList& errors) +SkinUpload::Ptr SkinUpload::make(QString token, QByteArray skin, SkinUpload::Model model) { - int i = 1; - for (auto error : errors) { - qCritical() << "Skin Upload SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void SkinUpload::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); + auto up = makeShared(token, skin, model); + up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); + up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); + return up; } diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h index 5716aa996..2da836d52 100644 --- a/launcher/minecraft/services/SkinUpload.h +++ b/launcher/minecraft/services/SkinUpload.h @@ -1,34 +1,25 @@ #pragma once -#include -#include -#include -#include "tasks/Task.h" +#include "net/NetRequest.h" -typedef shared_qobject_ptr SkinUploadPtr; - -class SkinUpload : public Task { +class SkinUpload : public Net::NetRequest { Q_OBJECT public: + using Ptr = shared_qobject_ptr; enum Model { STEVE, ALEX }; // Note this class takes ownership of the file. - SkinUpload(QObject* parent, QString token, QByteArray skin, Model model = STEVE); - virtual ~SkinUpload() {} + SkinUpload(QString token, QByteArray skin, Model model = STEVE); + virtual ~SkinUpload() = default; + + static SkinUpload::Ptr make(QString token, QByteArray skin, Model model = STEVE); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; private: Model m_model; QByteArray m_skin; QString m_token; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - - void downloadFinished(); }; diff --git a/launcher/net/Logging.cpp b/launcher/net/Logging.cpp index a9b9db7cf..45d2dcc20 100644 --- a/launcher/net/Logging.cpp +++ b/launcher/net/Logging.cpp @@ -22,5 +22,6 @@ Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net") Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download") Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload") +Q_LOGGING_CATEGORY(taskMCServicesLogC, "launcher.task.minecraft.servicies") Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache") Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http") diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h index 4deed2b49..d3a11cdce 100644 --- a/launcher/net/Logging.h +++ b/launcher/net/Logging.h @@ -24,5 +24,6 @@ Q_DECLARE_LOGGING_CATEGORY(taskNetLogC) Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC) Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC) +Q_DECLARE_LOGGING_CATEGORY(taskMCServicesLogC) Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC) Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC) diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index ff59da18b..eef550e15 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -111,6 +111,8 @@ void NetRequest::executeTask() m_last_progress_bytes = 0; QNetworkReply* rep = getReply(request); + if (rep == nullptr) // it failed + return; m_reply.reset(rep); connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); diff --git a/launcher/net/StaticHeaderProxy.h b/launcher/net/StaticHeaderProxy.h new file mode 100644 index 000000000..0e62d80ff --- /dev/null +++ b/launcher/net/StaticHeaderProxy.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * + * 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 "net/HeaderProxy.h" + +namespace Net { + +class StaticHeaderProxy : public HeaderProxy { + public: + StaticHeaderProxy(QList hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){}; + virtual ~StaticHeaderProxy() = default; + + public: + virtual QList headers(const QNetworkRequest&) const override { return m_hdrs; }; + void setHeaders(QList hdrs) { m_hdrs = hdrs; }; + + private: + QList m_hdrs; +}; + +} // namespace Net \ No newline at end of file diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp index 5b3ebfa23..70f1e6760 100644 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ b/launcher/ui/dialogs/SkinUploadDialog.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -101,12 +102,12 @@ void SkinUploadDialog::on_buttonBox_accepted() } else if (ui->alexBtn->isChecked()) { model = SkinUpload::ALEX; } - skinUpload.addTask(shared_qobject_ptr(new SkinUpload(this, m_acct->accessToken(), FS::read(fileName), model))); + skinUpload.addTask(SkinUpload::make(m_acct->accessToken(), FS::read(fileName), model)); } auto selectedCape = ui->capeCombo->currentData().toString(); if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(shared_qobject_ptr(new CapeChange(this, m_acct->accessToken(), selectedCape))); + skinUpload.addTask(CapeChange::make(m_acct->accessToken(), selectedCape)); } if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index c95bfabdd..3dcf05e0a 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -268,7 +268,7 @@ void AccountListPage::on_actionDeleteSkin_triggered() QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); ProgressDialog prog(this); - auto deleteSkinTask = std::make_shared(this, account->accessToken()); + auto deleteSkinTask = SkinDelete::make(account->accessToken()); if (prog.execWithTask((Task*)deleteSkinTask.get()) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); return; From 9ad029e0286ee18a6531d673c052a48a5f40d8d5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 1 Sep 2023 23:10:33 +0300 Subject: [PATCH 005/139] added skins directory Signed-off-by: Trial97 --- launcher/Application.cpp | 1 + launcher/ui/MainWindow.cpp | 5 ++++ launcher/ui/MainWindow.h | 2 ++ launcher/ui/MainWindow.ui | 13 +++++++++++ launcher/ui/pages/global/LauncherPage.cpp | 13 +++++++++++ launcher/ui/pages/global/LauncherPage.h | 1 + launcher/ui/pages/global/LauncherPage.ui | 28 +++++++++++++++++++---- 7 files changed, 59 insertions(+), 4 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 66044d9ac..9cff11546 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -536,6 +536,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); + m_settings->registerSetting("SkinsDir", "skins"); // Editors m_settings->registerSetting("JsonEditor", QString()); diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 5e55a5abb..a82932e08 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1186,6 +1186,11 @@ void MainWindow::on_actionViewCentralModsFolder_triggered() DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true); } +void MainWindow::on_actionViewSkinsFolder_triggered() +{ + DesktopServices::openDirectory(APPLICATION->settings()->get("SkinsDir").toString(), true); +} + void MainWindow::on_actionViewIconThemeFolder_triggered() { DesktopServices::openDirectory(APPLICATION->themeManager()->getIconThemesFolder().path()); diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 0b6144522..5d816b5d0 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -118,6 +118,8 @@ class MainWindow : public QMainWindow { void on_actionViewWidgetThemeFolder_triggered(); void on_actionViewCatPackFolder_triggered(); + void on_actionViewSkinsFolder_triggered(); + void on_actionViewSelectedInstFolder_triggered(); void refreshInstances(); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 91b2c2703..266551588 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -190,6 +190,7 @@ + @@ -575,6 +576,18 @@ Open the central mods folder in a file browser. + + + + .. + + + View &Skins Folder + + + Open the skins folder in a file browser. + + Themes diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 7f22fdb50..5576a0339 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -173,6 +173,17 @@ void LauncherPage::on_downloadsDirBrowseBtn_clicked() } } +void LauncherPage::on_skinsDirBrowseBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Skins Folder"), ui->skinsDirTextBox->text()); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) { + QString cooked_dir = FS::NormalizePath(raw_dir); + ui->skinsDirTextBox->setText(cooked_dir); + } +} + void LauncherPage::on_metadataDisableBtn_clicked() { ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); @@ -205,6 +216,7 @@ void LauncherPage::applySettings() s->set("CentralModsDir", ui->modsDirTextBox->text()); s->set("IconsDir", ui->iconsDirTextBox->text()); s->set("DownloadsDir", ui->downloadsDirTextBox->text()); + s->set("SkinsDir", ui->skinsDirTextBox->text()); s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked()); auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId(); @@ -259,6 +271,7 @@ void LauncherPage::loadSettings() ui->modsDirTextBox->setText(s->get("CentralModsDir").toString()); ui->iconsDirTextBox->setText(s->get("IconsDir").toString()); ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString()); + ui->skinsDirTextBox->setText(s->get("SkinsDir").toString()); ui->downloadsDirWatchRecursiveCheckBox->setChecked(s->get("DownloadsDirWatchRecursive").toBool()); QString sortMode = s->get("InstSortMode").toString(); diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index e733224d2..f9aefb171 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -74,6 +74,7 @@ class LauncherPage : public QWidget, public BasePage { void on_modsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked(); void on_downloadsDirBrowseBtn_clicked(); + void on_skinsDirBrowseBtn_clicked(); void on_metadataDisableBtn_clicked(); /*! diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index bc259a9b8..87c4b5ef9 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -67,7 +67,7 @@ Folders - + &Downloads: @@ -90,13 +90,16 @@ - + - + + + + Browse @@ -147,7 +150,24 @@ - + + + + Browse + + + + + + + &Skins: + + + skinsDirTextBox + + + + When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge). From c86b8b0f70894e447c8a08e136b919987c3dffae Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 5 Sep 2023 00:18:36 +0300 Subject: [PATCH 006/139] added skin manage dialog Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 28 +- launcher/SkinUtils.cpp | 52 --- launcher/SkinUtils.h | 22 - launcher/minecraft/services/CapeChange.h | 21 - launcher/minecraft/services/SkinDelete.h | 20 - launcher/minecraft/services/SkinUpload.h | 25 -- .../{services => skins}/CapeChange.cpp | 6 +- launcher/minecraft/skins/CapeChange.h | 39 ++ .../{services => skins}/SkinDelete.cpp | 3 +- launcher/minecraft/skins/SkinDelete.h | 38 ++ launcher/minecraft/skins/SkinList.cpp | 393 ++++++++++++++++++ launcher/minecraft/skins/SkinList.h | 80 ++++ launcher/minecraft/skins/SkinModel.cpp | 128 ++++++ launcher/minecraft/skins/SkinModel.h | 58 +++ .../{services => skins}/SkinUpload.cpp | 28 +- launcher/minecraft/skins/SkinUpload.h | 42 ++ launcher/net/Logging.cpp | 2 +- launcher/net/Logging.h | 2 +- launcher/net/NetJob.h | 4 +- launcher/net/NetRequest.cpp | 1 + launcher/net/NetRequest.h | 1 + launcher/net/StaticHeaderProxy.h | 2 +- launcher/ui/MainWindow.cpp | 1 - launcher/ui/dialogs/ProfileSelectDialog.cpp | 1 - launcher/ui/dialogs/SkinUploadDialog.cpp | 165 -------- launcher/ui/dialogs/SkinUploadDialog.h | 28 -- launcher/ui/dialogs/SkinUploadDialog.ui | 95 ----- .../ui/dialogs/skins/SkinManageDialog.cpp | 339 +++++++++++++++ launcher/ui/dialogs/skins/SkinManageDialog.h | 62 +++ launcher/ui/dialogs/skins/SkinManageDialog.ui | 193 +++++++++ launcher/ui/pages/global/AccountListPage.cpp | 34 +- launcher/ui/pages/global/AccountListPage.h | 3 +- launcher/ui/pages/global/AccountListPage.ui | 24 +- 33 files changed, 1425 insertions(+), 515 deletions(-) delete mode 100644 launcher/SkinUtils.cpp delete mode 100644 launcher/SkinUtils.h delete mode 100644 launcher/minecraft/services/CapeChange.h delete mode 100644 launcher/minecraft/services/SkinDelete.h delete mode 100644 launcher/minecraft/services/SkinUpload.h rename launcher/minecraft/{services => skins}/CapeChange.cpp (90%) create mode 100644 launcher/minecraft/skins/CapeChange.h rename launcher/minecraft/{services => skins}/SkinDelete.cpp (96%) create mode 100644 launcher/minecraft/skins/SkinDelete.h create mode 100644 launcher/minecraft/skins/SkinList.cpp create mode 100644 launcher/minecraft/skins/SkinList.h create mode 100644 launcher/minecraft/skins/SkinModel.cpp create mode 100644 launcher/minecraft/skins/SkinModel.h rename launcher/minecraft/{services => skins}/SkinUpload.cpp (79%) create mode 100644 launcher/minecraft/skins/SkinUpload.h delete mode 100644 launcher/ui/dialogs/SkinUploadDialog.cpp delete mode 100644 launcher/ui/dialogs/SkinUploadDialog.h delete mode 100644 launcher/ui/dialogs/SkinUploadDialog.ui create mode 100644 launcher/ui/dialogs/skins/SkinManageDialog.cpp create mode 100644 launcher/ui/dialogs/skins/SkinManageDialog.h create mode 100644 launcher/ui/dialogs/skins/SkinManageDialog.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 3b6218b7f..6914b3385 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -377,13 +377,17 @@ set(MINECRAFT_SOURCES minecraft/AssetsUtils.h minecraft/AssetsUtils.cpp - # Minecraft services - minecraft/services/CapeChange.cpp - minecraft/services/CapeChange.h - minecraft/services/SkinUpload.cpp - minecraft/services/SkinUpload.h - minecraft/services/SkinDelete.cpp - minecraft/services/SkinDelete.h + # Minecraft skins + minecraft/skins/CapeChange.cpp + minecraft/skins/CapeChange.h + minecraft/skins/SkinUpload.cpp + minecraft/skins/SkinUpload.h + minecraft/skins/SkinDelete.cpp + minecraft/skins/SkinDelete.h + minecraft/skins/SkinModel.cpp + minecraft/skins/SkinModel.h + minecraft/skins/SkinList.cpp + minecraft/skins/SkinList.h minecraft/Agent.h) @@ -742,8 +746,6 @@ SET(LAUNCHER_SOURCES ui/InstanceWindow.cpp # FIXME: maybe find a better home for this. - SkinUtils.cpp - SkinUtils.h FileIgnoreProxy.cpp FileIgnoreProxy.h FastFileIconProvider.cpp @@ -965,8 +967,6 @@ SET(LAUNCHER_SOURCES ui/dialogs/ReviewMessageBox.h ui/dialogs/VersionSelectDialog.cpp ui/dialogs/VersionSelectDialog.h - ui/dialogs/SkinUploadDialog.cpp - ui/dialogs/SkinUploadDialog.h ui/dialogs/ResourceDownloadDialog.cpp ui/dialogs/ResourceDownloadDialog.h ui/dialogs/ScrollMessageBox.cpp @@ -980,6 +980,9 @@ SET(LAUNCHER_SOURCES ui/dialogs/InstallLoaderDialog.cpp ui/dialogs/InstallLoaderDialog.h + ui/dialogs/skins/SkinManageDialog.cpp + ui/dialogs/skins/SkinManageDialog.h + # GUI - widgets ui/widgets/Common.cpp ui/widgets/Common.h @@ -1096,7 +1099,6 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/NewComponentDialog.ui ui/dialogs/NewsDialog.ui ui/dialogs/ProfileSelectDialog.ui - ui/dialogs/SkinUploadDialog.ui ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportPackDialog.ui ui/dialogs/ExportToModListDialog.ui @@ -1111,6 +1113,8 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ScrollMessageBox.ui ui/dialogs/BlockedModsDialog.ui ui/dialogs/ChooseProviderDialog.ui + + ui/dialogs/skins/SkinManageDialog.ui ) qt_add_resources(LAUNCHER_RESOURCES diff --git a/launcher/SkinUtils.cpp b/launcher/SkinUtils.cpp deleted file mode 100644 index 989114ad5..000000000 --- a/launcher/SkinUtils.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SkinUtils.h" -#include "Application.h" -#include "net/HttpMetaCache.h" - -#include -#include -#include -#include -#include - -namespace SkinUtils { -/* - * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise - */ -QPixmap getFaceFromCache(QString username, int height, int width) -{ - QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath()); - - if (fskin.exists()) { - QPixmap skinTexture(fskin.fileName()); - if (!skinTexture.isNull()) { - QPixmap skin = QPixmap(8, 8); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - skin.fill(QColorConstants::Transparent); -#else - skin.fill(QColor(0, 0, 0, 0)); -#endif - QPainter painter(&skin); - painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); - painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); - return skin.scaled(height, width, Qt::KeepAspectRatio); - } - } - - return QPixmap(); -} -} // namespace SkinUtils diff --git a/launcher/SkinUtils.h b/launcher/SkinUtils.h deleted file mode 100644 index 11bc8bc6f..000000000 --- a/launcher/SkinUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace SkinUtils { -QPixmap getFaceFromCache(QString id, int height = 64, int width = 64); -} diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h deleted file mode 100644 index 74805ef43..000000000 --- a/launcher/minecraft/services/CapeChange.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "net/NetRequest.h" - -class CapeChange : public Net::NetRequest { - Q_OBJECT - public: - using Ptr = shared_qobject_ptr; - CapeChange(QString token, QString capeId); - virtual ~CapeChange() = default; - - static CapeChange::Ptr make(QString token, QString capeId); - void init() override; - - protected: - virtual QNetworkReply* getReply(QNetworkRequest&) override; - - private: - QString m_capeId; - QString m_token; -}; diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h deleted file mode 100644 index b0fb866cd..000000000 --- a/launcher/minecraft/services/SkinDelete.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "net/NetRequest.h" - -class SkinDelete : public Net::NetRequest { - Q_OBJECT - public: - using Ptr = shared_qobject_ptr; - SkinDelete(QString token); - virtual ~SkinDelete() = default; - - static SkinDelete::Ptr make(QString token); - void init() override; - - protected: - virtual QNetworkReply* getReply(QNetworkRequest&) override; - - private: - QString m_token; -}; diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h deleted file mode 100644 index 2da836d52..000000000 --- a/launcher/minecraft/services/SkinUpload.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "net/NetRequest.h" - -class SkinUpload : public Net::NetRequest { - Q_OBJECT - public: - using Ptr = shared_qobject_ptr; - enum Model { STEVE, ALEX }; - - // Note this class takes ownership of the file. - SkinUpload(QString token, QByteArray skin, Model model = STEVE); - virtual ~SkinUpload() = default; - - static SkinUpload::Ptr make(QString token, QByteArray skin, Model model = STEVE); - void init() override; - - protected: - virtual QNetworkReply* getReply(QNetworkRequest&) override; - - private: - Model m_model; - QByteArray m_skin; - QString m_token; -}; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/skins/CapeChange.cpp similarity index 90% rename from launcher/minecraft/services/CapeChange.cpp rename to launcher/minecraft/skins/CapeChange.cpp index 5a7820b54..863e89844 100644 --- a/launcher/minecraft/services/CapeChange.cpp +++ b/launcher/minecraft/skins/CapeChange.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 @@ -42,7 +43,7 @@ CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token) { - logCat = taskMCServicesLogC; + logCat = taskMCSkinsLogC; }; QNetworkReply* CapeChange::getReply(QNetworkRequest& request) @@ -52,7 +53,7 @@ QNetworkReply* CapeChange::getReply(QNetworkRequest& request) return m_network->deleteResource(request); } else { setStatus(tr("Equipping cape")); - return m_network->post(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); + return m_network->put(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); } } @@ -67,6 +68,7 @@ CapeChange::Ptr CapeChange::make(QString token, QString capeId) { auto up = makeShared(token, capeId); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"); + up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); return up; } diff --git a/launcher/minecraft/skins/CapeChange.h b/launcher/minecraft/skins/CapeChange.h new file mode 100644 index 000000000..bcafcde87 --- /dev/null +++ b/launcher/minecraft/skins/CapeChange.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 "net/NetRequest.h" + +class CapeChange : public Net::NetRequest { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + CapeChange(QString token, QString capeId); + virtual ~CapeChange() = default; + + static CapeChange::Ptr make(QString token, QString capeId); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + + private: + QString m_capeId; + QString m_token; +}; diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/skins/SkinDelete.cpp similarity index 96% rename from launcher/minecraft/services/SkinDelete.cpp rename to launcher/minecraft/skins/SkinDelete.cpp index 7944637f6..982cac1b7 100644 --- a/launcher/minecraft/services/SkinDelete.cpp +++ b/launcher/minecraft/skins/SkinDelete.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 @@ -40,7 +41,7 @@ SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token) { - logCat = taskMCServicesLogC; + logCat = taskMCSkinsLogC; }; QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) diff --git a/launcher/minecraft/skins/SkinDelete.h b/launcher/minecraft/skins/SkinDelete.h new file mode 100644 index 000000000..5d02e0cc4 --- /dev/null +++ b/launcher/minecraft/skins/SkinDelete.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 "net/NetRequest.h" + +class SkinDelete : public Net::NetRequest { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + SkinDelete(QString token); + virtual ~SkinDelete() = default; + + static SkinDelete::Ptr make(QString token); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + + private: + QString m_token; +}; diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp new file mode 100644 index 000000000..be329564b --- /dev/null +++ b/launcher/minecraft/skins/SkinList.cpp @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 "SkinList.h" + +#include +#include + +#include "FileSystem.h" +#include "Json.h" +#include "minecraft/skins/SkinModel.h" + +SkinList::SkinList(QObject* parent, QString path, MinecraftAccountPtr acct) : QAbstractListModel(parent), m_acct(acct) +{ + FS::ensureFolderPathExists(m_dir.absolutePath()); + m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); + m_watcher.reset(new QFileSystemWatcher(this)); + is_watching = false; + connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &SkinList::directoryChanged); + connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &SkinList::fileChanged); + directoryChanged(path); +} + +void SkinList::startWatching() +{ + if (is_watching) { + return; + } + update(); + is_watching = m_watcher->addPath(m_dir.absolutePath()); + if (is_watching) { + qDebug() << "Started watching " << m_dir.absolutePath(); + } else { + qDebug() << "Failed to start watching " << m_dir.absolutePath(); + } +} + +void SkinList::stopWatching() +{ + save(); + if (!is_watching) { + return; + } + is_watching = !m_watcher->removePath(m_dir.absolutePath()); + if (!is_watching) { + qDebug() << "Stopped watching " << m_dir.absolutePath(); + } else { + qDebug() << "Failed to stop watching " << m_dir.absolutePath(); + } +} + +bool SkinList::update() +{ + QVector newSkins; + m_dir.refresh(); + + auto manifestInfo = QFileInfo(m_dir.absoluteFilePath("index.json")); + if (manifestInfo.exists()) { + try { + auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); + const auto root = doc.object(); + auto skins = Json::ensureArray(root, "skins"); + for (auto jSkin : skins) { + SkinModel s(m_dir, Json::ensureObject(jSkin)); + if (s.isValid()) { + newSkins << s; + } + } + } catch (const Exception& e) { + qCritical() << "Couldn't load skins json:" << e.cause(); + } + } else { + newSkins = loadMinecraftSkins(); + } + + bool needsSave = false; + const auto& skin = m_acct->accountData()->minecraftProfile.skin; + if (!skin.url.isEmpty() && !skin.data.isEmpty()) { + QPixmap skinTexture; + SkinModel* nskin = nullptr; + for (auto i = 0; i < newSkins.size(); i++) { + if (newSkins[i].getURL() == skin.url) { + nskin = &newSkins[i]; + break; + } + } + if (!nskin) { + auto name = m_acct->profileName() + ".png"; + if (QFileInfo(m_dir.absoluteFilePath(name)).exists()) { + name = QUrl(skin.url).fileName() + ".png"; + } + auto path = m_dir.absoluteFilePath(name); + if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { + SkinModel s(path); + s.setModel(SkinModel::CLASSIC); // maybe better model detection + s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); + s.setURL(skin.url); + newSkins << s; + needsSave = true; + } + } else { + nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); + } + } + + auto folderContents = m_dir.entryInfoList(); + // if there are any untracked files... + for (QFileInfo entry : folderContents) { + if (!entry.isFile() && entry.suffix() != "png") + continue; + + SkinModel w(entry.absoluteFilePath()); + if (w.isValid()) { + auto add = true; + for (auto s : newSkins) { + if (s.name() == w.name()) { + add = false; + break; + } + } + if (add) { + newSkins.append(w); + needsSave = true; + } + } + } + std::sort(newSkins.begin(), newSkins.end(), + [](const SkinModel& a, const SkinModel& b) { return a.getPath().localeAwareCompare(b.getPath()) < 0; }); + beginResetModel(); + m_skin_list.swap(newSkins); + endResetModel(); + if (needsSave) + save(); + return true; +} + +void SkinList::directoryChanged(const QString& path) +{ + QDir new_dir(path); + if (!new_dir.exists()) + if (!FS::ensureFolderPathExists(new_dir.absolutePath())) + return; + if (m_dir.absolutePath() != new_dir.absolutePath()) { + m_dir.setPath(path); + m_dir.refresh(); + if (is_watching) + stopWatching(); + startWatching(); + } + update(); +} + +void SkinList::fileChanged(const QString& path) +{ + qDebug() << "Checking " << path; + QFileInfo checkfile(path); + if (!checkfile.exists()) + return; + + for (int i = 0; i < m_skin_list.count(); i++) { + if (m_skin_list[i].getPath() == checkfile.absoluteFilePath()) { + m_skin_list[i].refresh(); + dataChanged(index(i), index(i)); + break; + } + } +} + +QStringList SkinList::mimeTypes() const +{ + return { "text/uri-list" }; +} + +Qt::DropActions SkinList::supportedDropActions() const +{ + return Qt::CopyAction; +} + +bool SkinList::dropMimeData(const QMimeData* data, + Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + [[maybe_unused]] const QModelIndex& parent) +{ + if (action == Qt::IgnoreAction) + return true; + // check if the action is supported + if (!data || !(action & supportedDropActions())) + return false; + + // files dropped from outside? + if (data->hasUrls()) { + auto urls = data->urls(); + QStringList iconFiles; + for (auto url : urls) { + // only local files may be dropped... + if (!url.isLocalFile()) + continue; + iconFiles += url.toLocalFile(); + } + installSkins(iconFiles); + return true; + } + return false; +} + +Qt::ItemFlags SkinList::flags(const QModelIndex& index) const +{ + Qt::ItemFlags f = Qt::ItemIsDropEnabled | QAbstractListModel::flags(index); + if (index.isValid()) { + f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + } + return f; +} + +QVariant SkinList::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + + if (row < 0 || row >= m_skin_list.size()) + return QVariant(); + auto skin = m_skin_list[row]; + switch (role) { + case Qt::DecorationRole: + return skin.getTexture(); + case Qt::DisplayRole: + return skin.name(); + case Qt::UserRole: + return skin.name(); + case Qt::EditRole: + return skin.name(); + default: + return QVariant(); + } +} + +int SkinList::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() ? 0 : m_skin_list.size(); +} + +void SkinList::installSkins(const QStringList& iconFiles) +{ + for (QString file : iconFiles) + installSkin(file, {}); +} + +void SkinList::installSkin(const QString& file, const QString& name) +{ + QFileInfo fileinfo(file); + if (!fileinfo.isReadable() || !fileinfo.isFile()) + return; + + if (fileinfo.suffix() != "png" && !SkinModel(fileinfo.absoluteFilePath()).isValid()) + return; + + QString target = FS::PathCombine(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name); + QFile::copy(file, target); +} + +int SkinList::getSkinIndex(const QString& key) const +{ + for (int i = 0; i < m_skin_list.count(); i++) { + if (m_skin_list[i].name() == key) { + return i; + } + } + return -1; +} + +const SkinModel* SkinList::skin(const QString& key) const +{ + int idx = getSkinIndex(key); + if (idx == -1) + return nullptr; + return &m_skin_list[idx]; +} + +SkinModel* SkinList::skin(const QString& key) +{ + int idx = getSkinIndex(key); + if (idx == -1) + return nullptr; + return &m_skin_list[idx]; +} + +bool SkinList::deleteSkin(const QString& key, const bool trash) +{ + int idx = getSkinIndex(key); + if (idx != -1) { + auto s = m_skin_list[idx]; + if (trash) { + if (FS::trash(s.getPath(), nullptr)) { + m_skin_list.remove(idx); + return true; + } + } else if (QFile::remove(s.getPath())) { + m_skin_list.remove(idx); + return true; + } + } + return false; +} + +void SkinList::save() +{ + QJsonObject doc; + QJsonArray arr; + for (auto s : m_skin_list) { + arr << s.toJSON(); + } + doc["skins"] = arr; + Json::write(doc, m_dir.absoluteFilePath("index.json")); +} + +int SkinList::getSelectedAccountSkin() +{ + const auto& skin = m_acct->accountData()->minecraftProfile.skin; + for (int i = 0; i < m_skin_list.count(); i++) { + if (m_skin_list[i].getURL() == skin.url) { + return i; + } + } + return -1; +} + +bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) +{ + if (!idx.isValid() || role != Qt::EditRole) { + return false; + } + + int row = idx.row(); + if (row < 0 || row >= m_skin_list.size()) + return false; + auto skin = m_skin_list[row]; + auto newName = value.toString(); + if (skin.name() != newName) { + skin.rename(newName); + save(); + } + return true; +} + +QVector SkinList::loadMinecraftSkins() +{ + QString partialPath; +#if defined(Q_OS_OSX) + partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); +#elif defined(Q_OS_WIN32) + partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); +#else + partialPath = QDir::homePath(); +#endif + QVector newSkins; + auto path = FS::PathCombine(partialPath, ".minecraft", "launcher_custom_skins.json"); + auto manifestInfo = QFileInfo(path); + if (!manifestInfo.exists()) + return {}; + try { + auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); + const auto root = doc.object(); + auto skins = Json::ensureObject(root, "customSkins"); + for (auto key : skins.keys()) { + SkinModel s(m_dir, Json::ensureObject(skins, key)); + if (s.isValid()) { + newSkins << s; + } + } + } catch (const Exception& e) { + qCritical() << "Couldn't load minecraft skins json:" << e.cause(); + } + return newSkins; +} diff --git a/launcher/minecraft/skins/SkinList.h b/launcher/minecraft/skins/SkinList.h new file mode 100644 index 000000000..8d8266d79 --- /dev/null +++ b/launcher/minecraft/skins/SkinList.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 +#include + +#include "QObjectPtr.h" +#include "SkinModel.h" +#include "minecraft/auth/MinecraftAccount.h" + +class SkinList : public QAbstractListModel { + Q_OBJECT + public: + explicit SkinList(QObject* parent, QString path, MinecraftAccountPtr acct); + virtual ~SkinList() { save(); }; + + int getSkinIndex(const QString& key) const; + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex& idx, const QVariant& value, int role) override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + virtual QStringList mimeTypes() const override; + virtual Qt::DropActions supportedDropActions() const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + + bool deleteSkin(const QString& key, const bool trash); + + void installSkins(const QStringList& iconFiles); + void installSkin(const QString& file, const QString& name); + + const SkinModel* skin(const QString& key) const; + SkinModel* skin(const QString& key); + + void startWatching(); + void stopWatching(); + + QString getDir() const { return m_dir.absolutePath(); } + void save(); + int getSelectedAccountSkin(); + + private: + // hide copy constructor + SkinList(const SkinList&) = delete; + // hide assign op + SkinList& operator=(const SkinList&) = delete; + + QVector loadMinecraftSkins(); + + protected slots: + void directoryChanged(const QString& path); + void fileChanged(const QString& path); + bool update(); + + private: + shared_qobject_ptr m_watcher; + bool is_watching; + QVector m_skin_list; + QDir m_dir; + MinecraftAccountPtr m_acct; +}; \ No newline at end of file diff --git a/launcher/minecraft/skins/SkinModel.cpp b/launcher/minecraft/skins/SkinModel.cpp new file mode 100644 index 000000000..3b467019c --- /dev/null +++ b/launcher/minecraft/skins/SkinModel.cpp @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 "SkinModel.h" +#include +#include +#include +#include + +#include "FileSystem.h" +#include "Json.h" + +SkinModel::SkinModel(QString path) : m_path(path), m_texture(path), m_model(Model::CLASSIC) {} + +SkinModel::SkinModel(QDir skinDir, QJsonObject obj) + : m_cape_id(Json::ensureString(obj, "capeId")), m_model(Model::CLASSIC), m_url(Json::ensureString(obj, "url")) +{ + auto name = Json::ensureString(obj, "name"); + auto skinImage = Json::ensureString(obj, "skinImage"); + if (!skinImage.isEmpty()) { // minecraft skin model + skinImage = skinImage.mid(22); + m_texture.loadFromData(QByteArray::fromBase64(skinImage.toUtf8()), "PNG"); + auto textureId = Json::ensureString(obj, "textureId"); + if (name.isEmpty()) { + name = textureId; + } + if (Json::ensureBoolean(obj, "slim", false)) { + m_model = Model::SLIM; + } + } else { + if (auto model = Json::ensureString(obj, "model"); model == "SLIM") { + m_model = Model::SLIM; + } + } + m_path = skinDir.absoluteFilePath(name) + ".png"; + if (!QFileInfo(m_path).exists() && isValid()) { + m_texture.save(m_path, "PNG"); + } else { + m_texture = QPixmap(m_path); + } +} + +QString SkinModel::name() const +{ + return QFileInfo(m_path).baseName(); +} + +bool SkinModel::rename(QString newName) +{ + auto info = QFileInfo(m_path); + m_path = FS::PathCombine(info.absolutePath(), newName + ".png"); + return FS::move(info.absoluteFilePath(), m_path); +} + +QJsonObject SkinModel::toJSON() const +{ + QJsonObject obj; + obj["name"] = name(); + obj["capeId"] = m_cape_id; + obj["url"] = m_url; + obj["model"] = getModelString(); + return obj; +} + +QString SkinModel::getModelString() const +{ + switch (m_model) { + case CLASSIC: + return "CLASSIC"; + case SLIM: + return "SLIM"; + } + return {}; +} + +bool SkinModel::isValid() const +{ + return !m_texture.isNull() && (m_texture.size().height() == 32 || m_texture.size().height() == 64) && m_texture.size().width() == 64; +} + +QPixmap SkinModel::renderFrontBody() const +{ + auto isSlim = m_model == SLIM; + auto slimOffset = isSlim ? 1 : 0; + auto isOldSkin = m_texture.height() < 64; + + auto head = m_texture.copy(QRect(8, 8, 16, 16)); + auto torso = m_texture.copy(QRect(20, 20, 28, 32)); + auto rightArm = m_texture.copy(QRect(44, 20, 48 - slimOffset, 32)); + auto rightLeg = m_texture.copy(QRect(4, 20, 8, 32)); + QPixmap leftArm, leftLeg; + + if (isOldSkin) { + leftArm = rightArm.transformed(QTransform().scale(-1, 1)); + leftLeg = rightLeg.transformed(QTransform().scale(-1, 1)); + } else { + leftArm = m_texture.copy(QRect(36, 52, 40 - slimOffset, 64)); + leftLeg = m_texture.copy(QRect(20, 52, 24, 64)); + } + QPixmap output(16, 32); + output.fill(Qt::black); + QPainter p; + if (!p.begin(&output)) + return {}; + p.drawPixmap(QPoint(4, 0), head); + p.drawPixmap(QPoint(4, 8), torso); + p.drawPixmap(QPoint(12, 8), leftArm); + p.drawPixmap(QPoint(slimOffset, 8), rightArm); + p.drawPixmap(QPoint(8, 20), leftLeg); + p.drawPixmap(QPoint(4, 20), leftArm); + + return output.scaled(128, 128, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation); +} \ No newline at end of file diff --git a/launcher/minecraft/skins/SkinModel.h b/launcher/minecraft/skins/SkinModel.h new file mode 100644 index 000000000..6d135c7f7 --- /dev/null +++ b/launcher/minecraft/skins/SkinModel.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 +#include + +class SkinModel { + public: + enum Model { CLASSIC, SLIM }; + + SkinModel(QString path); + SkinModel(QDir skinDir, QJsonObject obj); + virtual ~SkinModel() = default; + + QString name() const; + QString getModelString() const; + bool isValid() const; + QString getPath() const { return m_path; } + QPixmap getTexture() const { return m_texture; } + QString getCapeId() const { return m_cape_id; } + Model getModel() const { return m_model; } + QString getURL() const { return m_url; } + + bool rename(QString newName); + void setCapeId(QString capeID) { m_cape_id = capeID; } + void setModel(Model model) { m_model = model; } + void setURL(QString url) { m_url = url; } + void refresh() { m_texture = QPixmap(m_path); } + + QJsonObject toJSON() const; + + QPixmap renderFrontBody() const; + + private: + QString m_path; + QPixmap m_texture; + QString m_cape_id; + Model m_model; + QString m_url; +}; \ No newline at end of file diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp similarity index 79% rename from launcher/minecraft/services/SkinUpload.cpp rename to launcher/minecraft/skins/SkinUpload.cpp index 0400fa0f4..4e56bd7e6 100644 --- a/launcher/minecraft/services/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 @@ -37,12 +38,13 @@ #include +#include "FileSystem.h" #include "net/ByteArraySink.h" #include "net/StaticHeaderProxy.h" -SkinUpload::SkinUpload(QString token, QByteArray skin, SkinUpload::Model model) : NetRequest(), m_model(model), m_skin(skin), m_token(token) +SkinUpload::SkinUpload(QString token, SkinModel* skin) : NetRequest(), m_skin(skin), m_token(token) { - logCat = taskMCServicesLogC; + logCat = taskUploadLogC; }; QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) @@ -52,23 +54,12 @@ QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) QHttpPart skin; skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\"")); - skin.setBody(m_skin); + + skin.setBody(FS::read(m_skin->getPath())); QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - - switch (m_model) { - default: - qDebug() << "Unknown skin type!"; - emitFailed("Unknown skin type!"); - return nullptr; - case SkinUpload::STEVE: - model.setBody("CLASSIC"); - break; - case SkinUpload::ALEX: - model.setBody("SLIM"); - break; - } + model.setBody(m_skin->getModelString().toUtf8()); multiPart->append(skin); multiPart->append(model); @@ -83,10 +74,11 @@ void SkinUpload::init() })); } -SkinUpload::Ptr SkinUpload::make(QString token, QByteArray skin, SkinUpload::Model model) +SkinUpload::Ptr SkinUpload::make(QString token, SkinModel* skin) { - auto up = makeShared(token, skin, model); + auto up = makeShared(token, skin); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); + up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); return up; } diff --git a/launcher/minecraft/skins/SkinUpload.h b/launcher/minecraft/skins/SkinUpload.h new file mode 100644 index 000000000..d070f301d --- /dev/null +++ b/launcher/minecraft/skins/SkinUpload.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 "minecraft/skins/SkinModel.h" +#include "net/NetRequest.h" + +class SkinUpload : public Net::NetRequest { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + + // Note this class takes ownership of the file. + SkinUpload(QString token, SkinModel* skin); + virtual ~SkinUpload() = default; + + static SkinUpload::Ptr make(QString token, SkinModel* skin); + void init() override; + + protected: + virtual QNetworkReply* getReply(QNetworkRequest&) override; + + private: + SkinModel* m_skin; + QString m_token; +}; diff --git a/launcher/net/Logging.cpp b/launcher/net/Logging.cpp index 45d2dcc20..cd0c88d3c 100644 --- a/launcher/net/Logging.cpp +++ b/launcher/net/Logging.cpp @@ -22,6 +22,6 @@ Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net") Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download") Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload") -Q_LOGGING_CATEGORY(taskMCServicesLogC, "launcher.task.minecraft.servicies") +Q_LOGGING_CATEGORY(taskMCSkinsLogC, "launcher.task.minecraft.skins") Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache") Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http") diff --git a/launcher/net/Logging.h b/launcher/net/Logging.h index d3a11cdce..2536f31aa 100644 --- a/launcher/net/Logging.h +++ b/launcher/net/Logging.h @@ -24,6 +24,6 @@ Q_DECLARE_LOGGING_CATEGORY(taskNetLogC) Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC) Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC) -Q_DECLARE_LOGGING_CATEGORY(taskMCServicesLogC) +Q_DECLARE_LOGGING_CATEGORY(taskMCSkinsLogC) Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC) Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC) diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index cc63f4497..26791bc0d 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -52,8 +52,8 @@ class NetJob : public ConcurrentTask { public: using Ptr = shared_qobject_ptr; - explicit NetJob(QString job_name, shared_qobject_ptr network) - : ConcurrentTask(nullptr, job_name), m_network(network) + explicit NetJob(QString job_name, shared_qobject_ptr network, int max_concurrent = 6) + : ConcurrentTask(nullptr, job_name, max_concurrent), m_network(network) {} ~NetJob() override = default; diff --git a/launcher/net/NetRequest.cpp b/launcher/net/NetRequest.cpp index eef550e15..853873528 100644 --- a/launcher/net/NetRequest.cpp +++ b/launcher/net/NetRequest.cpp @@ -5,6 +5,7 @@ * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (c) 2023 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 diff --git a/launcher/net/NetRequest.h b/launcher/net/NetRequest.h index ee47ab2a6..917495ed9 100644 --- a/launcher/net/NetRequest.h +++ b/launcher/net/NetRequest.h @@ -4,6 +4,7 @@ * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (c) 2023 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 diff --git a/launcher/net/StaticHeaderProxy.h b/launcher/net/StaticHeaderProxy.h index 0e62d80ff..aabbc9c92 100644 --- a/launcher/net/StaticHeaderProxy.h +++ b/launcher/net/StaticHeaderProxy.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher - * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> + * Copyright (c) 2023 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 diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index a82932e08..4858e7d46 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -77,7 +77,6 @@ #include #include #include -#include #include #include #include diff --git a/launcher/ui/dialogs/ProfileSelectDialog.cpp b/launcher/ui/dialogs/ProfileSelectDialog.cpp index a62238bdb..fe03e1b6b 100644 --- a/launcher/ui/dialogs/ProfileSelectDialog.cpp +++ b/launcher/ui/dialogs/ProfileSelectDialog.cpp @@ -20,7 +20,6 @@ #include #include "Application.h" -#include "SkinUtils.h" #include "ui/dialogs/ProgressDialog.h" diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp deleted file mode 100644 index 70f1e6760..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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 . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "CustomMessageBox.h" -#include "ProgressDialog.h" -#include "SkinUploadDialog.h" -#include "ui_SkinUploadDialog.h" - -void SkinUploadDialog::on_buttonBox_rejected() -{ - close(); -} - -void SkinUploadDialog::on_buttonBox_accepted() -{ - QString fileName; - QString input = ui->skinPathTextBox->text(); - ProgressDialog prog(this); - SequentialTask skinUpload; - - if (!input.isEmpty()) { - QRegularExpression urlPrefixMatcher(QRegularExpression::anchoredPattern("^([a-z]+)://.+$")); - bool isLocalFile = false; - // it has an URL prefix -> it is an URL - if (urlPrefixMatcher.match(input).hasMatch()) { - QUrl fileURL = input; - if (fileURL.isValid()) { - // local? - if (fileURL.isLocalFile()) { - isLocalFile = true; - fileName = fileURL.toLocalFile(); - } else { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Using remote URLs for setting skins is not implemented yet."), - QMessageBox::Warning) - ->exec(); - close(); - return; - } - } else { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("You cannot use an invalid URL for uploading skins."), - QMessageBox::Warning) - ->exec(); - close(); - return; - } - } else { - // just assume it's a path then - isLocalFile = true; - fileName = ui->skinPathTextBox->text(); - } - if (isLocalFile && !QFile::exists(fileName)) { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); - close(); - return; - } - SkinUpload::Model model = SkinUpload::STEVE; - if (ui->steveBtn->isChecked()) { - model = SkinUpload::STEVE; - } else if (ui->alexBtn->isChecked()) { - model = SkinUpload::ALEX; - } - skinUpload.addTask(SkinUpload::make(m_acct->accessToken(), FS::read(fileName), model)); - } - - auto selectedCape = ui->capeCombo->currentData().toString(); - if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(CapeChange::make(m_acct->accessToken(), selectedCape)); - } - if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); - close(); - return; - } - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Success"), QMessageBox::Information)->exec(); - close(); -} - -void SkinUploadDialog::on_skinBrowseBtn_clicked() -{ - auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); - QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); - if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { - return; - } - QString cooked_path = FS::NormalizePath(raw_path); - ui->skinPathTextBox->setText(cooked_path); -} - -SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent) : QDialog(parent), m_acct(acct), ui(new Ui::SkinUploadDialog) -{ - ui->setupUi(this); - - // FIXME: add a model for this, download/refresh the capes on demand - auto& accountData = *acct->accountData(); - int index = 0; - ui->capeCombo->addItem(tr("No Cape"), QVariant()); - auto currentCape = accountData.minecraftProfile.currentCape; - if (currentCape.isEmpty()) { - ui->capeCombo->setCurrentIndex(index); - } - - for (auto& cape : accountData.minecraftProfile.capes) { - index++; - if (cape.data.size()) { - QPixmap capeImage; - if (capeImage.loadFromData(cape.data, "PNG")) { - QPixmap preview = QPixmap(10, 16); - QPainter painter(&preview); - painter.drawPixmap(0, 0, capeImage.copy(1, 1, 10, 16)); - ui->capeCombo->addItem(capeImage, cape.alias, cape.id); - if (currentCape == cape.id) { - ui->capeCombo->setCurrentIndex(index); - } - continue; - } - } - ui->capeCombo->addItem(cape.alias, cape.id); - if (currentCape == cape.id) { - ui->capeCombo->setCurrentIndex(index); - } - } -} diff --git a/launcher/ui/dialogs/SkinUploadDialog.h b/launcher/ui/dialogs/SkinUploadDialog.h deleted file mode 100644 index 81d6140cc..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -namespace Ui { -class SkinUploadDialog; -} - -class SkinUploadDialog : public QDialog { - Q_OBJECT - public: - explicit SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent = 0); - virtual ~SkinUploadDialog(){}; - - public slots: - void on_buttonBox_accepted(); - - void on_buttonBox_rejected(); - - void on_skinBrowseBtn_clicked(); - - protected: - MinecraftAccountPtr m_acct; - - private: - Ui::SkinUploadDialog* ui; -}; diff --git a/launcher/ui/dialogs/SkinUploadDialog.ui b/launcher/ui/dialogs/SkinUploadDialog.ui deleted file mode 100644 index c6df92df3..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.ui +++ /dev/null @@ -1,95 +0,0 @@ - - - SkinUploadDialog - - - - 0 - 0 - 394 - 360 - - - - Skin Upload - - - - - - Skin File - - - - - - Leave empty to keep current skin - - - - - - - - 0 - 0 - - - - Browse - - - - - - - - - - Player Model - - - - - - Steve Model - - - true - - - - - - - Alex Model - - - - - - - - - - Cape - - - - - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp new file mode 100644 index 000000000..1ba7e7055 --- /dev/null +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Application.h" +#include "DesktopServices.h" +#include "QObjectPtr.h" +#include "SkinManageDialog.h" + +#include "minecraft/auth/AccountTask.h" +#include "minecraft/skins/CapeChange.h" +#include "minecraft/skins/SkinDelete.h" +#include "minecraft/skins/SkinList.h" +#include "minecraft/skins/SkinModel.h" +#include "minecraft/skins/SkinUpload.h" + +#include "net/NetJob.h" +#include "tasks/Task.h" + +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h" +#include "ui/instanceview/InstanceDelegate.h" +#include "ui_SkinManageDialog.h" + +SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct) + : QDialog(parent), m_acct(acct), ui(new Ui::SkinManageDialog), m_list(this, APPLICATION->settings()->get("SkinsDir").toString(), acct) +{ + ui->setupUi(this); + + setWindowModality(Qt::WindowModal); + + auto contentsWidget = ui->listView; + contentsWidget->setViewMode(QListView::IconMode); + contentsWidget->setFlow(QListView::LeftToRight); + contentsWidget->setIconSize(QSize(48, 48)); + contentsWidget->setMovement(QListView::Static); + contentsWidget->setResizeMode(QListView::Adjust); + contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection); + contentsWidget->setSpacing(5); + contentsWidget->setWordWrap(false); + contentsWidget->setWrapping(true); + contentsWidget->setUniformItemSizes(true); + contentsWidget->setTextElideMode(Qt::ElideRight); + contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + contentsWidget->installEventFilter(this); + contentsWidget->setItemDelegate(new ListViewDelegate()); + + contentsWidget->setAcceptDrops(true); + contentsWidget->setDropIndicatorShown(true); + contentsWidget->viewport()->setAcceptDrops(true); + contentsWidget->setDragDropMode(QAbstractItemView::DropOnly); + contentsWidget->setDefaultDropAction(Qt::CopyAction); + + contentsWidget->installEventFilter(this); + contentsWidget->setModel(&m_list); + + connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex))); + + connect(contentsWidget->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + SLOT(selectionChanged(QItemSelection, QItemSelection))); + connect(ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu); + + setupCapes(); + + ui->listView->setCurrentIndex(m_list.index(m_list.getSelectedAccountSkin())); +} + +SkinManageDialog::~SkinManageDialog() +{ + delete ui; +} + +void SkinManageDialog::activated(QModelIndex index) +{ + m_selected_skin = index.data(Qt::UserRole).toString(); + accept(); +} + +void SkinManageDialog::selectionChanged(QItemSelection selected, QItemSelection deselected) +{ + if (selected.empty()) + return; + + QString key = selected.first().indexes().first().data(Qt::UserRole).toString(); + if (key.isEmpty()) + return; + m_selected_skin = key; + auto skin = m_list.skin(key); + if (!skin) + return; + ui->selectedModel->setPixmap(skin->getTexture().scaled(128, 128, Qt::KeepAspectRatio, Qt::FastTransformation)); + ui->capeCombo->setCurrentIndex(m_capes_idx.value(skin->getCapeId())); + ui->steveBtn->setChecked(skin->getModel() == SkinModel::CLASSIC); + ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM); +} + +void SkinManageDialog::delayed_scroll(QModelIndex model_index) +{ + auto contentsWidget = ui->listView; + contentsWidget->scrollTo(model_index); +} + +void SkinManageDialog::on_openDirBtn_clicked() +{ + DesktopServices::openDirectory(m_list.getDir(), true); +} + +void SkinManageDialog::on_addBtn_clicked() +{ + auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); + QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); + if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { + return; + } + if (!SkinModel(raw_path).isValid()) { + CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), + tr("Skin images must be 64x64 or 64x32 pixel PNG files."), QMessageBox::Critical) + ->show(); + return; + } + m_list.installSkin(raw_path, {}); +} + +QPixmap previewCape(QPixmap capeImage) +{ + QPixmap preview = QPixmap(10, 16); + QPainter painter(&preview); + painter.drawPixmap(0, 0, capeImage.copy(1, 1, 10, 16)); + return preview.scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation); +} + +void SkinManageDialog::setupCapes() +{ + // FIXME: add a model for this, download/refresh the capes on demand + auto& accountData = *m_acct->accountData(); + int index = 0; + ui->capeCombo->addItem(tr("No Cape"), QVariant()); + auto currentCape = accountData.minecraftProfile.currentCape; + if (currentCape.isEmpty()) { + ui->capeCombo->setCurrentIndex(index); + } + + auto capesDir = FS::PathCombine(m_list.getDir(), "capes"); + NetJob::Ptr job{ new NetJob(tr("Download capes"), APPLICATION->network()) }; + bool needsToDownload = false; + for (auto& cape : accountData.minecraftProfile.capes) { + auto path = FS::PathCombine(capesDir, cape.id + ".png"); + if (cape.data.size()) { + QPixmap capeImage; + if (capeImage.loadFromData(cape.data, "PNG") && capeImage.save(path)) { + m_capes[cape.id] = previewCape(capeImage); + continue; + } + } + if (QFileInfo(path).exists()) { + continue; + } + if (!cape.url.isEmpty()) { + needsToDownload = true; + job->addNetAction(Net::Download::makeFile(cape.url, path)); + } + } + if (needsToDownload) { + ProgressDialog dlg(this); + dlg.execWithTask(job.get()); + } + for (auto& cape : accountData.minecraftProfile.capes) { + index++; + QPixmap capeImage; + if (!m_capes.contains(cape.id)) { + auto path = FS::PathCombine(capesDir, cape.id + ".png"); + if (QFileInfo(path).exists() && capeImage.load(path)) { + capeImage = previewCape(capeImage); + m_capes[cape.id] = capeImage; + } + } + if (!capeImage.isNull()) { + ui->capeCombo->addItem(capeImage, cape.alias, cape.id); + } else { + ui->capeCombo->addItem(cape.alias, cape.id); + } + + m_capes_idx[cape.id] = index; + } +} + +void SkinManageDialog::on_capeCombo_currentIndexChanged(int index) +{ + auto id = ui->capeCombo->currentData(); + ui->capeImage->setPixmap(m_capes.value(id.toString(), {})); + if (auto skin = m_list.skin(m_selected_skin); skin) { + skin->setCapeId(id.toString()); + } +} + +void SkinManageDialog::on_steveBtn_toggled(bool checked) +{ + if (auto skin = m_list.skin(m_selected_skin); skin) { + skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM); + } +} + +void SkinManageDialog::accept() +{ + auto skin = m_list.skin(m_selected_skin); + if (!skin) + reject(); + auto path = skin->getPath(); + + ProgressDialog prog(this); + NetJob::Ptr skinUpload{ new NetJob(tr("Change skin"), APPLICATION->network(), 1) }; + + if (!QFile::exists(path)) { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); + reject(); + return; + } + + skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin)); + + auto selectedCape = skin->getCapeId(); + if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { + skinUpload->addNetAction(CapeChange::make(m_acct->accessToken(), selectedCape)); + } + + skinUpload->addTask(m_acct->refresh().staticCast()); + if (prog.execWithTask(skinUpload.get()) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); + reject(); + return; + } + skin->setURL(m_acct->accountData()->minecraftProfile.skin.url); + QDialog::accept(); +} + +void SkinManageDialog::on_resetBtn_clicked() +{ + ProgressDialog prog(this); + NetJob::Ptr skinReset{ new NetJob(tr("Reset skin"), APPLICATION->network(), 1) }; + skinReset->addNetAction(SkinDelete::make(m_acct->accessToken())); + skinReset->addTask(m_acct->refresh().staticCast()); + if (prog.execWithTask(skinReset.get()) != QDialog::Accepted) { + CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); + reject(); + return; + } + QDialog::accept(); +} + +void SkinManageDialog::show_context_menu(const QPoint& pos) +{ + QMenu myMenu(tr("Context menu"), this); + myMenu.addAction(ui->action_Rename_Skin); + myMenu.addAction(ui->action_Delete_Skin); + + myMenu.exec(ui->listView->mapToGlobal(pos)); +} + +bool SkinManageDialog::eventFilter(QObject* obj, QEvent* ev) +{ + if (obj == ui->listView) { + if (ev->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(ev); + switch (keyEvent->key()) { + case Qt::Key_Delete: + on_action_Delete_Skin_triggered(false); + return true; + case Qt::Key_F2: + on_action_Rename_Skin_triggered(false); + return true; + default: + break; + } + } + } + return QDialog::eventFilter(obj, ev); +} + +void SkinManageDialog::on_action_Rename_Skin_triggered(bool checked) +{ + if (!m_selected_skin.isEmpty()) { + ui->listView->edit(ui->listView->currentIndex()); + } +} + +void SkinManageDialog::on_action_Delete_Skin_triggered(bool checked) +{ + if (m_selected_skin.isEmpty()) + return; + + if (m_list.getSkinIndex(m_selected_skin) == m_list.getSelectedAccountSkin()) { + CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning); + return; + } + + auto skin = m_list.skin(m_selected_skin); + if (!skin) + return; + + auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"), + tr("You are about to delete \"%1\".\n" + "Are you sure?") + .arg(skin->name()), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response == QMessageBox::Yes) { + if (!m_list.deleteSkin(m_selected_skin, true)) { + m_list.deleteSkin(m_selected_skin, false); + } + } +} diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.h b/launcher/ui/dialogs/skins/SkinManageDialog.h new file mode 100644 index 000000000..8c55c3310 --- /dev/null +++ b/launcher/ui/dialogs/skins/SkinManageDialog.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 +#include + +#include "minecraft/auth/MinecraftAccount.h" +#include "minecraft/skins/SkinList.h" + +namespace Ui { +class SkinManageDialog; +} + +class SkinManageDialog : public QDialog { + Q_OBJECT + public: + explicit SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct); + virtual ~SkinManageDialog(); + + public slots: + void selectionChanged(QItemSelection, QItemSelection); + void activated(QModelIndex); + void delayed_scroll(QModelIndex); + void on_openDirBtn_clicked(); + void on_addBtn_clicked(); + void accept() override; + void on_capeCombo_currentIndexChanged(int index); + void on_steveBtn_toggled(bool checked); + void on_resetBtn_clicked(); + void show_context_menu(const QPoint& pos); + bool eventFilter(QObject* obj, QEvent* ev) override; + void on_action_Rename_Skin_triggered(bool checked); + void on_action_Delete_Skin_triggered(bool checked); + + private: + void setupCapes(); + + MinecraftAccountPtr m_acct; + Ui::SkinManageDialog* ui; + SkinList m_list; + QString m_selected_skin; + QHash m_capes; + QHash m_capes_idx; +}; diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.ui b/launcher/ui/dialogs/skins/SkinManageDialog.ui new file mode 100644 index 000000000..6ad826478 --- /dev/null +++ b/launcher/ui/dialogs/skins/SkinManageDialog.ui @@ -0,0 +1,193 @@ + + + SkinManageDialog + + + + 0 + 0 + 968 + 757 + + + + Skin Upload + + + + + + + + + + + + + true + + + + + + + + 0 + 0 + + + + Model + + + + + + Clasic + + + true + + + + + + + Slim + + + + + + + + + + Cape + + + + + + + + + + + + true + + + + + + + + + + + + Qt::CustomContextMenu + + + false + + + 0 + + + + + + + + + + + Open Folder + + + + + + + Import Skin + + + + + + + Reset Skin + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + &Delete Skin + + + Deletes selected skin + + + Del + + + + + &Rename Skin + + + Rename selected skin + + + F2 + + + + + + + buttonBox + rejected() + SkinManageDialog + reject() + + + 617 + 736 + + + 483 + 378 + + + + + buttonBox + accepted() + SkinManageDialog + accept() + + + 617 + 736 + + + 483 + 378 + + + + + diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 3dcf05e0a..25ccfd0d7 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -35,6 +35,7 @@ */ #include "AccountListPage.h" +#include "ui/dialogs/skins/SkinManageDialog.h" #include "ui_AccountListPage.h" #include @@ -42,23 +43,13 @@ #include -#include "net/NetJob.h" - #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/LoginDialog.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/OfflineLoginDialog.h" -#include "ui/dialogs/ProgressDialog.h" -#include "ui/dialogs/SkinUploadDialog.h" - -#include "minecraft/auth/AccountTask.h" -#include "minecraft/services/SkinDelete.h" -#include "tasks/Task.h" #include "Application.h" -#include "BuildConfig.h" - AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new Ui::AccountListPage) { ui->setupUi(this); @@ -235,8 +226,7 @@ void AccountListPage::updateButtonStates() } ui->actionRemove->setEnabled(accountIsReady); ui->actionSetDefault->setEnabled(accountIsReady); - ui->actionUploadSkin->setEnabled(accountIsReady && accountIsOnline); - ui->actionDeleteSkin->setEnabled(accountIsReady && accountIsOnline); + ui->actionManageSkins->setEnabled(accountIsReady && accountIsOnline); ui->actionRefresh->setEnabled(accountIsReady && accountIsOnline); if (m_accounts->defaultAccount().get() == nullptr) { @@ -248,29 +238,13 @@ void AccountListPage::updateButtonStates() } } -void AccountListPage::on_actionUploadSkin_triggered() +void AccountListPage::on_actionManageSkins_triggered() { QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); if (selection.size() > 0) { QModelIndex selected = selection.first(); MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); - SkinUploadDialog dialog(account, this); + SkinManageDialog dialog(this, account); dialog.exec(); } } - -void AccountListPage::on_actionDeleteSkin_triggered() -{ - QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); - if (selection.size() <= 0) - return; - - QModelIndex selected = selection.first(); - MinecraftAccountPtr account = selected.data(AccountList::PointerRole).value(); - ProgressDialog prog(this); - auto deleteSkinTask = SkinDelete::make(account->accessToken()); - if (prog.execWithTask((Task*)deleteSkinTask.get()) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); - return; - } -} diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h index add0f4aa0..64702cff7 100644 --- a/launcher/ui/pages/global/AccountListPage.h +++ b/launcher/ui/pages/global/AccountListPage.h @@ -77,8 +77,7 @@ class AccountListPage : public QMainWindow, public BasePage { void on_actionRefresh_triggered(); void on_actionSetDefault_triggered(); void on_actionNoDefault_triggered(); - void on_actionUploadSkin_triggered(); - void on_actionDeleteSkin_triggered(); + void on_actionManageSkins_triggered(); void listChanged(); diff --git a/launcher/ui/pages/global/AccountListPage.ui b/launcher/ui/pages/global/AccountListPage.ui index 469955b51..0e73f8c52 100644 --- a/launcher/ui/pages/global/AccountListPage.ui +++ b/launcher/ui/pages/global/AccountListPage.ui @@ -60,19 +60,13 @@ - - + Add &Mojang - - - Remo&ve - - &Set Default @@ -86,17 +80,12 @@ &No Default - + - &Upload Skin - - - - - &Delete Skin + &Manage Skins - Delete the currently active skin and go back to the default one + Manage Skins @@ -117,6 +106,11 @@ Refresh the account tokens + + + Remo&ve + + From 8bad255a9191cd76808a73942da366c981643d35 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 5 Sep 2023 20:13:16 +0300 Subject: [PATCH 007/139] added more import options Signed-off-by: Trial97 --- launcher/minecraft/skins/CapeChange.cpp | 2 +- launcher/minecraft/skins/SkinDelete.cpp | 2 +- launcher/minecraft/skins/SkinList.cpp | 74 ++++---- launcher/minecraft/skins/SkinList.h | 6 +- launcher/minecraft/skins/SkinModel.cpp | 58 +------ launcher/minecraft/skins/SkinModel.h | 3 +- launcher/minecraft/skins/SkinUpload.cpp | 4 +- launcher/net/NetAction.h | 1 + .../ui/dialogs/skins/SkinManageDialog.cpp | 158 ++++++++++++++++-- launcher/ui/dialogs/skins/SkinManageDialog.h | 4 +- launcher/ui/dialogs/skins/SkinManageDialog.ui | 43 ++++- 11 files changed, 229 insertions(+), 126 deletions(-) diff --git a/launcher/minecraft/skins/CapeChange.cpp b/launcher/minecraft/skins/CapeChange.cpp index 863e89844..4db28e245 100644 --- a/launcher/minecraft/skins/CapeChange.cpp +++ b/launcher/minecraft/skins/CapeChange.cpp @@ -44,7 +44,7 @@ CapeChange::CapeChange(QString token, QString cape) : NetRequest(), m_capeId(cape), m_token(token) { logCat = taskMCSkinsLogC; -}; +} QNetworkReply* CapeChange::getReply(QNetworkRequest& request) { diff --git a/launcher/minecraft/skins/SkinDelete.cpp b/launcher/minecraft/skins/SkinDelete.cpp index 982cac1b7..3c50cf313 100644 --- a/launcher/minecraft/skins/SkinDelete.cpp +++ b/launcher/minecraft/skins/SkinDelete.cpp @@ -42,7 +42,7 @@ SkinDelete::SkinDelete(QString token) : NetRequest(), m_token(token) { logCat = taskMCSkinsLogC; -}; +} QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) { diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index be329564b..15d0b0a8e 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -85,8 +85,6 @@ bool SkinList::update() } catch (const Exception& e) { qCritical() << "Couldn't load skins json:" << e.cause(); } - } else { - newSkins = loadMinecraftSkins(); } bool needsSave = false; @@ -108,7 +106,7 @@ bool SkinList::update() auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); - s.setModel(SkinModel::CLASSIC); // maybe better model detection + s.setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; @@ -116,6 +114,7 @@ bool SkinList::update() } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); + nskin->setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); } } @@ -207,14 +206,14 @@ bool SkinList::dropMimeData(const QMimeData* data, // files dropped from outside? if (data->hasUrls()) { auto urls = data->urls(); - QStringList iconFiles; + QStringList skinFiles; for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; - iconFiles += url.toLocalFile(); + skinFiles << url.toLocalFile(); } - installSkins(iconFiles); + installSkins(skinFiles); return true; } return false; @@ -261,20 +260,26 @@ int SkinList::rowCount(const QModelIndex& parent) const void SkinList::installSkins(const QStringList& iconFiles) { for (QString file : iconFiles) - installSkin(file, {}); + installSkin(file); } -void SkinList::installSkin(const QString& file, const QString& name) +QString SkinList::installSkin(const QString& file, const QString& name) { + if (file.isEmpty()) + return tr("Path is empty."); QFileInfo fileinfo(file); - if (!fileinfo.isReadable() || !fileinfo.isFile()) - return; - + if (!fileinfo.exists()) + return tr("File doesn't exist."); + if (!fileinfo.isFile()) + return tr("Not a file."); + if (!fileinfo.isReadable()) + return tr("File is not readable."); if (fileinfo.suffix() != "png" && !SkinModel(fileinfo.absoluteFilePath()).isValid()) - return; + return tr("Skin images must be 64x64 or 64x32 pixel PNG files."); QString target = FS::PathCombine(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name); - QFile::copy(file, target); + + return QFile::copy(file, target) ? "" : tr("Unable to copy file"); } int SkinList::getSkinIndex(const QString& key) const @@ -311,10 +316,12 @@ bool SkinList::deleteSkin(const QString& key, const bool trash) if (trash) { if (FS::trash(s.getPath(), nullptr)) { m_skin_list.remove(idx); + save(); return true; } } else if (QFile::remove(s.getPath())) { m_skin_list.remove(idx); + save(); return true; } } @@ -361,33 +368,22 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) return true; } -QVector SkinList::loadMinecraftSkins() +void SkinList::updateSkin(SkinModel s) { - QString partialPath; -#if defined(Q_OS_OSX) - partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); -#elif defined(Q_OS_WIN32) - partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); -#else - partialPath = QDir::homePath(); -#endif - QVector newSkins; - auto path = FS::PathCombine(partialPath, ".minecraft", "launcher_custom_skins.json"); - auto manifestInfo = QFileInfo(path); - if (!manifestInfo.exists()) - return {}; - try { - auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); - const auto root = doc.object(); - auto skins = Json::ensureObject(root, "customSkins"); - for (auto key : skins.keys()) { - SkinModel s(m_dir, Json::ensureObject(skins, key)); - if (s.isValid()) { - newSkins << s; - } + auto done = false; + for (auto i = 0; i < m_skin_list.size(); i++) { + if (m_skin_list[i].getPath() == s.getPath()) { + m_skin_list[i].setCapeId(s.getCapeId()); + m_skin_list[i].setModel(s.getModel()); + m_skin_list[i].setURL(s.getURL()); + done = true; + break; } - } catch (const Exception& e) { - qCritical() << "Couldn't load minecraft skins json:" << e.cause(); } - return newSkins; + if (!done) { + beginInsertRows(QModelIndex(), m_skin_list.count(), m_skin_list.count() + 1); + m_skin_list.append(s); + endInsertRows(); + } + save(); } diff --git a/launcher/minecraft/skins/SkinList.h b/launcher/minecraft/skins/SkinList.h index 8d8266d79..b6981e1b4 100644 --- a/launcher/minecraft/skins/SkinList.h +++ b/launcher/minecraft/skins/SkinList.h @@ -46,7 +46,7 @@ class SkinList : public QAbstractListModel { bool deleteSkin(const QString& key, const bool trash); void installSkins(const QStringList& iconFiles); - void installSkin(const QString& file, const QString& name); + QString installSkin(const QString& file, const QString& name = {}); const SkinModel* skin(const QString& key) const; SkinModel* skin(const QString& key); @@ -58,14 +58,14 @@ class SkinList : public QAbstractListModel { void save(); int getSelectedAccountSkin(); + void updateSkin(SkinModel s); + private: // hide copy constructor SkinList(const SkinList&) = delete; // hide assign op SkinList& operator=(const SkinList&) = delete; - QVector loadMinecraftSkins(); - protected slots: void directoryChanged(const QString& path); void fileChanged(const QString& path); diff --git a/launcher/minecraft/skins/SkinModel.cpp b/launcher/minecraft/skins/SkinModel.cpp index 3b467019c..d53b9e762 100644 --- a/launcher/minecraft/skins/SkinModel.cpp +++ b/launcher/minecraft/skins/SkinModel.cpp @@ -31,28 +31,12 @@ SkinModel::SkinModel(QDir skinDir, QJsonObject obj) : m_cape_id(Json::ensureString(obj, "capeId")), m_model(Model::CLASSIC), m_url(Json::ensureString(obj, "url")) { auto name = Json::ensureString(obj, "name"); - auto skinImage = Json::ensureString(obj, "skinImage"); - if (!skinImage.isEmpty()) { // minecraft skin model - skinImage = skinImage.mid(22); - m_texture.loadFromData(QByteArray::fromBase64(skinImage.toUtf8()), "PNG"); - auto textureId = Json::ensureString(obj, "textureId"); - if (name.isEmpty()) { - name = textureId; - } - if (Json::ensureBoolean(obj, "slim", false)) { - m_model = Model::SLIM; - } - } else { - if (auto model = Json::ensureString(obj, "model"); model == "SLIM") { - m_model = Model::SLIM; - } + + if (auto model = Json::ensureString(obj, "model"); model == "SLIM") { + m_model = Model::SLIM; } m_path = skinDir.absoluteFilePath(name) + ".png"; - if (!QFileInfo(m_path).exists() && isValid()) { - m_texture.save(m_path, "PNG"); - } else { - m_texture = QPixmap(m_path); - } + m_texture = QPixmap(m_path); } QString SkinModel::name() const @@ -92,37 +76,3 @@ bool SkinModel::isValid() const { return !m_texture.isNull() && (m_texture.size().height() == 32 || m_texture.size().height() == 64) && m_texture.size().width() == 64; } - -QPixmap SkinModel::renderFrontBody() const -{ - auto isSlim = m_model == SLIM; - auto slimOffset = isSlim ? 1 : 0; - auto isOldSkin = m_texture.height() < 64; - - auto head = m_texture.copy(QRect(8, 8, 16, 16)); - auto torso = m_texture.copy(QRect(20, 20, 28, 32)); - auto rightArm = m_texture.copy(QRect(44, 20, 48 - slimOffset, 32)); - auto rightLeg = m_texture.copy(QRect(4, 20, 8, 32)); - QPixmap leftArm, leftLeg; - - if (isOldSkin) { - leftArm = rightArm.transformed(QTransform().scale(-1, 1)); - leftLeg = rightLeg.transformed(QTransform().scale(-1, 1)); - } else { - leftArm = m_texture.copy(QRect(36, 52, 40 - slimOffset, 64)); - leftLeg = m_texture.copy(QRect(20, 52, 24, 64)); - } - QPixmap output(16, 32); - output.fill(Qt::black); - QPainter p; - if (!p.begin(&output)) - return {}; - p.drawPixmap(QPoint(4, 0), head); - p.drawPixmap(QPoint(4, 8), torso); - p.drawPixmap(QPoint(12, 8), leftArm); - p.drawPixmap(QPoint(slimOffset, 8), rightArm); - p.drawPixmap(QPoint(8, 20), leftLeg); - p.drawPixmap(QPoint(4, 20), leftArm); - - return output.scaled(128, 128, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation); -} \ No newline at end of file diff --git a/launcher/minecraft/skins/SkinModel.h b/launcher/minecraft/skins/SkinModel.h index 6d135c7f7..46e9d6cf1 100644 --- a/launcher/minecraft/skins/SkinModel.h +++ b/launcher/minecraft/skins/SkinModel.h @@ -26,6 +26,7 @@ class SkinModel { public: enum Model { CLASSIC, SLIM }; + SkinModel() = default; SkinModel(QString path); SkinModel(QDir skinDir, QJsonObject obj); virtual ~SkinModel() = default; @@ -47,8 +48,6 @@ class SkinModel { QJsonObject toJSON() const; - QPixmap renderFrontBody() const; - private: QString m_path; QPixmap m_texture; diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index 4e56bd7e6..4496f3f1c 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -44,8 +44,8 @@ SkinUpload::SkinUpload(QString token, SkinModel* skin) : NetRequest(), m_skin(skin), m_token(token) { - logCat = taskUploadLogC; -}; + logCat = taskMCSkinsLogC; +} QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index b66b91941..6440d38b9 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -55,6 +55,7 @@ class NetAction : public Task { virtual ~NetAction() = default; QUrl url() { return m_url; } + void setUrl(QUrl url) { m_url = url; } void setNetwork(shared_qobject_ptr network) { m_network = network; } diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 1ba7e7055..0afb6cbc4 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -16,37 +16,41 @@ * along with this program. If not, see . */ -#include -#include -#include -#include +#include "SkinManageDialog.h" +#include "ui_SkinManageDialog.h" #include #include #include +#include +#include #include #include #include +#include +#include +#include #include "Application.h" #include "DesktopServices.h" +#include "Json.h" #include "QObjectPtr.h" -#include "SkinManageDialog.h" #include "minecraft/auth/AccountTask.h" +#include "minecraft/auth/Parsers.h" #include "minecraft/skins/CapeChange.h" #include "minecraft/skins/SkinDelete.h" #include "minecraft/skins/SkinList.h" #include "minecraft/skins/SkinModel.h" #include "minecraft/skins/SkinUpload.h" +#include "net/Download.h" #include "net/NetJob.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/InstanceDelegate.h" -#include "ui_SkinManageDialog.h" SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct) : QDialog(parent), m_acct(acct), ui(new Ui::SkinManageDialog), m_list(this, APPLICATION->settings()->get("SkinsDir").toString(), acct) @@ -132,20 +136,15 @@ void SkinManageDialog::on_openDirBtn_clicked() DesktopServices::openDirectory(m_list.getDir(), true); } -void SkinManageDialog::on_addBtn_clicked() +void SkinManageDialog::on_fileBtn_clicked() { auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); - if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { + auto message = m_list.installSkin(raw_path, {}); + if (!message.isEmpty()) { + CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), message, QMessageBox::Critical)->show(); return; } - if (!SkinModel(raw_path).isValid()) { - CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), - tr("Skin images must be 64x64 or 64x32 pixel PNG files."), QMessageBox::Critical) - ->show(); - return; - } - m_list.installSkin(raw_path, {}); } QPixmap previewCape(QPixmap capeImage) @@ -337,3 +336,132 @@ void SkinManageDialog::on_action_Delete_Skin_triggered(bool checked) } } } + +void SkinManageDialog::on_urlBtn_clicked() +{ + auto url = QUrl(ui->urlLine->text()); + if (!url.isValid()) { + CustomMessageBox::selectable(this, tr("Invalid url"), tr("Invalid url"), QMessageBox::Critical)->show(); + return; + } + ui->urlLine->setText(""); + + NetJob::Ptr job{ new NetJob(tr("Download skin"), APPLICATION->network()) }; + + auto path = FS::PathCombine(m_list.getDir(), url.fileName()); + job->addNetAction(Net::Download::makeFile(url, path)); + ProgressDialog dlg(this); + dlg.execWithTask(job.get()); + SkinModel s(path); + if (!s.isValid()) { + CustomMessageBox::selectable(this, tr("URL is not a valid skin"), tr("Skin images must be 64x64 or 64x32 pixel PNG files."), + QMessageBox::Critical) + ->show(); + QFile::remove(path); + return; + } + if (QFileInfo(path).suffix().isEmpty()) { + QFile::rename(path, path + ".png"); + } +} + +class WaitTask : public Task { + public: + WaitTask() : m_loop(), m_done(false){}; + virtual ~WaitTask() = default; + + public slots: + void quit() + { + m_done = true; + m_loop.quit(); + } + + protected: + virtual void executeTask() + { + if (!m_done) + m_loop.exec(); + emitSucceeded(); + }; + + private: + QEventLoop m_loop; + bool m_done; +}; + +void SkinManageDialog::on_userBtn_clicked() +{ + auto user = ui->urlLine->text(); + if (user.isEmpty()) { + return; + } + ui->urlLine->setText(""); + MinecraftProfile mcProfile; + auto path = FS::PathCombine(m_list.getDir(), user + ".png"); + + NetJob::Ptr job{ new NetJob(tr("Download user skin"), APPLICATION->network(), 1) }; + + auto uuidOut = std::make_shared(); + auto profileOut = std::make_shared(); + + auto uuidLoop = makeShared(); + auto profileLoop = makeShared(); + + auto getUUID = Net::Download::makeByteArray("https://api.mojang.com/users/profiles/minecraft/" + user, uuidOut); + auto getProfile = Net::Download::makeByteArray(QUrl(), profileOut); + auto downloadSkin = Net::Download::makeFile(QUrl(), path); + + connect(getUUID.get(), &Task::aborted, uuidLoop.get(), &WaitTask::quit); + connect(getUUID.get(), &Task::failed, uuidLoop.get(), &WaitTask::quit); + connect(getProfile.get(), &Task::aborted, profileLoop.get(), &WaitTask::quit); + connect(getProfile.get(), &Task::failed, profileLoop.get(), &WaitTask::quit); + + connect(getUUID.get(), &Task::succeeded, this, [uuidLoop, uuidOut, job, getProfile] { + try { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*uuidOut, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from Minecraft skin service at " << parse_error.offset + << " reason: " << parse_error.errorString(); + uuidLoop->quit(); + return; + } + const auto root = doc.object(); + auto id = Json::ensureString(root, "id"); + if (!id.isEmpty()) { + getProfile->setUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + id); + } else { + job->abort(); + } + } catch (const Exception& e) { + qCritical() << "Couldn't load skin json:" << e.cause(); + } + uuidLoop->quit(); + }); + + connect(getProfile.get(), &Task::succeeded, this, [profileLoop, profileOut, job, getProfile, &mcProfile, downloadSkin] { + if (Parsers::parseMinecraftProfileMojang(*profileOut, mcProfile)) { + downloadSkin->setUrl(mcProfile.skin.url); + } else { + job->abort(); + } + profileLoop->quit(); + }); + + job->addNetAction(getUUID); + job->addTask(uuidLoop); + job->addNetAction(getProfile); + job->addTask(profileLoop); + job->addNetAction(downloadSkin); + ProgressDialog dlg(this); + dlg.execWithTask(job.get()); + + SkinModel s(path); + s.setModel(mcProfile.skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setURL(mcProfile.skin.url); + if (m_capes.contains(mcProfile.currentCape)) { + s.setCapeId(mcProfile.currentCape); + } + m_list.updateSkin(s); +} \ No newline at end of file diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.h b/launcher/ui/dialogs/skins/SkinManageDialog.h index 8c55c3310..ce8fc9348 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.h +++ b/launcher/ui/dialogs/skins/SkinManageDialog.h @@ -40,7 +40,9 @@ class SkinManageDialog : public QDialog { void activated(QModelIndex); void delayed_scroll(QModelIndex); void on_openDirBtn_clicked(); - void on_addBtn_clicked(); + void on_fileBtn_clicked(); + void on_urlBtn_clicked(); + void on_userBtn_clicked(); void accept() override; void on_capeCombo_currentIndexChanged(int index); void on_steveBtn_toggled(bool checked); diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.ui b/launcher/ui/dialogs/skins/SkinManageDialog.ui index 6ad826478..c2ce9143c 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.ui +++ b/launcher/ui/dialogs/skins/SkinManageDialog.ui @@ -100,7 +100,7 @@ - + @@ -108,13 +108,6 @@ - - - - Import Skin - - - @@ -122,8 +115,42 @@ + + + + + + + + + + + Import URL + + + + + + + Import user + + + + + + + Import File + + + + + + 0 + 0 + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok From 8c8e4329d72afb1b5141725127c883b52513761b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 5 Sep 2023 23:45:32 +0300 Subject: [PATCH 008/139] fix codeql Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 12 ++++++------ launcher/minecraft/skins/SkinList.h | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index 15d0b0a8e..b3a593454 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -368,21 +368,21 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) return true; } -void SkinList::updateSkin(SkinModel s) +void SkinList::updateSkin(SkinModel* s) { auto done = false; for (auto i = 0; i < m_skin_list.size(); i++) { - if (m_skin_list[i].getPath() == s.getPath()) { - m_skin_list[i].setCapeId(s.getCapeId()); - m_skin_list[i].setModel(s.getModel()); - m_skin_list[i].setURL(s.getURL()); + if (m_skin_list[i].getPath() == s->getPath()) { + m_skin_list[i].setCapeId(s->getCapeId()); + m_skin_list[i].setModel(s->getModel()); + m_skin_list[i].setURL(s->getURL()); done = true; break; } } if (!done) { beginInsertRows(QModelIndex(), m_skin_list.count(), m_skin_list.count() + 1); - m_skin_list.append(s); + m_skin_list.append(*s); endInsertRows(); } save(); diff --git a/launcher/minecraft/skins/SkinList.h b/launcher/minecraft/skins/SkinList.h index b6981e1b4..66af6a17b 100644 --- a/launcher/minecraft/skins/SkinList.h +++ b/launcher/minecraft/skins/SkinList.h @@ -58,7 +58,7 @@ class SkinList : public QAbstractListModel { void save(); int getSelectedAccountSkin(); - void updateSkin(SkinModel s); + void updateSkin(SkinModel* s); private: // hide copy constructor diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 0afb6cbc4..4ef91a2bf 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -463,5 +463,5 @@ void SkinManageDialog::on_userBtn_clicked() if (m_capes.contains(mcProfile.currentCape)) { s.setCapeId(mcProfile.currentCape); } - m_list.updateSkin(s); + m_list.updateSkin(&s); } \ No newline at end of file From 6ec1cf6e4933013e04acb9086f3fcf4a844d181a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 8 Sep 2023 19:50:46 +0300 Subject: [PATCH 009/139] made skin upload more generic Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinUpload.cpp | 10 +++++----- launcher/minecraft/skins/SkinUpload.h | 8 ++++---- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index 4496f3f1c..4a88faedf 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -42,7 +42,7 @@ #include "net/ByteArraySink.h" #include "net/StaticHeaderProxy.h" -SkinUpload::SkinUpload(QString token, SkinModel* skin) : NetRequest(), m_skin(skin), m_token(token) +SkinUpload::SkinUpload(QString token, QString path, QString variant) : NetRequest(), m_token(token), m_path(path), m_variant(variant) { logCat = taskMCSkinsLogC; } @@ -55,11 +55,11 @@ QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\"")); - skin.setBody(FS::read(m_skin->getPath())); + skin.setBody(FS::read(m_path)); QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - model.setBody(m_skin->getModelString().toUtf8()); + model.setBody(m_variant.toUtf8()); multiPart->append(skin); multiPart->append(model); @@ -74,9 +74,9 @@ void SkinUpload::init() })); } -SkinUpload::Ptr SkinUpload::make(QString token, SkinModel* skin) +SkinUpload::Ptr SkinUpload::make(QString token, QString path, QString variant) { - auto up = makeShared(token, skin); + auto up = makeShared(token, path, variant); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); diff --git a/launcher/minecraft/skins/SkinUpload.h b/launcher/minecraft/skins/SkinUpload.h index d070f301d..f24cef5a2 100644 --- a/launcher/minecraft/skins/SkinUpload.h +++ b/launcher/minecraft/skins/SkinUpload.h @@ -18,7 +18,6 @@ #pragma once -#include "minecraft/skins/SkinModel.h" #include "net/NetRequest.h" class SkinUpload : public Net::NetRequest { @@ -27,16 +26,17 @@ class SkinUpload : public Net::NetRequest { using Ptr = shared_qobject_ptr; // Note this class takes ownership of the file. - SkinUpload(QString token, SkinModel* skin); + SkinUpload(QString token, QString path, QString variant); virtual ~SkinUpload() = default; - static SkinUpload::Ptr make(QString token, SkinModel* skin); + static SkinUpload::Ptr make(QString token, QString path, QString variant); void init() override; protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; private: - SkinModel* m_skin; QString m_token; + QString m_path; + QString m_variant; }; diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 4ef91a2bf..24197baeb 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -242,7 +242,7 @@ void SkinManageDialog::accept() return; } - skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin)); + skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin->getPath(), skin->getModelString())); auto selectedCape = skin->getCapeId(); if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { From d2e662ddbb518b147de76ca3f613e046d2912db5 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 12:35:34 -0400 Subject: [PATCH 010/139] added support for components in resource pack descriptions. Signed-off-by: Caden Miller --- .../mod/tasks/LocalResourcePackParseTask.cpp | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 73cbf891c..f78d3b7b3 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -186,7 +186,35 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - pack.setDescription(Json::ensureString(pack_obj, "description", "")); + + // description could either be string, or array of dictionaries + auto desc_val = pack_obj.value("description"); + + if (desc_val.isString()) { + pack.setDescription(Json::ensureString(pack_obj, "description", "")); + } else if (desc_val.isArray()) { + + // rebuild the description from the dictionaries without colors + QString build_desc; + auto arr = desc_val.toArray(); + + for(const QJsonValue& dict : arr) { + // must be an object, for a dictionary + if (!dict.isObject()) throw Json::JsonException("Invalid value description."); + + auto obj = dict.toObject(); + auto val = obj.value(obj.keys()[0]); + + // value must be a string type + if (!val.isString()) throw Json::JsonException("Invalid value description type."); + + build_desc.append(val.toString()); + }; + + pack.setDescription(build_desc); + } else { + throw Json::JsonException("Invalid description type."); + } } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; From 093d09efe36dbe1b18e4ae0ccb48150ef2f9f3ab Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:03:33 -0400 Subject: [PATCH 011/139] fix style, and use qWarning instead of throw. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index f78d3b7b3..22f5fa163 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -189,9 +189,13 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); + if (desc_val.isUndefined()) { + qWarning() << "No resource pack description found."; + return false; + } if (desc_val.isString()) { - pack.setDescription(Json::ensureString(pack_obj, "description", "")); + pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { // rebuild the description from the dictionaries without colors @@ -200,20 +204,27 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) for(const QJsonValue& dict : arr) { // must be an object, for a dictionary - if (!dict.isObject()) throw Json::JsonException("Invalid value description."); + if (!dict.isObject()) + { + qWarning() << "Invalid component object."; + continue; + } auto obj = dict.toObject(); auto val = obj.value(obj.keys()[0]); - // value must be a string type - if (!val.isString()) throw Json::JsonException("Invalid value description type."); + if (!val.isString()) + { + qWarning() << "Invalid text description type in components."; + continue; + } build_desc.append(val.toString()); }; pack.setDescription(build_desc); } else { - throw Json::JsonException("Invalid description type."); + qWarning() << "Invalid description type."; } } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); From 04aa0155bf181dcd17236ef5baa3b91a4b9df449 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:45:30 -0400 Subject: [PATCH 012/139] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: d2e662ddbb518b147de76ca3f613e046d2912db5 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 22f5fa163..7a2fd7a86 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -221,7 +221,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) build_desc.append(val.toString()); }; - pack.setDescription(build_desc); } else { qWarning() << "Invalid description type."; From 05f4214cc5d3a9005c0d259ca185303792aa2fa7 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:50:13 -0400 Subject: [PATCH 013/139] fix clang-format failing --- .../minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 7a2fd7a86..b696adea5 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -202,10 +202,9 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) QString build_desc; auto arr = desc_val.toArray(); - for(const QJsonValue& dict : arr) { + for (const QJsonValue& dict : arr) { // must be an object, for a dictionary - if (!dict.isObject()) - { + if (!dict.isObject()) { qWarning() << "Invalid component object."; continue; } @@ -213,8 +212,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) auto obj = dict.toObject(); auto val = obj.value(obj.keys()[0]); - if (!val.isString()) - { + if (!val.isString()) { qWarning() << "Invalid text description type in components."; continue; } From ef1dc2afaccc681fdfd53513ac134b36a5d943cc Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:50:59 -0400 Subject: [PATCH 014/139] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: 05f4214cc5d3a9005c0d259ca185303792aa2fa7 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index b696adea5..eaef76a6c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -197,7 +197,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { - // rebuild the description from the dictionaries without colors QString build_desc; auto arr = desc_val.toArray(); From 1261908ef7465e000390c9c12133171f4e73302f Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 19:55:55 -0400 Subject: [PATCH 015/139] remove space for clang-format Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index eaef76a6c..1f535695b 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -194,7 +194,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) return false; } - if (desc_val.isString()) { + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { // rebuild the description from the dictionaries without colors From 7a7c3015f47cba94eb34deba6748dc8555d8e457 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sat, 9 Sep 2023 20:25:49 -0400 Subject: [PATCH 016/139] fix not properly reading json text. The text now displays properly in the GUI of Prism. Signed-off-by: cullvox --- .../minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 1f535695b..4c30c1204 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -209,14 +209,8 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) } auto obj = dict.toObject(); - auto val = obj.value(obj.keys()[0]); - if (!val.isString()) { - qWarning() << "Invalid text description type in components."; - continue; - } - - build_desc.append(val.toString()); + build_desc.append(Json::ensureString(obj, "text", {})); }; pack.setDescription(build_desc); } else { From 58bd6d929f20e43c38cb372e431d1e1240d736b6 Mon Sep 17 00:00:00 2001 From: cullvox Date: Sun, 10 Sep 2023 23:37:26 -0400 Subject: [PATCH 017/139] added formatting with colors. This is somewhat dirty implementation and I will clean it up soon. Using the HTML rich text features of Qt, made it much easier to understand what was needed. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 170 +++++++++++++++--- 1 file changed, 143 insertions(+), 27 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 4c30c1204..e6dde4ba0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -178,9 +178,143 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta -bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) +struct TextFormat { + QString color = "#000000"; + bool bold = false; + bool italic = false; + bool underlined = false; + bool strikethrough = false; + + TextFormat() = default; + ~TextFormat() = default; + + void clear() { + color = "#00000"; + bold = false; + italic = false; + underlined = false; + strikethrough = false; + } +}; + +QString textColorToHexColor(const QString& color) { + static const std::unordered_map str_to_hex = { + { "black", "#000000" }, + { "dark_blue", "#0000AA" }, + { "dark_green", "#00AA00" }, + { "dark_aqua", "#00AAAA" }, + { "dark_red", "#AA0000" }, + { "dark_purple", "#AA00AA" }, + { "gold", "#FFAA00" }, + { "gray", "#AAAAAA" }, + { "dark_gray", "#555555" }, + { "blue", "#5555FF" }, + { "green", "#55FF55" }, + { "aqua", "#55FFFF" }, + { "red", "#FF5555" }, + { "light_purple", "#FF55FF" }, + { "yellow", "#FFFF55" }, + { "white", "#FFFFFF" }, + }; + + auto it = str_to_hex.find(color); + return (it != str_to_hex.end()) ? it->second : QString("#000000"); // return black if color not found +} + +bool readFormat(const QJsonObject& obj, TextFormat& format) { + auto text = obj.value("text"); + auto color = obj.value("color"); + auto bold = obj.value("bold"); + auto italic = obj.value("italic"); + auto underlined = obj.value("underlined"); + auto strikethrough = obj.value("strikethrough"); + auto extra = obj.value("extra"); + + if (color.isString()) { + format.color = textColorToHexColor(color.toString()); + } + if (bold.isBool()) + format.bold = bold.toBool(); + if (italic.isBool()) + format.italic = italic.toBool(); + if (underlined.isBool()) + format.underlined = underlined.toBool(); + if (strikethrough.isBool()) + format.strikethrough = strikethrough.toBool(); + + return true; +} + +void appendBeginFormat(TextFormat& format, QString& toAppend) { + toAppend.append(""); + if (format.bold) + toAppend.append(""); + if (format.italic) + toAppend.append(""); + if (format.underlined) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); +} + +void appendEndFormat(TextFormat& format, QString& toAppend) { + toAppend.append(""); + if (format.bold) + toAppend.append(""); + if (format.italic) + toAppend.append(""); + if (format.underlined) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); +} + +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); + +bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { + + for (const QJsonValue& val : arr) { + processComponent(val, result, parentFormat); + } + + return true; +} + +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) { + if (value.isString()) { + result.append(value.toString()); + } else if (value.isObject()) { + auto obj = value.toObject(); + + TextFormat format{}; + if (parentFormat) + format = *parentFormat; + + if (!readFormat(obj, format)) + return false; + + appendBeginFormat(format, result); + + auto text = obj.value("text"); + if (text.isString()) + result.append(text.toString()); + + auto extra = obj.value("extra"); + if (extra.isArray()) + if (!processComponentList(extra.toArray(), result, &format)) + return false; + + appendEndFormat(format, result); + } + + return true; +} + +// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { + + try { auto json_doc = QJsonDocument::fromJson(raw_data); auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); @@ -189,33 +323,15 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - if (desc_val.isUndefined()) { - qWarning() << "No resource pack description found."; - return false; - } - - if (desc_val.isString()) { + + if (desc_val.isString()) pack.setDescription(desc_val.toString()); - } else if (desc_val.isArray()) { - // rebuild the description from the dictionaries without colors - QString build_desc; - auto arr = desc_val.toArray(); - - for (const QJsonValue& dict : arr) { - // must be an object, for a dictionary - if (!dict.isObject()) { - qWarning() << "Invalid component object."; - continue; - } - - auto obj = dict.toObject(); - - build_desc.append(Json::ensureString(obj, "text", {})); - }; - pack.setDescription(build_desc); - } else { - qWarning() << "Invalid description type."; + else if (desc_val.isArray()) { + QString desc{}; + processComponentList(desc_val.toArray(), desc); + pack.setDescription(desc); } + } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; From fbe4043651bebaca399b9f4499a2097670e485b6 Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 01:34:53 -0400 Subject: [PATCH 018/139] fix formatting and add more proper errors. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 129 +++++++++++------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index e6dde4ba0..ca1a5455c 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -184,45 +184,60 @@ struct TextFormat { bool italic = false; bool underlined = false; bool strikethrough = false; - - TextFormat() = default; - ~TextFormat() = default; - - void clear() { - color = "#00000"; - bold = false; - italic = false; - underlined = false; - strikethrough = false; - } }; -QString textColorToHexColor(const QString& color) +bool textColorToHexColor(const QString& text_color, QString& result) { - static const std::unordered_map str_to_hex = { - { "black", "#000000" }, - { "dark_blue", "#0000AA" }, - { "dark_green", "#00AA00" }, - { "dark_aqua", "#00AAAA" }, - { "dark_red", "#AA0000" }, - { "dark_purple", "#AA00AA" }, - { "gold", "#FFAA00" }, - { "gray", "#AAAAAA" }, - { "dark_gray", "#555555" }, - { "blue", "#5555FF" }, - { "green", "#55FF55" }, - { "aqua", "#55FFFF" }, - { "red", "#FF5555" }, - { "light_purple", "#FF55FF" }, - { "yellow", "#FFFF55" }, - { "white", "#FFFFFF" }, + static const QHash text_to_hex = { + { "black", "#000000" }, { "dark_blue", "#0000AA" }, { "dark_green", "#00AA00" }, { "dark_aqua", "#00AAAA" }, + { "dark_red", "#AA0000" }, { "dark_purple", "#AA00AA" }, { "gold", "#FFAA00" }, { "gray", "#AAAAAA" }, + { "dark_gray", "#555555" }, { "blue", "#5555FF" }, { "green", "#55FF55" }, { "aqua", "#55FFFF" }, + { "red", "#FF5555" }, { "light_purple", "#FF55FF" }, { "yellow", "#FFFF55" }, { "white", "#FFFFFF" }, }; - auto it = str_to_hex.find(color); - return (it != str_to_hex.end()) ? it->second : QString("#000000"); // return black if color not found + auto it = text_to_hex.find(text_color); + + if (it == text_to_hex.end()) { + qWarning() << "Invalid color string in component!"; + result = "#000000"; // return black if color not found + return false; + } + + result = it.value(); + return true; } -bool readFormat(const QJsonObject& obj, TextFormat& format) { +bool getBoolOrFromText(const QJsonValue& val, bool& result) +{ + if (val.isUndefined()) { + result = false; + return true; + } + + if (val.isBool()) { + result = val.toBool(); + return true; + } else if (val.isString()) { + auto bool_str = val.toString(); + + if (bool_str == "true") { + result = true; + return true; + } else if (bool_str == "false") { + result = false; + return true; + } else { + qWarning() << "Invalid bool value in component!"; + return false; + } + } else { + qWarning() << "Invalid type where bool expected!"; + return false; + } +} + +bool readFormat(const QJsonObject& obj, TextFormat& format) +{ auto text = obj.value("text"); auto color = obj.value("color"); auto bold = obj.value("bold"); @@ -232,21 +247,30 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) { auto extra = obj.value("extra"); if (color.isString()) { - format.color = textColorToHexColor(color.toString()); - } - if (bold.isBool()) - format.bold = bold.toBool(); - if (italic.isBool()) - format.italic = italic.toBool(); - if (underlined.isBool()) - format.underlined = underlined.toBool(); - if (strikethrough.isBool()) - format.strikethrough = strikethrough.toBool(); + // colors can either be a hex code or one of a few text colors + auto col_str = color.toString(); - return true; + const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); + const auto hex_match = hex_expression.match(col_str); + + if (hex_match.hasMatch()) { + format.color = color.toString(); + } else { + if (!textColorToHexColor(color.toString(), format.color)) { + qWarning() << "Invalid color type in component!"; + return false; + } + } + } + + return getBoolOrFromText(bold, format.bold) && + getBoolOrFromText(italic, format.italic) && + getBoolOrFromText(underlined, format.underlined) && + getBoolOrFromText(strikethrough, format.strikethrough); } -void appendBeginFormat(TextFormat& format, QString& toAppend) { +void appendBeginFormat(TextFormat& format, QString& toAppend) +{ toAppend.append(""); if (format.bold) toAppend.append(""); @@ -275,7 +299,8 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { for (const QJsonValue& val : arr) { - processComponent(val, result, parentFormat); + if (!processComponent(val, result, parentFormat)) + return false; } return true; @@ -306,6 +331,9 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare return false; appendEndFormat(format, result); + } else { + qWarning() << "Invalid component type!"; + return false; } return true; @@ -324,12 +352,15 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - if (desc_val.isString()) + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); - else if (desc_val.isArray()) { - QString desc{}; - processComponentList(desc_val.toArray(), desc); + } else if (desc_val.isArray()) { + QString desc; + if (!processComponentList(desc_val.toArray(), desc)) + return false; pack.setDescription(desc); + } else { + return false; } } catch (Json::JsonException& e) { From b16085f66c9083a1ec6d0feb08b96ce9173baccd Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 02:05:05 -0400 Subject: [PATCH 019/139] attempt to fix clang-format and ubuntu build. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index ca1a5455c..c2aef06d5 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -26,6 +26,7 @@ #include #include +#include namespace ResourcePackUtils { @@ -236,7 +237,7 @@ bool getBoolOrFromText(const QJsonValue& val, bool& result) } } -bool readFormat(const QJsonObject& obj, TextFormat& format) +bool readFormat(const QJsonObject& obj, TextFormat& format) { auto text = obj.value("text"); auto color = obj.value("color"); @@ -247,7 +248,7 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) auto extra = obj.value("extra"); if (color.isString()) { - // colors can either be a hex code or one of a few text colors + // colors can either be a hex code or one of a few text colors auto col_str = color.toString(); const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); @@ -263,13 +264,11 @@ bool readFormat(const QJsonObject& obj, TextFormat& format) } } - return getBoolOrFromText(bold, format.bold) && - getBoolOrFromText(italic, format.italic) && - getBoolOrFromText(underlined, format.underlined) && - getBoolOrFromText(strikethrough, format.strikethrough); + return getBoolOrFromText(bold, format.bold) && getBoolOrFromText(italic, format.italic) && + getBoolOrFromText(underlined, format.underlined) && getBoolOrFromText(strikethrough, format.strikethrough); } -void appendBeginFormat(TextFormat& format, QString& toAppend) +void appendBeginFormat(TextFormat& format, QString& toAppend) { toAppend.append(""); if (format.bold) @@ -282,7 +281,8 @@ void appendBeginFormat(TextFormat& format, QString& toAppend) toAppend.append(""); } -void appendEndFormat(TextFormat& format, QString& toAppend) { +void appendEndFormat(TextFormat& format, QString& toAppend) +{ toAppend.append(""); if (format.bold) toAppend.append(""); @@ -296,7 +296,8 @@ void appendEndFormat(TextFormat& format, QString& toAppend) { bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); -bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) { +bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) +{ for (const QJsonValue& val : arr) { if (!processComponent(val, result, parentFormat)) @@ -306,7 +307,8 @@ bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* pa return true; } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) { +bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) +{ if (value.isString()) { result.append(value.toString()); } else if (value.isObject()) { @@ -340,9 +342,8 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta -bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { - - +bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) +{ try { auto json_doc = QJsonDocument::fromJson(raw_data); auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); @@ -351,7 +352,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { // description could either be string, or array of dictionaries auto desc_val = pack_obj.value("description"); - + if (desc_val.isString()) { pack.setDescription(desc_val.toString()); } else if (desc_val.isArray()) { From df88ccd4190ad2f605efe96b2b1503bc0ce0d5c8 Mon Sep 17 00:00:00 2001 From: cullvox Date: Mon, 11 Sep 2023 15:49:01 -0400 Subject: [PATCH 020/139] clean up and add review suggestions, links open Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 133 ++++++------------ .../mod/tasks/LocalResourcePackParseTask.h | 3 + 2 files changed, 43 insertions(+), 93 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index c2aef06d5..5b6f6fee0 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -185,87 +185,23 @@ struct TextFormat { bool italic = false; bool underlined = false; bool strikethrough = false; + bool is_linked = false; + QString link_url = ""; }; -bool textColorToHexColor(const QString& text_color, QString& result) -{ - static const QHash text_to_hex = { - { "black", "#000000" }, { "dark_blue", "#0000AA" }, { "dark_green", "#00AA00" }, { "dark_aqua", "#00AAAA" }, - { "dark_red", "#AA0000" }, { "dark_purple", "#AA00AA" }, { "gold", "#FFAA00" }, { "gray", "#AAAAAA" }, - { "dark_gray", "#555555" }, { "blue", "#5555FF" }, { "green", "#55FF55" }, { "aqua", "#55FFFF" }, - { "red", "#FF5555" }, { "light_purple", "#FF55FF" }, { "yellow", "#FFFF55" }, { "white", "#FFFFFF" }, - }; - - auto it = text_to_hex.find(text_color); - - if (it == text_to_hex.end()) { - qWarning() << "Invalid color string in component!"; - result = "#000000"; // return black if color not found - return false; - } - - result = it.value(); - return true; -} - -bool getBoolOrFromText(const QJsonValue& val, bool& result) -{ - if (val.isUndefined()) { - result = false; - return true; - } - - if (val.isBool()) { - result = val.toBool(); - return true; - } else if (val.isString()) { - auto bool_str = val.toString(); - - if (bool_str == "true") { - result = true; - return true; - } else if (bool_str == "false") { - result = false; - return true; - } else { - qWarning() << "Invalid bool value in component!"; - return false; - } - } else { - qWarning() << "Invalid type where bool expected!"; - return false; - } -} - bool readFormat(const QJsonObject& obj, TextFormat& format) { - auto text = obj.value("text"); - auto color = obj.value("color"); - auto bold = obj.value("bold"); - auto italic = obj.value("italic"); - auto underlined = obj.value("underlined"); - auto strikethrough = obj.value("strikethrough"); - auto extra = obj.value("extra"); + format.color = Json::ensureString(obj, "color"); + format.bold = Json::ensureBoolean(obj, "bold", false); + format.italic = Json::ensureBoolean(obj, "italic", false); + format.underlined = Json::ensureBoolean(obj, "underlined", false); + format.strikethrough = Json::ensureBoolean(obj, "strikethrough", false); - if (color.isString()) { - // colors can either be a hex code or one of a few text colors - auto col_str = color.toString(); + auto click_event = Json::ensureObject(obj, "clickEvent"); + format.is_linked = Json::ensureBoolean(click_event, "open_url", false); + format.link_url = Json::ensureString(click_event, "value"); - const QRegularExpression hex_expression("^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$"); - const auto hex_match = hex_expression.match(col_str); - - if (hex_match.hasMatch()) { - format.color = color.toString(); - } else { - if (!textColorToHexColor(color.toString(), format.color)) { - qWarning() << "Invalid color type in component!"; - return false; - } - } - } - - return getBoolOrFromText(bold, format.bold) && getBoolOrFromText(italic, format.italic) && - getBoolOrFromText(underlined, format.underlined) && getBoolOrFromText(strikethrough, format.strikethrough); + return true; } void appendBeginFormat(TextFormat& format, QString& toAppend) @@ -279,35 +215,36 @@ void appendBeginFormat(TextFormat& format, QString& toAppend) toAppend.append(""); if (format.strikethrough) toAppend.append(""); + if (format.is_linked) + toAppend.append(""); } void appendEndFormat(TextFormat& format, QString& toAppend) { - toAppend.append(""); - if (format.bold) - toAppend.append(""); + if (format.is_linked) + toAppend.append(""); + if (format.strikethrough) + toAppend.append(""); if (format.italic) toAppend.append(""); if (format.underlined) toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); + if (format.bold) + toAppend.append(""); + toAppend.append(""); } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat = nullptr); - -bool processComponentList(const QJsonArray& arr, QString& result, TextFormat* parentFormat = nullptr) +bool processComponentList(const QJsonArray& arr, QString& result) { - for (const QJsonValue& val : arr) { - if (!processComponent(val, result, parentFormat)) + if (!processComponent(val, result)) return false; } return true; } -bool processComponent(const QJsonValue& value, QString& result, TextFormat* parentFormat) +bool processComponent(const QJsonValue& value, QString& result) { if (value.isString()) { result.append(value.toString()); @@ -315,8 +252,6 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare auto obj = value.toObject(); TextFormat format{}; - if (parentFormat) - format = *parentFormat; if (!readFormat(obj, format)) return false; @@ -329,7 +264,7 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare auto extra = obj.value("extra"); if (extra.isArray()) - if (!processComponentList(extra.toArray(), result, &format)) + if (!processComponentList(extra.toArray(), result)) return false; appendEndFormat(format, result); @@ -342,6 +277,7 @@ bool processComponent(const QJsonValue& value, QString& result, TextFormat* pare } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta +// https://minecraft.fandom.com/wiki/Raw_JSON_text_format#Plain_Text bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) { try { @@ -350,20 +286,31 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - // description could either be string, or array of dictionaries + // description could be many things according to minecraft auto desc_val = pack_obj.value("description"); + QString desc; if (desc_val.isString()) { - pack.setDescription(desc_val.toString()); + desc = desc_val.toString(); } else if (desc_val.isArray()) { - QString desc; if (!processComponentList(desc_val.toArray(), desc)) return false; - pack.setDescription(desc); + } else if (desc_val.isObject()) { + if (!processComponent(desc_val, desc)) + return false; + } else if (desc_val.isBool()) { + desc = desc_val.toBool() ? "true" : "false"; + } else if (desc_val.isDouble()) { + desc = QString::number(desc_val.toDouble()); } else { + qWarning() << "Invalid description type!"; return false; } + qInfo() << desc; + + pack.setDescription(desc); + } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); return false; diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 5199bf3f0..ed3317643 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,6 +34,9 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); + +bool processComponent(const QJsonValue& value, QString& result); +bool processComponentList(const QJsonArray& value, QString& result); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); From a4e65305139dc5cdc7ad246b3203175a21262050 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:34:42 -0400 Subject: [PATCH 021/139] added tests, fixed issues with overriding/format In the documentation it states that child values can override the parent values. Originally this code did not support that but now it does. Also added in testing inspired by the previous tests. Signed-off-by: cullvox --- .../mod/tasks/LocalResourcePackParseTask.cpp | 217 ++++++++++-------- .../mod/tasks/LocalResourcePackParseTask.h | 4 +- tests/CMakeLists.txt | 3 + tests/MetaComponentParse_test.cpp | 108 +++++++++ .../MetaComponentParse/component_basic.json | 4 + .../component_with_extra.json | 18 ++ .../component_with_format.json | 13 ++ .../component_with_link.json | 12 + .../component_with_mixed.json | 40 ++++ 9 files changed, 321 insertions(+), 98 deletions(-) create mode 100644 tests/MetaComponentParse_test.cpp create mode 100644 tests/testdata/MetaComponentParse/component_basic.json create mode 100644 tests/testdata/MetaComponentParse/component_with_extra.json create mode 100644 tests/testdata/MetaComponentParse/component_with_format.json create mode 100644 tests/testdata/MetaComponentParse/component_with_link.json create mode 100644 tests/testdata/MetaComponentParse/component_with_mixed.json diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 5b6f6fee0..31cfdca04 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,96 +179,138 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormat { - QString color = "#000000"; - bool bold = false; - bool italic = false; - bool underlined = false; - bool strikethrough = false; - bool is_linked = false; - QString link_url = ""; -}; +struct TextFormatter { + // left is value, right is if the value was explicitly written + QPair color = { "#000000", false }; + QPair bold = { false, false }; + QPair italic = { false, false }; + QPair underlined = { false, false }; + QPair strikethrough = { false, false }; + QPair is_linked = { false, false }; + QPair link_url = { "", false }; -bool readFormat(const QJsonObject& obj, TextFormat& format) -{ - format.color = Json::ensureString(obj, "color"); - format.bold = Json::ensureBoolean(obj, "bold", false); - format.italic = Json::ensureBoolean(obj, "italic", false); - format.underlined = Json::ensureBoolean(obj, "underlined", false); - format.strikethrough = Json::ensureBoolean(obj, "strikethrough", false); + void setColor(const QString& new_color, bool written) { color = { new_color, written}; } + void setBold(bool new_bold, bool written) { bold = { new_bold, written}; } + void setItalic(bool new_italic, bool written) { italic = { new_italic, written}; } + void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written}; } + void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written}; } + void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written}; } + void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written}; } - auto click_event = Json::ensureObject(obj, "clickEvent"); - format.is_linked = Json::ensureBoolean(click_event, "open_url", false); - format.link_url = Json::ensureString(click_event, "value"); - - return true; -} - -void appendBeginFormat(TextFormat& format, QString& toAppend) -{ - toAppend.append(""); - if (format.bold) - toAppend.append(""); - if (format.italic) - toAppend.append(""); - if (format.underlined) - toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); - if (format.is_linked) - toAppend.append(""); -} - -void appendEndFormat(TextFormat& format, QString& toAppend) -{ - if (format.is_linked) - toAppend.append(""); - if (format.strikethrough) - toAppend.append(""); - if (format.italic) - toAppend.append(""); - if (format.underlined) - toAppend.append(""); - if (format.bold) - toAppend.append(""); - toAppend.append(""); -} - -bool processComponentList(const QJsonArray& arr, QString& result) -{ - for (const QJsonValue& val : arr) { - if (!processComponent(val, result)) - return false; + void overrideFrom(const TextFormatter& child) + { + if (child.color.second) + color.first = child.color.first; + if (child.bold.second) + bold.first = child.bold.first; + if (child.italic.second) + italic.first = child.italic.first; + if (child.underlined.second) + underlined.first = child.underlined.first; + if (child.strikethrough.second) + strikethrough.first = child.strikethrough.first; + if (child.is_linked.second) + is_linked.first = child.is_linked.first; + if (child.link_url.second) + link_url.first = child.link_url.first; } - return true; -} + QString format(QString text) + { + if (text.isEmpty()) + return QString(); -bool processComponent(const QJsonValue& value, QString& result) + QString result; + + if (color.first != "#000000") + result.append(""); + if (bold.first) + result.append(""); + if (italic.first) + result.append(""); + if (underlined.first) + result.append(""); + if (strikethrough.first) + result.append(""); + if (is_linked.first) + result.append(""); + + result.append(text); + + if (is_linked.first) + result.append(""); + if (strikethrough.first) + result.append(""); + if (underlined.first) + result.append(""); + if (italic.first) + result.append(""); + if (bold.first) + result.append(""); + if (color.first != "#000000") + result.append(""); + + return result; + } + + bool readFormat(const QJsonObject& obj) + { + setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); + setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); + setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); + setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); + setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); + + auto click_event = Json::ensureObject(obj, "clickEvent"); + setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); + setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); + + return true; + } +}; + +bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) { + TextFormatter formatter; + + if (parentFormat) + formatter = *parentFormat; + if (value.isString()) { - result.append(value.toString()); - } else if (value.isObject()) { + result.append(formatter.format(value.toString())); + } else if (value.isBool()) { + result.append(formatter.format(value.toBool() ? "true" : "false")); + } else if (value.isDouble()) { + result.append(formatter.format(QString::number(value.toDouble()))); + } else if (value.isObject()) { auto obj = value.toObject(); - TextFormat format{}; - - if (!readFormat(obj, format)) + if (not formatter.readFormat(obj)) return false; - - appendBeginFormat(format, result); - - auto text = obj.value("text"); - if (text.isString()) - result.append(text.toString()); + // override the parent format with our new one + TextFormatter mixed; + if (parentFormat) + mixed = *parentFormat; + + mixed.overrideFrom(formatter); + + result.append(mixed.format(Json::ensureString(obj, "text"))); + + // process any 'extra' children with this format auto extra = obj.value("extra"); - if (extra.isArray()) - if (!processComponentList(extra.toArray(), result)) - return false; + if (not extra.isUndefined()) + return processComponent(extra, result, &mixed); - appendEndFormat(format, result); - } else { + } else if (value.isArray()) { + auto array = value.toArray(); + + for (const QJsonValue& current : array) { + if (not processComponent(current, result, parentFormat)) { + return false; + } + } + } else { qWarning() << "Invalid component type!"; return false; } @@ -286,29 +328,12 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - // description could be many things according to minecraft auto desc_val = pack_obj.value("description"); - - QString desc; - if (desc_val.isString()) { - desc = desc_val.toString(); - } else if (desc_val.isArray()) { - if (!processComponentList(desc_val.toArray(), desc)) - return false; - } else if (desc_val.isObject()) { - if (!processComponent(desc_val, desc)) - return false; - } else if (desc_val.isBool()) { - desc = desc_val.toBool() ? "true" : "false"; - } else if (desc_val.isDouble()) { - desc = QString::number(desc_val.toDouble()); - } else { - qWarning() << "Invalid description type!"; + QString desc{}; + if (not processComponent(desc_val, desc)) return false; - } qInfo() << desc; - pack.setDescription(desc); } catch (Json::JsonException& e) { diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index ed3317643..7e893979a 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -35,8 +35,8 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Ful bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -bool processComponent(const QJsonValue& value, QString& result); -bool processComponentList(const QJsonArray& value, QString& result); +struct TextFormatter; +bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a26a49fec..8690c078e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,3 +56,6 @@ ecm_add_test(Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}: ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Version) + +ecm_add_test(MetaComponentParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test + TEST_NAME MetaComponentParse) \ No newline at end of file diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp new file mode 100644 index 000000000..77b49b478 --- /dev/null +++ b/tests/MetaComponentParse_test.cpp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +class MetaComponentParseTest : public QObject { + Q_OBJECT + + void doTest(QString name) + { + QString source = QFINDTESTDATA("testdata/MetaComponentParse"); + + QString comp_rp = FS::PathCombine(source, name); + + QFile file; + file.setFileName(comp_rp); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + QString data = file.readAll(); + file.close(); + + QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8()); + QJsonObject obj = doc.object(); + + QJsonValue description_json = obj.value("description"); + QJsonValue expected_json = obj.value("expected_output"); + + QVERIFY(description_json.isUndefined() == false); + QVERIFY(expected_json.isString() == true); + + QString expected = expected_json.toString(); + + QString processed; + bool valid = ResourcePackUtils::processComponent(description_json, processed); + + QVERIFY(processed == expected); + QVERIFY(valid == true); + } + +private slots: + void test_parseComponentBasic() + { + doTest("component_basic.json"); + } + + void test_parseComponentWithFormat() + { + doTest("component_with_format.json"); + } + + void test_parseComponentWithExtra() + { + doTest("component_with_extra.json"); + } + + void test_parseComponentWithLink() + { + doTest("component_with_link.json"); + } + + void test_parseComponentWithMixed() + { + doTest("component_with_mixed.json"); + } +}; + +QTEST_GUILESS_MAIN(MetaComponentParseTest) + +#include "MetaComponentParse_test.moc" diff --git a/tests/testdata/MetaComponentParse/component_basic.json b/tests/testdata/MetaComponentParse/component_basic.json new file mode 100644 index 000000000..5cfdeeeab --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_basic.json @@ -0,0 +1,4 @@ +{ + "description": [{"text": "Hello, Component!"}], + "expected_output": "Hello, Component!" +} diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json new file mode 100644 index 000000000..49397556d --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -0,0 +1,18 @@ +{ + "description": [ + { + "text": "Hello, ", + "color": "red", + "bold": true, + "italic": true, + "extra": [ + { + "extra": "Component!", + "bold": false, + "italic": false + } + ] + } + ], + "expected_output": "Hello, Component!" +} \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json new file mode 100644 index 000000000..000de20cb --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -0,0 +1,13 @@ +{ + "description": [ + { + "text": "Hello, Component!", + "color": "blue", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true + } + ], + "expected_output": "Hello, Component!" +} \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json new file mode 100644 index 000000000..e190cc5e8 --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -0,0 +1,12 @@ +{ + "description": [ + { + "text": "Hello, Component!", + "clickEvent": { + "open_url": true, + "value": "https://google.com" + } + } + ], + "expected_output": "Hello, Component!" +} diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json new file mode 100644 index 000000000..7e65169af --- /dev/null +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -0,0 +1,40 @@ +{ + "description": [ + { + "text": "The quick ", + "color": "blue", + "italic": true + }, + { + "text": "brown fox ", + "color": "#873600", + "bold": true, + "underlined": true, + "extra": { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + }, + { + "text": "the lazy dog's back. ", + "color": "green", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true, + "extra": [ + { + "text": "1234567890 ", + "color": "black", + "strikethrough": false, + "extra": "How vexingly quick daft zebras jump!" + } + ] + } + ], + "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" +} From ddf0c28b1b0b6617b0308405b4d5eefd20ff48b2 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:45:29 -0400 Subject: [PATCH 022/139] clang-format fixes --- .../mod/tasks/LocalResourcePackParseTask.cpp | 22 ++++++------ .../mod/tasks/LocalResourcePackParseTask.h | 1 - tests/MetaComponentParse_test.cpp | 35 +++++-------------- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 31cfdca04..8e209a416 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -189,18 +189,18 @@ struct TextFormatter { QPair is_linked = { false, false }; QPair link_url = { "", false }; - void setColor(const QString& new_color, bool written) { color = { new_color, written}; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written}; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written}; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written}; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written}; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written}; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written}; } + void setColor(const QString& new_color, bool written) { color = { new_color, written }; } + void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } + void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } + void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } + void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } + void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } + void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } void overrideFrom(const TextFormatter& child) { if (child.color.second) - color.first = child.color.first; + color.first = child.color.first; if (child.bold.second) bold.first = child.bold.first; if (child.italic.second) @@ -282,7 +282,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat result.append(formatter.format(value.toBool() ? "true" : "false")); } else if (value.isDouble()) { result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { + } else if (value.isObject()) { auto obj = value.toObject(); if (not formatter.readFormat(obj)) @@ -294,7 +294,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat mixed = *parentFormat; mixed.overrideFrom(formatter); - + result.append(mixed.format(Json::ensureString(obj, "text"))); // process any 'extra' children with this format @@ -310,7 +310,7 @@ bool processComponent(const QJsonValue& value, QString& result, const TextFormat return false; } } - } else { + } else { qWarning() << "Invalid component type!"; return false; } diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 7e893979a..945499a91 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,7 +34,6 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); - struct TextFormatter; bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 77b49b478..1b1e2ce3e 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -33,11 +33,11 @@ * limitations under the License. */ -#include -#include #include #include #include +#include +#include #include @@ -76,31 +76,12 @@ class MetaComponentParseTest : public QObject { QVERIFY(valid == true); } -private slots: - void test_parseComponentBasic() - { - doTest("component_basic.json"); - } - - void test_parseComponentWithFormat() - { - doTest("component_with_format.json"); - } - - void test_parseComponentWithExtra() - { - doTest("component_with_extra.json"); - } - - void test_parseComponentWithLink() - { - doTest("component_with_link.json"); - } - - void test_parseComponentWithMixed() - { - doTest("component_with_mixed.json"); - } + private slots: + void test_parseComponentBasic() { doTest("component_basic.json"); } + void test_parseComponentWithFormat() { doTest("component_with_format.json"); } + void test_parseComponentWithExtra() { doTest("component_with_extra.json"); } + void test_parseComponentWithLink() { doTest("component_with_link.json"); } + void test_parseComponentWithMixed() { doTest("component_with_mixed.json"); } }; QTEST_GUILESS_MAIN(MetaComponentParseTest) From e1dda6c005dab379a5b93a2a9279205895b71060 Mon Sep 17 00:00:00 2001 From: cullvox Date: Tue, 12 Sep 2023 21:50:33 -0400 Subject: [PATCH 023/139] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: ddf0c28b1b0b6617b0308405b4d5eefd20ff48b2 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 8e209a416..5e53244ff 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -333,7 +333,6 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) if (not processComponent(desc_val, desc)) return false; - qInfo() << desc; pack.setDescription(desc); } catch (Json::JsonException& e) { From ee7016fa542e3ca92bdd691bd2e168d9d43a02c2 Mon Sep 17 00:00:00 2001 From: cullvox Date: Wed, 13 Sep 2023 14:28:56 -0400 Subject: [PATCH 024/139] use clang-format Signed-off-by: cullvox Hello, Component!" + ], + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 000de20cb..00dfc7daf 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -1,13 +1,13 @@ { - "description": [ - { - "text": "Hello, Component!", - "color": "blue", - "bold": true, - "italic": true, - "underlined": true, - "strikethrough": true - } - ], - "expected_output": "Hello, Component!" + "description": [ + { + "text": "Hello, Component!", + "color": "blue", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true + } + ], + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index e190cc5e8..b1e34c7d6 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -1,12 +1,12 @@ { - "description": [ - { - "text": "Hello, Component!", - "clickEvent": { - "open_url": true, - "value": "https://google.com" - } - } - ], - "expected_output": "Hello, Component!" + "description": [ + { + "text": "Hello, Component!", + "clickEvent": { + "open_url": true, + "value": "https://google.com" + } + } + ], + "expected_output": "Hello, Component!" } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7e65169af..7c8c5b032 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -1,40 +1,41 @@ { - "description": [ - { - "text": "The quick ", - "color": "blue", - "italic": true - }, - { - "text": "brown fox ", - "color": "#873600", - "bold": true, - "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } - }, - { - "text": "the lazy dog's back. ", - "color": "green", - "bold": true, - "italic": true, - "underlined": true, - "strikethrough": true, - "extra": [ + "description": [ { - "text": "1234567890 ", - "color": "black", - "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "text": "The quick ", + "color": "blue", + "italic": true + }, + { + "text": "brown fox ", + "color": "#873600", + "bold": true, + "underlined": true, + "extra": { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + }, + { + "text": "the lazy dog's back. ", + "color": "green", + "bold": true, + "italic": true, + "underlined": true, + "strikethrough": true, + "extra": [ + { + "text": "1234567890 ", + "color": "black", + "strikethrough": false, + "extra": "How vexingly quick daft zebras jump!" + } + ] } - ] - } - ], - "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + ], + "expected_output": + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } From e96d0b6cafb0a0c17e7f5baef1bfbafe4a7fcb07 Mon Sep 17 00:00:00 2001 From: cullvox Date: Wed, 13 Sep 2023 14:31:04 -0400 Subject: [PATCH 025/139] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: ee7016fa542e3ca92bdd691bd2e168d9d43a02c2 Signed-off-by: cullvox --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index 5e53244ff..fd28aa0b7 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -272,7 +272,6 @@ struct TextFormatter { bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) { TextFormatter formatter; - if (parentFormat) formatter = *parentFormat; From c81689d39322d5ea5c7ed0073d73e4d8d2446b33 Mon Sep 17 00:00:00 2001 From: cullvox Date: Fri, 15 Sep 2023 20:41:21 -0400 Subject: [PATCH 026/139] fixes html elide issue with InfoFrame --- launcher/ui/widgets/InfoFrame.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 1f03f9eaf..0162b4000 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "InfoFrame.h" #include "ui_InfoFrame.h" @@ -274,12 +275,31 @@ void InfoFrame::setDescription(QString text) } QString labeltext; labeltext.reserve(300); - if (finaltext.length() > 290) { + + + // elide rich text by getting characters without formatting + const int maxCharacterElide = 290; + QTextDocument doc; + doc.setHtml(text); + + if (doc.characterCount() > maxCharacterElide) { + ui->descriptionLabel->setOpenExternalLinks(false); - ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); + ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - // This allows injecting HTML here. - labeltext.append("" + finaltext.left(287) + "..."); + + const QString elidedPostfix = "..."; + + // move the cursor to the character elide, doesn't see html + QTextCursor cursor(&doc); + cursor.movePosition(QTextCursor::End); + cursor.setPosition(maxCharacterElide, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + + // insert the post fix at the cursor + cursor.insertHtml(elidedPostfix); + + labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); } else { ui->descriptionLabel->setTextFormat(Qt::TextFormat::AutoText); From 4053229544ca2d80cd569f4d67365f81ed64353d Mon Sep 17 00:00:00 2001 From: cullvox Date: Fri, 15 Sep 2023 20:55:34 -0400 Subject: [PATCH 027/139] DCO Remediation Commit for cullvox I, cullvox , hereby add my Signed-off-by to this commit: c81689d39322d5ea5c7ed0073d73e4d8d2446b33 Signed-off-by: cullvox --- launcher/ui/widgets/InfoFrame.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 0162b4000..6423e88d6 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -36,8 +36,9 @@ #include #include -#include #include +#include +#include #include "InfoFrame.h" #include "ui_InfoFrame.h" @@ -276,16 +277,14 @@ void InfoFrame::setDescription(QString text) QString labeltext; labeltext.reserve(300); - // elide rich text by getting characters without formatting const int maxCharacterElide = 290; QTextDocument doc; doc.setHtml(text); if (doc.characterCount() > maxCharacterElide) { - ui->descriptionLabel->setOpenExternalLinks(false); - ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. + ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; const QString elidedPostfix = "..."; @@ -298,7 +297,7 @@ void InfoFrame::setDescription(QString text) // insert the post fix at the cursor cursor.insertHtml(elidedPostfix); - + labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); } else { From 01e98a6ce810ed1e8169d2252dca5cec39f8d335 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 16 Sep 2023 10:20:24 +0300 Subject: [PATCH 028/139] simplify the raw json parsing Signed-off-by: Trial97 Fixed Tests Signed-off-by: Trial97 --- .../mod/tasks/LocalResourcePackParseTask.cpp | 214 +++++++----------- .../mod/tasks/LocalResourcePackParseTask.h | 3 +- launcher/ui/widgets/InfoFrame.cpp | 6 +- tests/MetaComponentParse_test.cpp | 10 +- .../component_with_extra.json | 7 +- .../component_with_format.json | 2 +- .../component_with_link.json | 2 +- .../component_with_mixed.json | 24 +- 8 files changed, 104 insertions(+), 164 deletions(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index fd28aa0b7..d9d26b9c2 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,142 +179,85 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormatter { - // left is value, right is if the value was explicitly written - QPair color = { "#000000", false }; - QPair bold = { false, false }; - QPair italic = { false, false }; - QPair underlined = { false, false }; - QPair strikethrough = { false, false }; - QPair is_linked = { false, false }; - QPair link_url = { "", false }; - - void setColor(const QString& new_color, bool written) { color = { new_color, written }; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } - - void overrideFrom(const TextFormatter& child) - { - if (child.color.second) - color.first = child.color.first; - if (child.bold.second) - bold.first = child.bold.first; - if (child.italic.second) - italic.first = child.italic.first; - if (child.underlined.second) - underlined.first = child.underlined.first; - if (child.strikethrough.second) - strikethrough.first = child.strikethrough.first; - if (child.is_linked.second) - is_linked.first = child.is_linked.first; - if (child.link_url.second) - link_url.first = child.link_url.first; - } - - QString format(QString text) - { - if (text.isEmpty()) - return QString(); - - QString result; - - if (color.first != "#000000") - result.append(""); - if (bold.first) - result.append(""); - if (italic.first) - result.append(""); - if (underlined.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (is_linked.first) - result.append(""); - - result.append(text); - - if (is_linked.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (underlined.first) - result.append(""); - if (italic.first) - result.append(""); - if (bold.first) - result.append(""); - if (color.first != "#000000") - result.append(""); - - return result; - } - - bool readFormat(const QJsonObject& obj) - { - setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); - setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); - setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); - setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); - setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); - - auto click_event = Json::ensureObject(obj, "clickEvent"); - setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); - setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); - - return true; - } -}; - -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) +QString buildStyle(const QJsonObject& obj) { - TextFormatter formatter; - if (parentFormat) - formatter = *parentFormat; - - if (value.isString()) { - result.append(formatter.format(value.toString())); - } else if (value.isBool()) { - result.append(formatter.format(value.toBool() ? "true" : "false")); - } else if (value.isDouble()) { - result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { - auto obj = value.toObject(); - - if (not formatter.readFormat(obj)) - return false; - - // override the parent format with our new one - TextFormatter mixed; - if (parentFormat) - mixed = *parentFormat; - - mixed.overrideFrom(formatter); - - result.append(mixed.format(Json::ensureString(obj, "text"))); - - // process any 'extra' children with this format - auto extra = obj.value("extra"); - if (not extra.isUndefined()) - return processComponent(extra, result, &mixed); - - } else if (value.isArray()) { - auto array = value.toArray(); - - for (const QJsonValue& current : array) { - if (not processComponent(current, result, parentFormat)) { - return false; - } + QStringList styles; + if (auto color = Json::ensureString(obj, "color"); !color.isEmpty()) { + styles << QString("color: %1;").arg(color); + } + if (obj.contains("bold")) { + QString weight = "normal"; + if (Json::ensureBoolean(obj, "bold", false)) { + weight = "bold"; } - } else { - qWarning() << "Invalid component type!"; - return false; + styles << QString("font-weight: %1;").arg(weight); + } + if (obj.contains("italic")) { + QString style = "normal"; + if (Json::ensureBoolean(obj, "italic", false)) { + style = "italic"; + } + styles << QString("font-style: %1;").arg(style); } - return true; + return styles.isEmpty() ? "" : QString("style=\"%1\"").arg(styles.join(" ")); +} + +QString processComponent(const QJsonArray& value, bool strikethrough, bool underline) +{ + QString result; + for (auto current : value) + result += processComponent(current, strikethrough, underline); + return result; +} + +QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline) +{ + underline = Json::ensureBoolean(obj, "underlined", underline); + strikethrough = Json::ensureBoolean(obj, "strikethrough", strikethrough); + + QString result = Json::ensureString(obj, "text"); + if (underline) { + result = QString("%1").arg(result); + } + if (strikethrough) { + result = QString("%1").arg(result); + } + // the extra needs to be a array + result += processComponent(Json::ensureArray(obj, "extra"), strikethrough, underline); + if (auto style = buildStyle(obj); !style.isEmpty()) { + result = QString("%2").arg(style, result); + } + if (obj.contains("clickEvent")) { + auto click_event = Json::ensureObject(obj, "clickEvent"); + auto action = Json::ensureString(click_event, "action"); + auto value = Json::ensureString(click_event, "value"); + if (action == "open_url" && !value.isEmpty()) { + result = QString("%2").arg(value, result); + } + } + return result; +} + +QString processComponent(const QJsonValue& value, bool strikethrough, bool underline) +{ + if (value.isString()) { + return value.toString(); + } + if (value.isBool()) { + return value.toBool() ? "true" : "false"; + } + if (value.isDouble()) { + return QString::number(value.toDouble()); + } + if (value.isArray()) { + return processComponent(value.toArray(), strikethrough, underline); + } + if (value.isObject()) { + return processComponent(value.toObject(), strikethrough, underline); + } + qWarning() << "Invalid component type!"; + return {}; } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta @@ -327,12 +270,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - auto desc_val = pack_obj.value("description"); - QString desc{}; - if (not processComponent(desc_val, desc)) - return false; - - pack.setDescription(desc); + pack.setDescription(processComponent(pack_obj.value("description"))); } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 945499a91..97bf7b2ba 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,8 +34,7 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -struct TextFormatter; -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); +QString processComponent(const QJsonValue& value, bool strikethrough = false, bool underline = false); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 6423e88d6..2cf3e7a93 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -287,8 +287,6 @@ void InfoFrame::setDescription(QString text) ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - const QString elidedPostfix = "..."; - // move the cursor to the character elide, doesn't see html QTextCursor cursor(&doc); cursor.movePosition(QTextCursor::End); @@ -296,7 +294,7 @@ void InfoFrame::setDescription(QString text) cursor.removeSelectedText(); // insert the post fix at the cursor - cursor.insertHtml(elidedPostfix); + cursor.insertHtml("..."); labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); @@ -335,7 +333,7 @@ void InfoFrame::setLicense(QString text) if (finaltext.length() > 290) { ui->licenseLabel->setOpenExternalLinks(false); ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText); - m_description = text; + m_license = text; // This allows injecting HTML here. labeltext.append("" + finaltext.left(287) + "..."); QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 1b1e2ce3e..9979a9fa6 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -64,16 +64,14 @@ class MetaComponentParseTest : public QObject { QJsonValue description_json = obj.value("description"); QJsonValue expected_json = obj.value("expected_output"); - QVERIFY(description_json.isUndefined() == false); - QVERIFY(expected_json.isString() == true); + QVERIFY(!description_json.isUndefined()); + QVERIFY(expected_json.isString()); QString expected = expected_json.toString(); - QString processed; - bool valid = ResourcePackUtils::processComponent(description_json, processed); + QString processed = ResourcePackUtils::processComponent(description_json); - QVERIFY(processed == expected); - QVERIFY(valid == true); + QCOMPARE(processed, expected); } private slots: diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json index e26b2abff..887becdbe 100644 --- a/tests/testdata/MetaComponentParse/component_with_extra.json +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -7,12 +7,15 @@ "italic": true, "extra": [ { - "extra": "Component!", + "extra": [ + "Component!" + ], "bold": false, "italic": false } ] } ], - "expected_output": "Hello, Component!" + "expected_output": + "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 00dfc7daf..1078886a6 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -9,5 +9,5 @@ "strikethrough": true } ], - "expected_output": "Hello, Component!" + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index b1e34c7d6..188c004cd 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -3,7 +3,7 @@ { "text": "Hello, Component!", "clickEvent": { - "open_url": true, + "action": "open_url", "value": "https://google.com" } } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7c8c5b032..661fc1a3e 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -10,14 +10,16 @@ "color": "#873600", "bold": true, "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } + "extra": [ + { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + ] }, { "text": "the lazy dog's back. ", @@ -31,11 +33,13 @@ "text": "1234567890 ", "color": "black", "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "extra": [ + "How vexingly quick daft zebras jump!" + ] } ] } ], "expected_output": - "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } From 0e41ceffc4a2b8842e44a8bd1dc027944ce27471 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 16 Sep 2023 19:18:58 +0300 Subject: [PATCH 029/139] removed missed header Signed-off-by: Trial97 --- launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index d9d26b9c2..c5d899123 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -26,7 +26,6 @@ #include #include -#include namespace ResourcePackUtils { From ab725eeb1816aea428107dcc9ed9184136c7f766 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 11:52:28 +0300 Subject: [PATCH 030/139] corected side and added loaders Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.cpp | 19 ++++++++++++++++++- launcher/modplatform/ModIndex.h | 4 +++- launcher/modplatform/flame/FlameModIndex.cpp | 6 ++++++ launcher/modplatform/modrinth/ModrinthAPI.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 19 ++++++++++++++++++- launcher/modplatform/packwiz/Packwiz.h | 1 + 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index fc79dff15..5b7624563 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -117,7 +117,7 @@ QString getMetaURL(ResourceProvider provider, QVariant projectID) projectID.toString(); } -auto getModLoaderString(ModLoaderType type) -> const QString +auto getModLoaderAsString(ModLoaderType type) -> const QString { switch (type) { case NeoForge: @@ -138,4 +138,21 @@ auto getModLoaderString(ModLoaderType type) -> const QString return ""; } +auto getModLoaderFromString(QString type) -> const ModLoaderType +{ + if (type == "neoforge") + return NeoForge; + if (type == "forge") + return Forge; + if (type == "cauldron") + return Cauldron; + if (type == "liteloader") + return LiteLoader; + if (type == "fabric") + return Fabric; + if (type == "quilt") + return Quilt; + return {}; +} + } // namespace ModPlatform diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 72294c399..940dddea0 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -109,6 +109,7 @@ struct IndexedVersion { bool is_preferred = true; QString changelog; QList dependencies; + QString side; // this is for flame API // For internal use, not provided by APIs bool is_currently_selected = false; @@ -181,7 +182,8 @@ inline auto getOverrideDeps() -> QList QString getMetaURL(ResourceProvider provider, QVariant projectID); -auto getModLoaderString(ModLoaderType type) -> const QString; +auto getModLoaderAsString(ModLoaderType type) -> const QString; +auto getModLoaderFromString(QString type) -> const ModLoaderType; constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 345883c17..75c10d6f5 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -130,6 +130,12 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.loaders |= ModPlatform::Fabric; if (loader == "quilt") file.loaders |= ModPlatform::Quilt; + if (loader == "server" || loader == "client") { + if (file.side.isEmpty()) + file.side = loader; + else if (file.side != loader) + file.side = "both"; + } } file.addonId = Json::requireInteger(obj, "modId"); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index d0f0811b2..857719902 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -41,7 +41,7 @@ class ModrinthAPI : public NetworkResourceAPI { for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader }) { if (types & loader) { - l << getModLoaderString(loader); + l << getModLoaderAsString(loader); } } return l; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index e35567f24..90d7d0ed7 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -113,7 +113,8 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.provider = mod_pack.provider; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; - mod.side = stringToSide(mod_pack.side); + mod.side = stringToSide(mod_version.side.isEmpty() ? mod_pack.side : mod_version.side); + mod.loaders = mod_version.loaders; return mod; } @@ -181,6 +182,14 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) break; } + toml::array loaders; + for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, ModPlatform::Fabric, + ModPlatform::Quilt }) { + if (mod.loaders & loader) { + loaders.push_back(getModLoaderAsString(loader)); + } + } + if (!index_file.open(QIODevice::ReadWrite)) { qCritical() << QString("Could not open file %1!").arg(normalized_fname); return; @@ -192,6 +201,7 @@ 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() }, + { "loader", loaders }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -276,6 +286,13 @@ 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")); + if (auto loaders = table["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(""))); + } + } + } } { // [download] info diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index dce198b0e..a61bb0503 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -41,6 +41,7 @@ class V1 { QString name{}; QString filename{}; Side side{ Side::UniversalSide }; + ModPlatform::ModLoaderTypes loaders; // [download] QString mode{}; From 749975e8ef96ba780aa910217911ff0af6ecbe5b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 15 Oct 2023 12:46:23 +0300 Subject: [PATCH 031/139] made side and loaders visible to user Signed-off-by: Trial97 --- launcher/minecraft/mod/MetadataHandler.h | 2 ++ launcher/minecraft/mod/Mod.cpp | 29 +++++++++++++++ launcher/minecraft/mod/Mod.h | 3 ++ launcher/minecraft/mod/ModFolderModel.cpp | 35 ++++++++++++++++--- launcher/minecraft/mod/ModFolderModel.h | 12 ++++++- launcher/minecraft/mod/Resource.h | 2 +- .../modrinth/ModrinthCheckUpdate.cpp | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 4 +-- 8 files changed, 79 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 3496da2a0..ccdd7d559 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -52,4 +52,6 @@ class Metadata { static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); } static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } + + static auto modSideToString(ModSide side) -> QString { return Packwiz::V1::sideToString(side); } }; diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 310946379..132f99f7d 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -45,6 +45,7 @@ #include "MetadataHandler.h" #include "Version.h" #include "minecraft/mod/ModDetails.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" static ModPlatform::ProviderCapabilities ProviderCaps; @@ -109,6 +110,20 @@ std::pair Mod::compare(const Resource& other, SortType type) const return { compare_result, type == SortType::PROVIDER }; break; } + case SortType::SIDE: { + if (side() > cast_other->side()) + return { 1, type == SortType::SIDE }; + else if (side() < cast_other->side()) + return { -1, type == SortType::SIDE }; + break; + } + case SortType::LOADERS: { + if (loaders() > cast_other->loaders()) + return { 1, type == SortType::LOADERS }; + else if (loaders() < cast_other->loaders()) + return { -1, type == SortType::LOADERS }; + break; + } } return { 0, false }; } @@ -232,6 +247,20 @@ auto Mod::provider() const -> std::optional return {}; } +auto Mod::side() const -> Metadata::ModSide +{ + if (metadata()) + return metadata()->side; + return Metadata::ModSide::UniversalSide; +} + +auto Mod::loaders() const -> ModPlatform::ModLoaderTypes +{ + if (metadata()) + return metadata()->loaders; + return {}; +} + auto Mod::licenses() const -> const QList& { return details().licenses; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index e97ee9d3b..970f85a00 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -47,6 +47,7 @@ #include "ModDetails.h" #include "Resource.h" +#include "modplatform/ModIndex.h" class Mod : public Resource { Q_OBJECT @@ -70,6 +71,8 @@ class Mod : public Resource { auto licenses() const -> const QList&; auto issueTracker() const -> QString; auto metaurl() const -> QString; + auto side() const -> Metadata::ModSide; + auto loaders() const -> ModPlatform::ModLoaderTypes; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index a5f1489dd..631425a72 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,6 +52,8 @@ #include "Application.h" #include "Json.h" +#include "minecraft/mod/MetadataHandler.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" @@ -62,12 +64,15 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; + m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders" }); + m_column_names_translated = QStringList( + { tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Side"), tr("Loaders") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, + SortType::DATE, SortType::PROVIDER, SortType::SIDE, SortType::LOADERS }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -105,6 +110,20 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } + case SideColumn: { + return Metadata::modSideToString(at(row)->side()); + } + case LoadersColumn: { + QStringList loaders; + auto modLoaders = at(row)->loaders(); + for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, + ModPlatform::Fabric, ModPlatform::Quilt }) { + if (modLoaders & loader) { + loaders << getModLoaderAsString(loader); + } + } + return loaders.join(","); + } default: return QVariant(); } @@ -154,6 +173,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case DateColumn: case ProviderColumn: case ImageColumn: + case SideColumn: + case LoadersColumn: return columnNames().at(section); default: return QVariant(); @@ -171,6 +192,10 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The date and time this mod was last changed (or added)."); case ProviderColumn: return tr("Where the mod was downloaded from."); + case SideColumn: + return tr("On what environment the mod is running."); + case LoadersColumn: + return tr("The mod loader."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 61d840f9b..1623fadfe 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -61,7 +61,17 @@ class QFileSystemWatcher; class ModFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; + enum Columns { + ActiveColumn = 0, + ImageColumn, + NameColumn, + VersionColumn, + DateColumn, + ProviderColumn, + SideColumn, + LoadersColumn, + NUM_COLUMNS + }; enum ModStatusAction { Disable, Enable, Toggle }; ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c1ed49461..eefb5f8a5 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 9b7c53854..7d491f4fe 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -114,7 +114,7 @@ void ModrinthCheckUpdate::executeTask() ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt }; for (auto flag : flags) { if (m_loaders.value().testFlag(flag)) { - loader_filter = ModPlatform::getModLoaderString(flag); + loader_filter = ModPlatform::getModLoaderAsString(flag); break; } } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 90d7d0ed7..f15ab9e7a 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -186,7 +186,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Cauldron, ModPlatform::LiteLoader, ModPlatform::Fabric, ModPlatform::Quilt }) { if (mod.loaders & loader) { - loaders.push_back(getModLoaderAsString(loader)); + loaders.push_back(getModLoaderAsString(loader).toStdString()); } } @@ -201,7 +201,7 @@ 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() }, - { "loader", loaders }, + { "loaders", loaders }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, From b54376062e87b9f278b3953da5f7f1bb04695b49 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 17 Oct 2023 19:25:01 +0300 Subject: [PATCH 032/139] Added mcVersion column Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 15 ++++++++++++ launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 23 ++++++++++++------- launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/minecraft/mod/Resource.h | 2 +- launcher/modplatform/ModIndex.cpp | 2 +- launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/flame/FlameModIndex.cpp | 16 ++++++------- .../modrinth/ModrinthPackIndex.cpp | 10 ++++---- launcher/modplatform/packwiz/Packwiz.cpp | 18 +++++++++++++++ launcher/modplatform/packwiz/Packwiz.h | 1 + 11 files changed, 67 insertions(+), 24 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 132f99f7d..22e652319 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -124,6 +124,14 @@ std::pair Mod::compare(const Resource& other, SortType type) const return { -1, type == SortType::LOADERS }; break; } + case SortType::MC_VERSIONS: { + auto thisVersion = mcVersions().join(","); + auto otherVersion = cast_other->mcVersions().join(","); + auto compare_result = QString::compare(thisVersion, otherVersion, Qt::CaseInsensitive); + if (compare_result != 0) + return { compare_result, type == SortType::MC_VERSIONS }; + break; + } } return { 0, false }; } @@ -261,6 +269,13 @@ auto Mod::loaders() const -> ModPlatform::ModLoaderTypes return {}; } +auto Mod::mcVersions() const -> QStringList +{ + if (metadata()) + return metadata()->mcVersions; + return {}; +} + auto Mod::licenses() const -> const QList& { return details().licenses; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 970f85a00..92de65889 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -73,6 +73,7 @@ class Mod : public Resource { auto metaurl() const -> QString; auto side() const -> Metadata::ModSide; auto loaders() const -> ModPlatform::ModLoaderTypes; + auto mcVersions() const -> QStringList; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 631425a72..ef4ab957c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -64,15 +64,16 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders" }); - m_column_names_translated = QStringList( - { tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Side"), tr("Loaders") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, - SortType::DATE, SortType::PROVIDER, SortType::SIDE, SortType::LOADERS }; + m_column_names = + QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders", "Miecraft Versions" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), + tr("Side"), tr("Loaders"), tr("Miecraft Versions") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, + SortType::PROVIDER, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -122,7 +123,10 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const loaders << getModLoaderAsString(loader); } } - return loaders.join(","); + return loaders.join(", "); + } + case McVersionsColumn: { + return at(row)->mcVersions().join(", "); } default: return QVariant(); @@ -175,6 +179,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case ImageColumn: case SideColumn: case LoadersColumn: + case McVersionsColumn: return columnNames().at(section); default: return QVariant(); @@ -196,6 +201,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("On what environment the mod is running."); case LoadersColumn: return tr("The mod loader."); + case McVersionsColumn: + return tr("The supported minecraft versions."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 1623fadfe..f9a87a741 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -70,6 +70,7 @@ class ModFolderModel : public ResourceFolderModel { ProviderColumn, SideColumn, LoadersColumn, + McVersionsColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index eefb5f8a5..c46980837 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS, MC_VERSIONS }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 5b7624563..2cd7710e3 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -138,7 +138,7 @@ auto getModLoaderAsString(ModLoaderType type) -> const QString return ""; } -auto getModLoaderFromString(QString type) -> const ModLoaderType +auto getModLoaderFromString(QString type) -> ModLoaderType { if (type == "neoforge") return NeoForge; diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 940dddea0..7c472ec9b 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -183,7 +183,7 @@ inline auto getOverrideDeps() -> QList QString getMetaURL(ResourceProvider provider, QVariant projectID); auto getModLoaderAsString(ModLoaderType type) -> const QString; -auto getModLoaderFromString(QString type) -> const ModLoaderType; +auto getModLoaderFromString(QString type) -> ModLoaderType; constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 75c10d6f5..d56e46b23 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -117,20 +117,20 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> if (str.contains('.')) file.mcVersion.append(str); - auto loader = str.toLower(); - if (loader == "neoforge") + + if (auto loader = str.toLower(); loader == "neoforge") file.loaders |= ModPlatform::NeoForge; - if (loader == "forge") + else if (loader == "forge") file.loaders |= ModPlatform::Forge; - if (loader == "cauldron") + else if (loader == "cauldron") file.loaders |= ModPlatform::Cauldron; - if (loader == "liteloader") + else if (loader == "liteloader") file.loaders |= ModPlatform::LiteLoader; - if (loader == "fabric") + else if (loader == "fabric") file.loaders |= ModPlatform::Fabric; - if (loader == "quilt") + else if (loader == "quilt") file.loaders |= ModPlatform::Quilt; - if (loader == "server" || loader == "client") { + else if (loader == "server" || loader == "client") { if (file.side.isEmpty()) file.side = loader; else if (file.side != loader) diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 7d0893261..23770b7fb 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -152,15 +152,15 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t for (auto loader : loaders) { if (loader == "neoforge") file.loaders |= ModPlatform::NeoForge; - if (loader == "forge") + else if (loader == "forge") file.loaders |= ModPlatform::Forge; - if (loader == "cauldron") + else if (loader == "cauldron") file.loaders |= ModPlatform::Cauldron; - if (loader == "liteloader") + else if (loader == "liteloader") file.loaders |= ModPlatform::LiteLoader; - if (loader == "fabric") + else if (loader == "fabric") file.loaders |= ModPlatform::Fabric; - if (loader == "quilt") + else if (loader == "quilt") file.loaders |= ModPlatform::Quilt; } file.version = Json::requireString(obj, "name"); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index f15ab9e7a..0692a627d 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -115,6 +115,8 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.project_id = mod_pack.addonId; mod.side = stringToSide(mod_version.side.isEmpty() ? mod_pack.side : mod_version.side); mod.loaders = mod_version.loaders; + mod.mcVersions = mod_version.mcVersion; + mod.mcVersions.sort(); return mod; } @@ -189,6 +191,10 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) loaders.push_back(getModLoaderAsString(loader).toStdString()); } } + toml::array mcVersions; + for (auto version : mod.mcVersions) { + mcVersions.push_back(version.toStdString()); + } if (!index_file.open(QIODevice::ReadWrite)) { qCritical() << QString("Could not open file %1!").arg(normalized_fname); @@ -202,6 +208,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) { "filename", mod.filename.toStdString() }, { "side", sideToString(mod.side).toStdString() }, { "loaders", loaders }, + { "mcVersions", mcVersions }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -293,6 +300,17 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod } } } + if (auto versions = table["mcVersions"]; versions && versions.is_array()) { + for (auto&& version : *versions.as_array()) { + if (version.is_string()) { + auto ver = QString::fromStdString(version.as_string()->value_or("")); + if (!ver.isEmpty()) { + mod.mcVersions << ver; + } + } + } + mod.mcVersions.sort(); + } } { // [download] info diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index a61bb0503..5cf99e74a 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -42,6 +42,7 @@ class V1 { QString filename{}; Side side{ Side::UniversalSide }; ModPlatform::ModLoaderTypes loaders; + QStringList mcVersions; // [download] QString mode{}; From fea4c4eba8bd6d919e1fd2cb96a25b2bc0968894 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 18 Oct 2023 09:00:45 +0300 Subject: [PATCH 033/139] Updated filters Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 1 + launcher/modplatform/ModIndex.cpp | 3 +- .../ui/dialogs/ResourceDownloadDialog.cpp | 1 + launcher/ui/pages/modplatform/ModModel.cpp | 10 +- launcher/ui/pages/modplatform/ModPage.cpp | 9 +- launcher/ui/pages/modplatform/ModPage.h | 3 +- .../modplatform/flame/FlameResourceModels.cpp | 1 + launcher/ui/widgets/ModFilterWidget.cpp | 250 ++++++++++-------- launcher/ui/widgets/ModFilterWidget.h | 55 ++-- launcher/ui/widgets/ModFilterWidget.ui | 156 +++++++++-- 10 files changed, 311 insertions(+), 178 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index df8c690af..deec0db7c 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -23,6 +23,7 @@ #include #include "Json.h" #include "QObjectPtr.h" +#include "minecraft/PackProfile.h" #include "minecraft/mod/MetadataHandler.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 2cd7710e3..d2113320a 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -94,10 +94,9 @@ auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString t { QCryptographicHash::Algorithm algo = QCryptographicHash::Sha1; switch (p) { - case ResourceProvider::MODRINTH: { + case ResourceProvider::MODRINTH: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Sha512; break; - } case ResourceProvider::FLAME: algo = (type == "sha1") ? QCryptographicHash::Sha1 : QCryptographicHash::Md5; break; diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index dc7cfff06..f21502365 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -27,6 +27,7 @@ #include "Application.h" #include "ResourceDownloadTask.h" +#include "minecraft/PackProfile.h" #include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ResourcePackFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index c628f74ac..3b53d1348 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -25,15 +25,18 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() Q_ASSERT(m_filter); std::optional> versions{}; + auto loaders = profile->getSupportedModLoaders(); { // Version filter if (!m_filter->versions.empty()) versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; } auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, profile->getSupportedModLoaders(), versions }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) @@ -45,10 +48,13 @@ ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& en Q_ASSERT(m_filter); std::optional> versions{}; + auto loaders = profile->getSupportedModLoaders(); if (!m_filter->versions.empty()) versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; - return { pack, versions, profile->getSupportedModLoaders() }; + return { pack, versions, loaders }; } ResourceAPI::ProjectInfoArgs ModModel::createInfoArguments(QModelIndex& entry) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index d6cc1fdcc..838cf21c8 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -71,7 +71,6 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount()); - m_filter_widget->setInstance(&static_cast(m_base_instance)); m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, @@ -89,13 +88,14 @@ void ModPage::filterMods() void ModPage::triggerSearch() { + auto changed = m_filter_widget->changed(); m_filter = m_filter_widget->getFilter(); m_ui->packView->clearSelection(); m_ui->packDescription->clear(); m_ui->versionSelectionBox->clear(); updateSelectionButton(); - static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), m_filter_widget->changed()); + static_cast(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), changed); m_fetch_progress.watch(m_model->activeSearchJob().get()); } @@ -116,6 +116,9 @@ void ModPage::updateVersionList() auto packProfile = (dynamic_cast(m_base_instance)).getPackProfile(); QString mcVersion = packProfile->getComponentVersion("net.minecraft"); + auto loaders = packProfile->getSupportedModLoaders(); + if (m_filter->loaders) + loaders = m_filter->loaders; auto current_pack = getCurrentPack(); if (!current_pack) @@ -124,7 +127,7 @@ void ModPage::updateVersionList() auto version = current_pack->versions[i]; bool valid = false; for (auto& mcVer : m_filter->versions) { - if (validateVersion(version, mcVer.toString(), packProfile->getSupportedModLoaders())) { + if (validateVersion(version, mcVer.toString(), loaders)) { valid = true; break; } diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 5a43e49a6..6efb318ea 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -31,8 +31,7 @@ class ModPage : public ResourcePage { auto page = new T(dialog, instance); auto model = static_cast(page->getModel()); - auto filter_widget = - ModFilterWidget::create(static_cast(instance).getPackProfile()->getComponentVersion("net.minecraft"), page); + auto filter_widget = ModFilterWidget::create(&static_cast(instance), page); page->setFilterWidget(filter_widget); model->setFilter(page->getFilter()); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 7d18e72a6..39a2a0d6a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -6,6 +6,7 @@ #include "Json.h" +#include "minecraft/PackProfile.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index c2c099eeb..62a8eb154 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,13 +1,70 @@ #include "ModFilterWidget.h" +#include +#include +#include "BaseVersionList.h" +#include "meta/Index.h" +#include "modplatform/ModIndex.h" #include "ui_ModFilterWidget.h" #include "Application.h" +#include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(Version default_version, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, QWidget* parent) { - auto filter_widget = new ModFilterWidget(default_version, parent); + return unique_qobject_ptr(new ModFilterWidget(instance, parent)); +} - if (!filter_widget->versionList()->isLoaded()) { +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) + : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) +{ + ui->setupUi(this); + + m_versions_proxy = new VersionProxyModel(this); + + ui->versionsCb->setModel(m_versions_proxy); + + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + + ui->versionsCb->setStyleSheet("combobox-popup: 0;"); + connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); + connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + + connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabricCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quiltCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->liteLoaderCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->cauldronCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + + ui->liteLoaderCb->hide(); + ui->cauldronCb->hide(); + + connect(ui->serverEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + connect(ui->clientEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + + connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); + + setHidden(true); + loadVersionList(); + prepareBasicFilter(); +} + +auto ModFilterWidget::getFilter() -> std::shared_ptr +{ + m_filter_changed = false; + emit filterUnchanged(); + return m_filter; +} + +ModFilterWidget::~ModFilterWidget() +{ + delete ui; +} + +void ModFilterWidget::loadVersionList() +{ + m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); + if (!m_version_list->isLoaded()) { QEventLoop load_version_list_loop; QTimer time_limit_for_list_load; @@ -16,10 +73,12 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit); time_limit_for_list_load.start(4000); - auto task = filter_widget->versionList()->getLoadTask(); + auto task = m_version_list->getLoadTask(); - connect(task.get(), &Task::failed, - [filter_widget] { filter_widget->disableVersionButton(VersionButtonID::Major, tr("failed to get version index")); }); + connect(task.get(), &Task::failed, [this] { + ui->versionsCb->setEnabled(false); + ui->snapshotsCb->setEnabled(false); + }); connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); if (!task->isRunning()) @@ -29,128 +88,93 @@ unique_qobject_ptr ModFilterWidget::create(Version default_vers if (time_limit_for_list_load.isActive()) time_limit_for_list_load.stop(); } - - return unique_qobject_ptr(filter_widget); + m_versions_proxy->setSourceModel(m_version_list.get()); } -ModFilterWidget::ModFilterWidget(Version def, QWidget* parent) : QTabWidget(parent), m_filter(new Filter()), ui(new Ui::ModFilterWidget) +void ModFilterWidget::prepareBasicFilter() { - ui->setupUi(this); - - m_mcVersion_buttons.addButton(ui->strictVersionButton, VersionButtonID::Strict); - ui->strictVersionButton->click(); - m_mcVersion_buttons.addButton(ui->majorVersionButton, VersionButtonID::Major); - m_mcVersion_buttons.addButton(ui->allVersionsButton, VersionButtonID::All); - // m_mcVersion_buttons.addButton(ui->betweenVersionsButton, VersionButtonID::Between); - - connect(&m_mcVersion_buttons, SIGNAL(idClicked(int)), this, SLOT(onVersionFilterChanged(int))); - - m_filter->versions.push_front(def); - - m_version_list = APPLICATION->metadataIndex()->get("net.minecraft"); - setHidden(true); + m_filter->hideInstalled = false; + m_filter->side = ""; // or "both"t + auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + ui->neoForgeCb->setChecked(loaders & ModPlatform::NeoForge); + ui->forgeCb->setChecked(loaders & ModPlatform::Forge); + ui->fabricCb->setChecked(loaders & ModPlatform::Fabric); + ui->quiltCb->setChecked(loaders & ModPlatform::Quilt); + ui->liteLoaderCb->setChecked(loaders & ModPlatform::LiteLoader); + ui->cauldronCb->setChecked(loaders & ModPlatform::Cauldron); + m_filter->loaders = loaders; + auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); + m_filter->versions.push_front(Version{ def }); + m_versions_proxy->setCurrentVersion(def); + ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row()); } -void ModFilterWidget::setInstance(MinecraftInstance* instance) +void ModFilterWidget::onIncludeSnapshotsChanged() { - m_instance = instance; - - ui->strictVersionButton->setText(tr("Strict match (= %1)").arg(mcVersionStr())); - - // we can't do this for snapshots sadly - if (mcVersionStr().contains('.')) { - auto mcVersionSplit = mcVersionStr().split("."); - ui->majorVersionButton->setText(tr("Major version match (= %1.%2.x)").arg(mcVersionSplit[0], mcVersionSplit[1])); - } else { - ui->majorVersionButton->setText(tr("Major version match (unsupported)")); - disableVersionButton(Major); - } - ui->allVersionsButton->setText(tr("Any version")); - // ui->betweenVersionsButton->setText( - // tr("Between two versions")); + QString filter = "(release)"; + if (ui->snapshotsCb->isChecked()) + filter += "|(snapshot)"; + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); } -auto ModFilterWidget::getFilter() -> std::shared_ptr +void ModFilterWidget::onVersionFilterChanged() { - m_last_version_id = m_version_id; - emit filterUnchanged(); - return m_filter; -} - -void ModFilterWidget::disableVersionButton(VersionButtonID id, QString reason) -{ - QAbstractButton* btn = nullptr; - - switch (id) { - case (VersionButtonID::Strict): - btn = ui->strictVersionButton; - break; - case (VersionButtonID::Major): - btn = ui->majorVersionButton; - break; - case (VersionButtonID::All): - btn = ui->allVersionsButton; - break; - case (VersionButtonID::Between): - default: - break; - } - - if (btn) { - btn->setEnabled(false); - if (!reason.isEmpty()) - btn->setText(btn->text() + QString(" (%1)").arg(reason)); - } -} - -void ModFilterWidget::onVersionFilterChanged(int id) -{ - // ui->lowerVersionComboBox->setEnabled(id == VersionButtonID::Between); - // ui->upperVersionComboBox->setEnabled(id == VersionButtonID::Between); - - int index = 1; - - auto cast_id = (VersionButtonID)id; - if (cast_id != m_version_id) { - m_version_id = cast_id; - } else { - return; - } - + auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); m_filter->versions.clear(); + m_filter->versions.push_front(version); + m_filter_changed = true; + emit filterChanged(); +} - switch (cast_id) { - case (VersionButtonID::Strict): - m_filter->versions.push_front(mcVersion()); - break; - case (VersionButtonID::Major): { - auto versionSplit = mcVersionStr().split("."); - - auto major_version = QString("%1.%2").arg(versionSplit[0], versionSplit[1]); - QString version_str = major_version; - - while (m_version_list->hasVersion(version_str)) { - m_filter->versions.emplace_back(version_str); - version_str = QString("%1.%2").arg(major_version, QString::number(index++)); - } - - break; - } - case (VersionButtonID::All): - // Empty list to avoid enumerating all versions :P - break; - case (VersionButtonID::Between): - // TODO - break; - } - - if (changed()) +void ModFilterWidget::onLoadersFilterChanged() +{ + ModPlatform::ModLoaderTypes loaders; + if (ui->neoForgeCb->isChecked()) + loaders |= ModPlatform::NeoForge; + if (ui->forgeCb->isChecked()) + loaders |= ModPlatform::Forge; + if (ui->fabricCb->isChecked()) + loaders |= ModPlatform::Fabric; + if (ui->quiltCb->isChecked()) + loaders |= ModPlatform::Quilt; + if (ui->cauldronCb->isChecked()) + loaders |= ModPlatform::Cauldron; + if (ui->liteLoaderCb->isChecked()) + loaders |= ModPlatform::LiteLoader; + m_filter_changed = loaders != m_filter->loaders; + m_filter->loaders = loaders; + if (m_filter_changed) emit filterChanged(); else emit filterUnchanged(); } -ModFilterWidget::~ModFilterWidget() +void ModFilterWidget::onSideFilterChanged() { - delete ui; + QString side; + if (ui->serverEnv->isChecked()) + side = "server"; + if (ui->clientEnv->isChecked()) { + if (side.isEmpty()) + side = "client"; + else + side = ""; // or both + } + m_filter_changed = side != m_filter->side; + m_filter->side = side; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); } + +void ModFilterWidget::onHideInstalledFilterChanged() +{ + auto hide = ui->hide_installed->isChecked(); + m_filter_changed = hide != m_filter->hideInstalled; + m_filter->hideInstalled = hide; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); +} \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index ed6cd0ea7..b92437a4f 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -5,11 +5,11 @@ #include "Version.h" -#include "meta/Index.h" +#include "VersionProxyModel.h" #include "meta/VersionList.h" #include "minecraft/MinecraftInstance.h" -#include "minecraft/PackProfile.h" +#include "modplatform/ModIndex.h" class MinecraftInstance; @@ -20,42 +20,37 @@ class ModFilterWidget; class ModFilterWidget : public QTabWidget { Q_OBJECT public: - enum VersionButtonID { Strict = 0, Major = 1, All = 2, Between = 3 }; - struct Filter { std::list versions; + ModPlatform::ModLoaderTypes loaders; + QString side; + bool hideInstalled; - bool operator==(const Filter& other) const { return versions == other.versions; } + bool operator==(const Filter& other) const + { + return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions; + } bool operator!=(const Filter& other) const { return !(*this == other); } }; - std::shared_ptr m_filter; - - public: - static unique_qobject_ptr create(Version default_version, QWidget* parent = nullptr); - ~ModFilterWidget(); - - void setInstance(MinecraftInstance* instance); - - /// By default all buttons are enabled - void disableVersionButton(VersionButtonID, QString reason = {}); + static unique_qobject_ptr create(MinecraftInstance* instance, QWidget* parent = nullptr); + virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; - auto changed() const -> bool { return m_last_version_id != m_version_id; } - - Meta::VersionList::Ptr versionList() { return m_version_list; } + auto changed() const -> bool { return m_filter_changed; } private: - ModFilterWidget(Version def, QWidget* parent = nullptr); + ModFilterWidget(MinecraftInstance* instance, QWidget* parent = nullptr); - inline auto mcVersionStr() const -> QString - { - return m_instance ? m_instance->getPackProfile()->getComponentVersion("net.minecraft") : ""; - } - inline auto mcVersion() const -> Version { return { mcVersionStr() }; } + void loadVersionList(); + void prepareBasicFilter(); private slots: - void onVersionFilterChanged(int id); + void onVersionFilterChanged(); + void onLoadersFilterChanged(); + void onSideFilterChanged(); + void onHideInstalledFilterChanged(); + void onIncludeSnapshotsChanged(); public: signals: @@ -66,13 +61,9 @@ class ModFilterWidget : public QTabWidget { Ui::ModFilterWidget* ui; MinecraftInstance* m_instance = nullptr; - - /* Version stuff */ - QButtonGroup m_mcVersion_buttons; + std::shared_ptr m_filter; + bool m_filter_changed = false; Meta::VersionList::Ptr m_version_list; - - /* Used to tell if the filter was changed since the last getFilter() call */ - VersionButtonID m_last_version_id = VersionButtonID::Strict; - VersionButtonID m_version_id = VersionButtonID::Strict; + VersionProxyModel* m_versions_proxy = nullptr; }; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index ebe5d2be1..74e27e5f7 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -7,7 +7,7 @@ 0 0 400 - 300 + 127 @@ -16,35 +16,143 @@ 0 + + 0 + Minecraft versions + + + + + 0 + 0 + + + + 5 + + + QComboBox::AdjustToContentsOnFirstShow + + + + + + + Include Snapshots + + + + + + + + Loaders + + + + QLayout::SetMinimumSize + + + + + false + + + LiteLoader + + + + + + + false + + + Cauldron + + + - - - - - allVersions - - - - - - - strictVersion - - - - - - - majorVersion - - - - + + + NeoForge + + + + + + + Forge + + + + + + + Quilt + + + + + + + Fabric + + + + + + + + Others + + + + + + Environments + + + + QLayout::SetDefaultConstraint + + + + + Client + + + + + + + Server + + + + + + + + + + Instaled status + + + + + + Hide already installed + + + + + From 55946c8923bf0e1b804944934aec2a42478f84a4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 19 Oct 2023 23:53:26 +0300 Subject: [PATCH 034/139] first attempt at a combobox Signed-off-by: Trial97 --- launcher/CMakeLists.txt | 2 + launcher/ui/widgets/CheckComboBox.cpp | 205 ++++++++++++++++++++++++ launcher/ui/widgets/CheckComboBox.h | 61 +++++++ launcher/ui/widgets/ModFilterWidget.cpp | 8 +- launcher/ui/widgets/ModFilterWidget.ui | 9 +- 5 files changed, 280 insertions(+), 5 deletions(-) create mode 100644 launcher/ui/widgets/CheckComboBox.cpp create mode 100644 launcher/ui/widgets/CheckComboBox.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d15dc85de..a6c3a3e08 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1035,6 +1035,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/InstallLoaderDialog.h # GUI - widgets + ui/widgets/CheckComboBox.cpp + ui/widgets/CheckComboBox.h ui/widgets/Common.cpp ui/widgets/Common.h ui/widgets/CustomCommands.cpp diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp new file mode 100644 index 000000000..9c9006c74 --- /dev/null +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 "CheckComboBox.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "BaseVersionList.h" + +class CheckComboModel : public QIdentityProxyModel { + Q_OBJECT + + public: + explicit CheckComboModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {} + + virtual Qt::ItemFlags flags(const QModelIndex& index) const { return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable; } + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const + { + if (role == Qt::CheckStateRole) { + auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + return checked.contains(txt) ? Qt::Checked : Qt::Unchecked; + } + if (role == Qt::DisplayRole) + return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole); + return {}; + } + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) + { + if (role == Qt::CheckStateRole) { + auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + if (checked.contains(txt)) { + checked.removeOne(txt); + } else { + checked.push_back(txt); + } + emit dataChanged(index, index); + emit checkStateChanged(); + return true; + } + return QIdentityProxyModel::setData(index, value, role); + } + QStringList getChecked() { return checked; } + + signals: + void checkStateChanged(); + + private: + QStringList checked; +}; + +CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",") +{ + // read-only contents + // QLineEdit* lineEdit = new QLineEdit(this); + // lineEdit->setReadOnly(false); + // setLineEdit(lineEdit); + // lineEdit->disconnect(this); + setInsertPolicy(QComboBox::NoInsert); + + view()->installEventFilter(this); + view()->window()->installEventFilter(this); + view()->viewport()->installEventFilter(this); + this->installEventFilter(this); +} + +void CheckComboBox::setModel(QAbstractItemModel* new_model) +{ + auto proxy = new CheckComboModel(this); + proxy->setSourceModel(new_model); + model()->disconnect(this); + QComboBox::setModel(proxy); + connect(this, QOverload::of(&QComboBox::activated), this, &CheckComboBox::toggleCheckState); + connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::updateCheckedItems); + connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::updateCheckedItems); + connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::updateCheckedItems); +} + +void CheckComboBox::hidePopup() +{ + if (containerMousePress) + QComboBox::hidePopup(); +} + +void CheckComboBox::updateCheckedItems() +{ + QStringList items = checkedItems(); + if (items.isEmpty()) + setEditText(defaultText()); + else + setEditText(items.join(separator())); + + emit checkedItemsChanged(items); +} + +QString CheckComboBox::defaultText() const +{ + return m_default_text; +} + +void CheckComboBox::setDefaultText(const QString& text) +{ + if (m_default_text != text) { + m_default_text = text; + updateCheckedItems(); + } +} + +QString CheckComboBox::separator() const +{ + return m_separator; +} + +void CheckComboBox::setSeparator(const QString& separator) +{ + if (m_separator != separator) { + m_separator = separator; + updateCheckedItems(); + } +} + +bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) +{ + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { + QKeyEvent* keyEvent = static_cast(event); + if (receiver == this && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { + showPopup(); + return true; + } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Escape) { + // it is important to call QComboBox implementation + QComboBox::hidePopup(); + return (keyEvent->key() != Qt::Key_Escape); + } + } + case QEvent::MouseButtonPress: + containerMousePress = (receiver == view()->window()); + break; + case QEvent::MouseButtonRelease: + containerMousePress = false; + break; + default: + break; + } + return false; +} + +void CheckComboBox::toggleCheckState(int index) +{ + QVariant value = itemData(index, Qt::CheckStateRole); + if (value.isValid()) { + Qt::CheckState state = static_cast(value.toInt()); + setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole); + } + updateCheckedItems(); +} + +Qt::CheckState CheckComboBox::itemCheckState(int index) const +{ + return static_cast(itemData(index, Qt::CheckStateRole).toInt()); +} + +void CheckComboBox::setItemCheckState(int index, Qt::CheckState state) +{ + setItemData(index, state, Qt::CheckStateRole); +} + +QStringList CheckComboBox::checkedItems() const +{ + if (model()) + return dynamic_cast(model())->getChecked(); + return {}; +} + +void CheckComboBox::setCheckedItems(const QStringList& items) +{ + foreach (auto text, items) { + auto index = findText(text); + setItemCheckState(index, index != -1 ? Qt::Checked : Qt::Unchecked); + } +} + +#include "CheckComboBox.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h new file mode 100644 index 000000000..277dd5fb8 --- /dev/null +++ b/launcher/ui/widgets/CheckComboBox.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 + +class CheckComboBox : public QComboBox { + Q_OBJECT + + public: + explicit CheckComboBox(QWidget* parent = nullptr); + virtual ~CheckComboBox() = default; + + virtual void hidePopup() override; + + QString defaultText() const; + void setDefaultText(const QString& text); + + Qt::CheckState itemCheckState(int index) const; + void setItemCheckState(int index, Qt::CheckState state); + + QString separator() const; + void setSeparator(const QString& separator); + + QStringList checkedItems() const; + + virtual void setModel(QAbstractItemModel* model) override; + + public slots: + void setCheckedItems(const QStringList& items); + + signals: + void checkedItemsChanged(const QStringList& items); + + private: + void updateCheckedItems(); + bool eventFilter(QObject* receiver, QEvent* event) override; + void toggleCheckState(int index); + + private: + QString m_default_text; + QString m_separator; + bool containerMousePress; +}; \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 62a8eb154..5ed2e34f6 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -105,8 +105,7 @@ void ModFilterWidget::prepareBasicFilter() m_filter->loaders = loaders; auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); m_filter->versions.push_front(Version{ def }); - m_versions_proxy->setCurrentVersion(def); - ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row()); + ui->versionsCb->setCheckedItems({ def }); } void ModFilterWidget::onIncludeSnapshotsChanged() @@ -119,9 +118,10 @@ void ModFilterWidget::onIncludeSnapshotsChanged() void ModFilterWidget::onVersionFilterChanged() { - auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); + auto versions = ui->versionsCb->checkedItems(); m_filter->versions.clear(); - m_filter->versions.push_front(version); + for (auto version : versions) + m_filter->versions.push_back(version); m_filter_changed = true; emit filterChanged(); } diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 74e27e5f7..e27de6f1a 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -25,7 +25,7 @@ - + 0 @@ -157,6 +157,13 @@ + + + CheckComboBox + QComboBox +
ui/widgets/CheckComboBox.h
+
+
From 6883c195795a8bb61a7ab50869d60265ddebc3d8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 20 Oct 2023 00:44:36 +0300 Subject: [PATCH 035/139] Fixed the version combobox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 16 +++++++--------- launcher/ui/widgets/ModFilterWidget.cpp | 23 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 9c9006c74..f826c1afe 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -39,17 +39,17 @@ class CheckComboModel : public QIdentityProxyModel { virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const { if (role == Qt::CheckStateRole) { - auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString(); return checked.contains(txt) ? Qt::Checked : Qt::Unchecked; } if (role == Qt::DisplayRole) - return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole); + return QIdentityProxyModel::data(index, Qt::DisplayRole); return {}; } virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) { if (role == Qt::CheckStateRole) { - auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString(); + auto txt = QIdentityProxyModel::data(index, Qt::DisplayRole).toString(); if (checked.contains(txt)) { checked.removeOne(txt); } else { @@ -72,11 +72,10 @@ class CheckComboModel : public QIdentityProxyModel { CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",") { - // read-only contents - // QLineEdit* lineEdit = new QLineEdit(this); - // lineEdit->setReadOnly(false); - // setLineEdit(lineEdit); - // lineEdit->disconnect(this); + QLineEdit* lineEdit = new QLineEdit(this); + lineEdit->setReadOnly(false); + setLineEdit(lineEdit); + lineEdit->disconnect(this); setInsertPolicy(QComboBox::NoInsert); view()->installEventFilter(this); @@ -150,7 +149,6 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) showPopup(); return true; } else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Escape) { - // it is important to call QComboBox implementation QComboBox::hidePopup(); return (keyEvent->key() != Qt::Key_Escape); } diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 5ed2e34f6..5a67757b5 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -14,6 +14,20 @@ unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* i return unique_qobject_ptr(new ModFilterWidget(instance, parent)); } +class VersionBasicModel : public QIdentityProxyModel { + Q_OBJECT + + public: + explicit VersionBasicModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {} + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override + { + if (role == Qt::DisplayRole) + return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole); + return {}; + } +}; + ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) { @@ -21,7 +35,10 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) m_versions_proxy = new VersionProxyModel(this); - ui->versionsCb->setModel(m_versions_proxy); + auto proxy = new VersionBasicModel(this); + proxy->setSourceModel(m_versions_proxy); + ui->versionsCb->setModel(proxy); + ui->versionsCb->setSeparator("| "); m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); @@ -177,4 +194,6 @@ void ModFilterWidget::onHideInstalledFilterChanged() emit filterChanged(); else emit filterUnchanged(); -} \ No newline at end of file +} + +#include "ModFilterWidget.moc" From 4850434c67017622cf7b082728cf56a3b42d7920 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 12:16:02 +0300 Subject: [PATCH 036/139] added releaseType? Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.ui | 75 +++++++++++++++++++------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index e27de6f1a..3113f2df2 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -17,7 +17,7 @@ - 0 + 2 @@ -57,23 +57,10 @@ QLayout::SetMinimumSize - - - - false - + + - LiteLoader - - - - - - - false - - - Cauldron + Fabric @@ -91,6 +78,16 @@ + + + + false + + + Cauldron + + + @@ -98,10 +95,48 @@ - - + + + + false + - Fabric + LiteLoader + + + +
+ + + + Release type + + + + + + Release + + + + + + + Beta + + + + + + + Alpha + + + + + + + Unknown From 9e85297f7af495ecf8c34ea62a3559b712ff8e14 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 18:28:33 +0300 Subject: [PATCH 037/139] Connected filters Signed-off-by: Trial97 --- launcher/modplatform/ResourceAPI.h | 1 + launcher/modplatform/flame/FlameModIndex.cpp | 7 +-- launcher/modplatform/modrinth/ModrinthAPI.h | 18 +++++++ .../modrinth/ModrinthPackIndex.cpp | 7 +-- launcher/ui/pages/modplatform/ModModel.cpp | 53 +++++++++++++++--- launcher/ui/pages/modplatform/ModModel.h | 3 ++ launcher/ui/pages/modplatform/ModPage.cpp | 38 +------------ launcher/ui/pages/modplatform/ModPage.h | 9 +--- .../ui/pages/modplatform/ResourceModel.cpp | 15 ++++-- launcher/ui/pages/modplatform/ResourceModel.h | 11 ++++ .../ui/pages/modplatform/ResourcePage.cpp | 2 +- launcher/ui/pages/modplatform/ResourcePage.h | 7 --- .../modplatform/flame/FlameResourceModels.cpp | 25 +++++++++ .../modplatform/flame/FlameResourceModels.h | 8 +++ .../modplatform/flame/FlameResourcePages.cpp | 38 ++----------- .../modplatform/flame/FlameResourcePages.h | 12 +---- .../modrinth/ModrinthResourcePages.cpp | 11 ++-- .../modrinth/ModrinthResourcePages.h | 3 +- launcher/ui/widgets/CheckComboBox.cpp | 2 +- launcher/ui/widgets/CheckComboBox.h | 4 +- launcher/ui/widgets/ModFilterWidget.cpp | 54 ++++++++++++++++--- launcher/ui/widgets/ModFilterWidget.h | 19 ++++--- launcher/ui/widgets/ModFilterWidget.ui | 19 ++++--- 23 files changed, 214 insertions(+), 152 deletions(-) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 3b1959384..732c66621 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -73,6 +73,7 @@ class ResourceAPI { std::optional sorting; std::optional loaders; std::optional > versions; + std::optional side; }; struct SearchCallbacks { std::function on_succeed; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index d56e46b23..f95b17683 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -79,10 +79,6 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, const BaseInstance* inst) { QVector unsortedVersions; - auto profile = (dynamic_cast(inst))->getPackProfile(); - QString mcVersion = profile->getComponentVersion("net.minecraft"); - auto loaders = profile->getSupportedModLoaders(); - for (auto versionIter : arr) { auto obj = versionIter.toObject(); @@ -90,8 +86,7 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, if (!file.addonId.isValid()) file.addonId = pack.addonId; - if (file.fileId.isValid() && - (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + if (file.fileId.isValid()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 857719902..8e0f9fe22 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -56,6 +56,18 @@ class ModrinthAPI : public NetworkResourceAPI { return l.join(','); } + static auto getSideFilters(QString side) -> const QString + { + if (side.isEmpty() || side == "both") { + return {}; + } + if (side == "client") + return QString("\"client_side:required\",\"client_side:optional\""); + if (side == "server") + return QString("\"server_side:required\",\"server_side:optional\""); + return {}; + } + private: [[nodiscard]] static QString resourceTypeParameter(ModPlatform::ResourceType type) { @@ -73,6 +85,7 @@ class ModrinthAPI : public NetworkResourceAPI { return ""; } + [[nodiscard]] QString createFacets(SearchArgs const& args) const { QStringList facets_list; @@ -81,6 +94,11 @@ class ModrinthAPI : public NetworkResourceAPI { facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value()))); if (args.versions.has_value()) facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value()))); + if (args.side.has_value()) { + auto side = getSideFilters(args.side.value()); + if (!side.isEmpty()) + facets_list.append(QString("[%1]").arg(side)); + } facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type))); return QString("[%1]").arg(facets_list.join(',')); diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 23770b7fb..5de645220 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -112,16 +112,11 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const BaseInstance* inst) { QVector unsortedVersions; - auto profile = (dynamic_cast(inst))->getPackProfile(); - QString mcVersion = profile->getComponentVersion("net.minecraft"); - auto loaders = profile->getSupportedModLoaders(); - for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj); - if (file.fileId.isValid() && - (!loaders.has_value() || !file.loaders || loaders.value() & file.loaders)) // Heuristic to check if the returned value is valid + if (file.fileId.isValid()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 3b53d1348..64ae7de36 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -27,16 +27,16 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() std::optional> versions{}; auto loaders = profile->getSupportedModLoaders(); - { // Version filter - if (!m_filter->versions.empty()) - versions = m_filter->versions; - if (m_filter->loaders) - loaders = m_filter->loaders; - } + // Version filter + if (!m_filter->versions.empty()) + versions = m_filter->versions; + if (m_filter->loaders) + loaders = m_filter->loaders; + auto side = m_filter->side; auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) @@ -85,4 +85,43 @@ bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const }); } +bool checkSide(QString filter, QString value) +{ + return filter.isEmpty() || value.isEmpty() || filter == "both" || value == "both" || filter == value; +} + +bool checkMcVersions(std::list filter, QStringList value) +{ + bool valid = false; + for (auto mcVersion : filter) { + if (value.contains(mcVersion.toString())) { + valid = true; + break; + } + } + return filter.empty() || valid; +} + +bool ModModel::checkFilters(ModPlatform::IndexedPack::Ptr pack) +{ + if (!m_filter) + return true; + return !(m_filter->hideInstalled && isPackInstalled(pack)) && checkSide(m_filter->side, pack->side); +} + +bool ModModel::checkVersionFilters(const ModPlatform::IndexedVersion& v) +{ + if (!m_filter) + return true; + auto loaders = static_cast(m_base_instance).getPackProfile()->getSupportedModLoaders(); + if (m_filter->loaders) + loaders = m_filter->loaders; + return (!optedOut(v) && // is opted out(aka curseforge download link) + (!loaders.has_value() || !v.loaders || loaders.value() & v.loaders) && // loaders + checkSide(m_filter->side, v.side) && // side + (m_filter->releases.empty() || // releases + std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.version_type) != m_filter->releases.cend()) && + checkMcVersions(m_filter->versions, v.mcVersion)); // mcVersions +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index dd187aa8d..9101e07ba 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -45,6 +45,9 @@ class ModModel : public ResourceModel { auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0; virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override; + virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) override; + virtual bool checkVersionFilters(const ModPlatform::IndexedVersion&) override; + protected: BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 838cf21c8..e6106a0de 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -73,6 +73,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_filter = m_filter_widget->getFilter(); + connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ResourcePage::updateVersionList); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, @@ -110,43 +111,6 @@ QMap ModPage::urlHandlers() const /******** Make changes to the UI ********/ -void ModPage::updateVersionList() -{ - m_ui->versionSelectionBox->clear(); - auto packProfile = (dynamic_cast(m_base_instance)).getPackProfile(); - - QString mcVersion = packProfile->getComponentVersion("net.minecraft"); - auto loaders = packProfile->getSupportedModLoaders(); - if (m_filter->loaders) - loaders = m_filter->loaders; - - auto current_pack = getCurrentPack(); - if (!current_pack) - return; - for (int i = 0; i < current_pack->versions.size(); i++) { - auto version = current_pack->versions[i]; - bool valid = false; - for (auto& mcVer : m_filter->versions) { - if (validateVersion(version, mcVer.toString(), loaders)) { - valid = true; - break; - } - } - - // Only add the version if it's valid or using the 'Any' filter, but never if the version is opted out - if ((valid || m_filter->versions.empty()) && !optedOut(version)) { - auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; - m_ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(i)); - } - } - if (m_ui->versionSelectionBox->count() == 0) { - m_ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1)); - m_ui->resourceSelectionButton->setText(tr("Cannot select invalid version :(")); - } - - updateSelectionButton(); -} - void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version, const std::shared_ptr base_model) diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index 6efb318ea..c878bc004 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -31,7 +31,7 @@ class ModPage : public ResourcePage { auto page = new T(dialog, instance); auto model = static_cast(page->getModel()); - auto filter_widget = ModFilterWidget::create(&static_cast(instance), page); + auto filter_widget = page->createFilterWidget(); page->setFilterWidget(filter_widget); model->setFilter(page->getFilter()); @@ -52,17 +52,12 @@ class ModPage : public ResourcePage { ModPlatform::IndexedVersion&, const std::shared_ptr) override; - virtual auto validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders = {}) const -> bool = 0; + virtual unique_qobject_ptr createFilterWidget() = 0; [[nodiscard]] bool supportsFiltering() const override { return true; }; auto getFilter() const -> const std::shared_ptr { return m_filter; } void setFilterWidget(unique_qobject_ptr&); - public slots: - void updateVersionList() override; - protected: ModPage(ModDownloadDialog* dialog, BaseInstance& instance); diff --git a/launcher/ui/pages/modplatform/ResourceModel.cpp b/launcher/ui/pages/modplatform/ResourceModel.cpp index 48e66efca..776765d64 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.cpp +++ b/launcher/ui/pages/modplatform/ResourceModel.cpp @@ -399,12 +399,17 @@ void ResourceModel::searchRequestSucceeded(QJsonDocument& doc) m_search_state = SearchState::CanFetchMore; } + QList filteredNewList; + for (auto p : newList) + if (checkFilters(p)) + filteredNewList << p; + // When you have a Qt build with assertions turned on, proceeding here will abort the application - if (newList.size() == 0) + if (filteredNewList.size() == 0) return; - beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + newList.size() - 1); - m_packs.append(newList); + beginInsertRows(QModelIndex(), m_packs.size(), m_packs.size() + filteredNewList.size() - 1); + m_packs.append(filteredNewList); endInsertRows(); } @@ -547,4 +552,8 @@ void ResourceModel::removePack(const QString& rem) ver.is_currently_selected = false; } +bool ResourceModel::checkVersionFilters(const ModPlatform::IndexedVersion& v) +{ + return (!optedOut(v)); +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/ResourceModel.h b/launcher/ui/pages/modplatform/ResourceModel.h index ecf4f8f79..1956e54ac 100644 --- a/launcher/ui/pages/modplatform/ResourceModel.h +++ b/launcher/ui/pages/modplatform/ResourceModel.h @@ -11,6 +11,7 @@ #include "QObjectPtr.h" #include "ResourceDownloadTask.h" +#include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "tasks/ConcurrentTask.h" @@ -55,6 +56,16 @@ class ResourceModel : public QAbstractListModel { [[nodiscard]] auto getSortingMethods() const { return m_api->getSortingMethods(); } + /** Whether the version is opted out or not. Currently only makes sense in CF. */ + virtual bool optedOut(const ModPlatform::IndexedVersion& ver) const + { + Q_UNUSED(ver); + return false; + }; + + virtual bool checkFilters(ModPlatform::IndexedPack::Ptr) { return true; } + virtual bool checkVersionFilters(const ModPlatform::IndexedVersion&); + public slots: void fetchMore(const QModelIndex& parent) override; // NOTE: Can't use [[nodiscard]] here because of https://bugreports.qt.io/browse/QTBUG-58628 on Qt 5.12 diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 44a91003d..158b13c96 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -263,7 +263,7 @@ void ResourcePage::updateVersionList() if (current_pack) for (int i = 0; i < current_pack->versions.size(); i++) { auto& version = current_pack->versions[i]; - if (optedOut(version)) + if (!m_model->checkVersionFilters(version)) continue; auto release_type = current_pack->versions[i].version_type.isValid() diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 7bec0a375..84b37f50b 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -96,13 +96,6 @@ class ResourcePage : public QWidget, public BasePage { virtual QMap urlHandlers() const = 0; virtual void openUrl(const QUrl&); - /** Whether the version is opted out or not. Currently only makes sense in CF. */ - virtual bool optedOut(ModPlatform::IndexedVersion& ver) const - { - Q_UNUSED(ver); - return false; - }; - public: BaseInstance& m_base_instance; diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 39a2a0d6a..ae4562be4 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -12,6 +12,11 @@ namespace ResourceDownload { +static bool isOptedOut(const ModPlatform::IndexedVersion& ver) +{ + return ver.downloadUrl.isEmpty(); +} + FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {} void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) @@ -35,6 +40,11 @@ auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJs return FlameMod::loadDependencyVersions(m, arr, &m_base_instance); } +bool FlameModModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); @@ -58,6 +68,11 @@ void FlameResourcePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); } +bool FlameResourcePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); @@ -117,6 +132,11 @@ ResourceAPI::VersionSearchArgs FlameTexturePackModel::createVersionsArguments(QM return args; } +bool FlameTexturePackModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); @@ -140,6 +160,11 @@ void FlameShaderPackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance); } +bool FlameShaderPackModel::optedOut(const ModPlatform::IndexedVersion& ver) const +{ + return isOptedOut(ver); +} + auto FlameShaderPackModel::documentToArray(QJsonDocument& obj) const -> QJsonArray { return Json::ensureArray(obj.object(), "data"); diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 76dbd7b3d..458fd85d0 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -17,6 +17,8 @@ class FlameModModel : public ModModel { FlameModModel(BaseInstance&); ~FlameModModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } @@ -36,6 +38,8 @@ class FlameResourcePackModel : public ResourcePackResourceModel { FlameResourcePackModel(const BaseInstance&); ~FlameResourcePackModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } @@ -54,6 +58,8 @@ class FlameTexturePackModel : public TexturePackResourceModel { FlameTexturePackModel(const BaseInstance&); ~FlameTexturePackModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } @@ -75,6 +81,8 @@ class FlameShaderPackModel : public ShaderPackResourceModel { FlameShaderPackModel(const BaseInstance&); ~FlameShaderPackModel() override = default; + bool optedOut(const ModPlatform::IndexedVersion& ver) const override; + private: [[nodiscard]] QString debugName() const override { return Flame::debugName() + " (Model)"; } [[nodiscard]] QString metaEntryBase() const override { return Flame::metaEntryBase(); } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 23373ec9d..66a51decd 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -44,11 +44,6 @@ namespace ResourceDownload { -static bool isOptedOut(ModPlatform::IndexedVersion const& ver) -{ - return ver.downloadUrl.isEmpty(); -} - FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ModPage(dialog, instance) { m_model = new FlameModModel(instance); @@ -66,19 +61,6 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance& instance) : m_ui->packDescription->setMetaEntry(metaEntryBase()); } -auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders) const -> bool -{ - return ver.mcVersion.contains(mineVer) && !ver.downloadUrl.isEmpty() && - (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders); -} - -bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameModPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -113,11 +95,6 @@ FlameResourcePackPage::FlameResourcePackPage(ResourcePackDownloadDialog* dialog, m_ui->packDescription->setMetaEntry(metaEntryBase()); } -bool FlameResourcePackPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameResourcePackPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -152,11 +129,6 @@ FlameTexturePackPage::FlameTexturePackPage(TexturePackDownloadDialog* dialog, Ba m_ui->packDescription->setMetaEntry(metaEntryBase()); } -bool FlameTexturePackPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameTexturePackPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -191,11 +163,6 @@ FlameShaderPackPage::FlameShaderPackPage(ShaderPackDownloadDialog* dialog, BaseI m_ui->packDescription->setMetaEntry(metaEntryBase()); } -bool FlameShaderPackPage::optedOut(ModPlatform::IndexedVersion& ver) const -{ - return isOptedOut(ver); -} - void FlameShaderPackPage::openUrl(const QUrl& url) { if (url.scheme().isEmpty()) { @@ -232,4 +199,9 @@ auto FlameShaderPackPage::shouldDisplay() const -> bool return true; } +unique_qobject_ptr FlameModPage::createFilterWidget() +{ + return ModFilterWidget::create(&static_cast(m_base_instance), false, this); +} + } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index f2f5cecad..88ef404fb 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -94,12 +94,8 @@ class FlameModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - bool validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders = {}) const override; - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; + unique_qobject_ptr createFilterWidget() override; }; class FlameResourcePackPage : public ResourcePackResourcePage { @@ -124,8 +120,6 @@ class FlameResourcePackPage : public ResourcePackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; }; @@ -151,8 +145,6 @@ class FlameTexturePackPage : public TexturePackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; }; @@ -178,8 +170,6 @@ class FlameShaderPackPage : public ShaderPackResourcePage { [[nodiscard]] inline auto helpPage() const -> QString override { return ""; } - bool optedOut(ModPlatform::IndexedVersion& ver) const override; - void openUrl(const QUrl& url) override; }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index a4197b225..ab015ff0e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -63,13 +63,6 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance& instan m_ui->packDescription->setMetaEntry(metaEntryBase()); } -auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, - QString mineVer, - std::optional loaders) const -> bool -{ - return ver.mcVersion.contains(mineVer) && (!loaders.has_value() || !ver.loaders || loaders.value() & ver.loaders); -} - ModrinthResourcePackPage::ModrinthResourcePackPage(ResourcePackDownloadDialog* dialog, BaseInstance& instance) : ResourcePackResourcePage(dialog, instance) { @@ -144,4 +137,8 @@ auto ModrinthShaderPackPage::shouldDisplay() const -> bool return true; } +unique_qobject_ptr ModrinthModPage::createFilterWidget() +{ + return ModFilterWidget::create(&static_cast(m_base_instance), true, this); +} } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 311bcfe32..0a715d747 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -93,8 +93,7 @@ class ModrinthModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } - auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, std::optional loaders = {}) const - -> bool override; + unique_qobject_ptr createFilterWidget() override; }; class ModrinthResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index f826c1afe..af4594335 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -84,7 +84,7 @@ CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(" this->installEventFilter(this); } -void CheckComboBox::setModel(QAbstractItemModel* new_model) +void CheckComboBox::setSourceModel(QAbstractItemModel* new_model) { auto proxy = new CheckComboModel(this); proxy->setSourceModel(new_model); diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h index 277dd5fb8..f1d9771cc 100644 --- a/launcher/ui/widgets/CheckComboBox.h +++ b/launcher/ui/widgets/CheckComboBox.h @@ -28,7 +28,7 @@ class CheckComboBox : public QComboBox { explicit CheckComboBox(QWidget* parent = nullptr); virtual ~CheckComboBox() = default; - virtual void hidePopup() override; + void hidePopup() override; QString defaultText() const; void setDefaultText(const QString& text); @@ -41,7 +41,7 @@ class CheckComboBox : public QComboBox { QStringList checkedItems() const; - virtual void setModel(QAbstractItemModel* model) override; + void setSourceModel(QAbstractItemModel* model); public slots: void setCheckedItems(const QStringList& items); diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 5a67757b5..dde5d7502 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -9,9 +9,9 @@ #include "Application.h" #include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) { - return unique_qobject_ptr(new ModFilterWidget(instance, parent)); + return unique_qobject_ptr(new ModFilterWidget(instance, extendedSupport, parent)); } class VersionBasicModel : public QIdentityProxyModel { @@ -28,23 +28,33 @@ class VersionBasicModel : public QIdentityProxyModel { } }; -ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) { ui->setupUi(this); m_versions_proxy = new VersionProxyModel(this); + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); auto proxy = new VersionBasicModel(this); proxy->setSourceModel(m_versions_proxy); - ui->versionsCb->setModel(proxy); - ui->versionsCb->setSeparator("| "); - m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + if (!extendedSupport) { + ui->versionsSimpleCb->setModel(proxy); + ui->versionsCb->hide(); + ui->snapshotsCb->hide(); + ui->envBox->hide(); + } else { + ui->versionsCb->setSourceModel(proxy); + ui->versionsCb->setSeparator("| "); + ui->versionsSimpleCb->hide(); + } ui->versionsCb->setStyleSheet("combobox-popup: 0;"); + ui->versionsSimpleCb->setStyleSheet("combobox-popup: 0;"); connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->versionsSimpleCb, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); @@ -61,6 +71,11 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, QWidget* parent) connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); + connect(ui->releaseCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->betaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + setHidden(true); loadVersionList(); prepareBasicFilter(); @@ -196,4 +211,31 @@ void ModFilterWidget::onHideInstalledFilterChanged() emit filterUnchanged(); } +void ModFilterWidget::onReleaseFilterChanged() +{ + std::list releases; + if (ui->releaseCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Release)); + if (ui->betaCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Beta)); + if (ui->alphaCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Alpha)); + if (ui->unknownCb->isChecked()) + releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Unknown)); + m_filter_changed = releases != m_filter->releases; + m_filter->releases = releases; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); +} + +void ModFilterWidget::onVersionFilterTextChanged(QString version) +{ + m_filter->versions.clear(); + m_filter->versions.push_front(version); + m_filter_changed = true; + emit filterChanged(); +} + #include "ModFilterWidget.moc" diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index b92437a4f..50e6b3762 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -22,41 +22,44 @@ class ModFilterWidget : public QTabWidget { public: struct Filter { std::list versions; + std::list releases; ModPlatform::ModLoaderTypes loaders; QString side; bool hideInstalled; bool operator==(const Filter& other) const { - return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions; + return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions && + releases == other.releases; } bool operator!=(const Filter& other) const { return !(*this == other); } }; - static unique_qobject_ptr create(MinecraftInstance* instance, QWidget* parent = nullptr); + static unique_qobject_ptr create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; auto changed() const -> bool { return m_filter_changed; } + signals: + void filterChanged(); + void filterUnchanged(); + private: - ModFilterWidget(MinecraftInstance* instance, QWidget* parent = nullptr); + ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); void loadVersionList(); void prepareBasicFilter(); private slots: void onVersionFilterChanged(); + void onVersionFilterTextChanged(QString version); + void onReleaseFilterChanged(); void onLoadersFilterChanged(); void onSideFilterChanged(); void onHideInstalledFilterChanged(); void onIncludeSnapshotsChanged(); - public: - signals: - void filterChanged(); - void filterUnchanged(); - private: Ui::ModFilterWidget* ui; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 3113f2df2..d962ea44a 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -6,7 +6,7 @@ 0 0 - 400 + 460 127 @@ -17,13 +17,20 @@ - 2 + 0 Minecraft versions + + + + Include Snapshots + + + @@ -40,12 +47,8 @@ - - - - Include Snapshots - - + + From 9aac8e389f4e5daaf05826691b6bdb21bfb06e09 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 18:48:55 +0300 Subject: [PATCH 038/139] made release type visible Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 15 +++++++++++++++ launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 21 ++++++++++++++------- launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/minecraft/mod/Resource.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 3 +++ launcher/modplatform/packwiz/Packwiz.h | 1 + launcher/ui/widgets/ModFilterWidget.cpp | 2 +- launcher/ui/widgets/ModFilterWidget.h | 2 +- 9 files changed, 38 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 22e652319..f301cc949 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -47,6 +47,7 @@ #include "minecraft/mod/ModDetails.h" #include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" +#include "modplatform/ModIndex.h" static ModPlatform::ProviderCapabilities ProviderCaps; @@ -132,6 +133,13 @@ std::pair Mod::compare(const Resource& other, SortType type) const return { compare_result, type == SortType::MC_VERSIONS }; break; } + case SortType::RELEASE_TYPE: { + if (releaseType() > cast_other->releaseType()) + return { 1, type == SortType::RELEASE_TYPE }; + else if (releaseType() < cast_other->releaseType()) + return { -1, type == SortType::RELEASE_TYPE }; + break; + } } return { 0, false }; } @@ -262,6 +270,13 @@ auto Mod::side() const -> Metadata::ModSide return Metadata::ModSide::UniversalSide; } +auto Mod::releaseType() const -> ModPlatform::IndexedVersionType +{ + if (metadata()) + return metadata()->releaseType; + return ModPlatform::IndexedVersionType::VersionType::Unknown; +} + auto Mod::loaders() const -> ModPlatform::ModLoaderTypes { if (metadata()) diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 92de65889..bf503bc80 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -74,6 +74,7 @@ class Mod : public Resource { auto side() const -> Metadata::ModSide; auto loaders() const -> ModPlatform::ModLoaderTypes; auto mcVersions() const -> QStringList; + auto releaseType() const -> ModPlatform::IndexedVersionType; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index ef4ab957c..fb3688d5d 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -64,16 +64,17 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = - QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders", "Miecraft Versions" }); + m_column_names = QStringList( + { "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Side", "Loaders", "Miecraft Versions", "Release Type" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), - tr("Side"), tr("Loaders"), tr("Miecraft Versions") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, - SortType::PROVIDER, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS }; + tr("Side"), tr("Loaders"), tr("Miecraft Versions"), tr("Release Type") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, + SortType::PROVIDER, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS, SortType::RELEASE_TYPE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -128,6 +129,9 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const case McVersionsColumn: { return at(row)->mcVersions().join(", "); } + case ReleaseTypeColumn: { + return at(row)->releaseType().toString(); + } default: return QVariant(); } @@ -180,6 +184,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case SideColumn: case LoadersColumn: case McVersionsColumn: + case ReleaseTypeColumn: return columnNames().at(section); default: return QVariant(); @@ -203,6 +208,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The mod loader."); case McVersionsColumn: return tr("The supported minecraft versions."); + case ReleaseTypeColumn: + return tr("The release type."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index f9a87a741..a243a3c92 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -71,6 +71,7 @@ class ModFolderModel : public ResourceFolderModel { SideColumn, LoadersColumn, McVersionsColumn, + ReleaseTypeColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c46980837..101f65271 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS, MC_VERSIONS }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIDE, LOADERS, MC_VERSIONS, RELEASE_TYPE }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 0692a627d..1672cdf3d 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -117,6 +117,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP mod.loaders = mod_version.loaders; mod.mcVersions = mod_version.mcVersion; mod.mcVersions.sort(); + mod.releaseType = mod_version.version_type; return mod; } @@ -209,6 +210,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) { "side", sideToString(mod.side).toStdString() }, { "loaders", loaders }, { "mcVersions", mcVersions }, + { "releaseType", mod.releaseType.toString().toStdString() }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -293,6 +295,7 @@ 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()) { for (auto&& loader : *loaders.as_array()) { if (loader.is_string()) { diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 5cf99e74a..50cce9c66 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -43,6 +43,7 @@ class V1 { Side side{ Side::UniversalSide }; ModPlatform::ModLoaderTypes loaders; QStringList mcVersions; + ModPlatform::IndexedVersionType releaseType; // [download] QString mode{}; diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index dde5d7502..d702173f1 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -148,7 +148,7 @@ void ModFilterWidget::onIncludeSnapshotsChanged() m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); } -void ModFilterWidget::onVersionFilterChanged() +void ModFilterWidget::onVersionFilterChanged(int) { auto versions = ui->versionsCb->checkedItems(); m_filter->versions.clear(); diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 50e6b3762..b6edcb21e 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -52,7 +52,7 @@ class ModFilterWidget : public QTabWidget { void prepareBasicFilter(); private slots: - void onVersionFilterChanged(); + void onVersionFilterChanged(int); void onVersionFilterTextChanged(QString version); void onReleaseFilterChanged(); void onLoadersFilterChanged(); From 35d62cc5f2d79e499791a20a3997bf91137aadc0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 19:05:31 +0300 Subject: [PATCH 039/139] Updated dependencies Signed-off-by: Trial97 --- .../mod/tasks/GetModDependenciesTask.cpp | 15 ++++++++++++--- launcher/ui/widgets/ModFilterWidget.cpp | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index deec0db7c..625ab23bb 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -45,6 +45,14 @@ static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value(); } +static bool checkDependencies(std::shared_ptr sel, + Version mcVersion, + ModPlatform::ModLoaderTypes loaders) +{ + return (sel->pack->versions.isEmpty() || sel->version.mcVersion.contains(mcVersion.toString())) && + (!loaders || !sel->version.loaders || sel->version.loaders & loaders); +} + GetModDependenciesTask::GetModDependenciesTask(QObject* parent, BaseInstance* instance, ModFolderModel* folder, @@ -67,9 +75,10 @@ GetModDependenciesTask::GetModDependenciesTask(QObject* parent, void GetModDependenciesTask::prepare() { for (auto sel : m_selected) { - for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) { - addTask(prepareDependencyTask(dep, sel->pack->provider, 20)); - } + if (checkDependencies(sel, m_version, m_loaderType)) + for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) { + addTask(prepareDependencyTask(dep, sel->pack->provider, 20)); + } } } diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index d702173f1..0dff97510 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -53,7 +53,7 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSuppo ui->versionsCb->setStyleSheet("combobox-popup: 0;"); ui->versionsSimpleCb->setStyleSheet("combobox-popup: 0;"); connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); - connect(ui->versionsCb, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->versionsCb, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); connect(ui->versionsSimpleCb, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); From 41c9ca4f8ae0d18c53a5523b81e31d5d4e42072f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 22:15:07 +0300 Subject: [PATCH 040/139] Added categories filter Signed-off-by: Trial97 --- launcher/modplatform/ModIndex.h | 5 +++ launcher/modplatform/ResourceAPI.h | 1 + launcher/modplatform/flame/FlameAPI.cpp | 41 +++++++++++++++++++ launcher/modplatform/flame/FlameAPI.h | 7 ++++ launcher/modplatform/modrinth/ModrinthAPI.cpp | 38 +++++++++++++++++ launcher/modplatform/modrinth/ModrinthAPI.h | 15 +++++++ launcher/ui/pages/modplatform/ModModel.cpp | 5 ++- launcher/ui/pages/modplatform/ModPage.cpp | 1 + launcher/ui/pages/modplatform/ModPage.h | 2 + .../modplatform/flame/FlameResourcePages.cpp | 14 +++++++ .../modplatform/flame/FlameResourcePages.h | 3 ++ .../modrinth/ModrinthResourcePages.cpp | 11 +++++ .../modrinth/ModrinthResourcePages.h | 3 ++ launcher/ui/widgets/ModFilterWidget.cpp | 39 ++++++++++++++++-- launcher/ui/widgets/ModFilterWidget.h | 11 ++++- launcher/ui/widgets/ModFilterWidget.ui | 40 +++++++++++++++++- 16 files changed, 230 insertions(+), 6 deletions(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 7c472ec9b..a8ec6e7b3 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -191,6 +191,11 @@ constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept return x && !(x & (x - 1)); } +struct Category { + QString name; + QString id; +}; + } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 732c66621..97a38ca69 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -74,6 +74,7 @@ class ResourceAPI { std::optional loaders; std::optional > versions; std::optional side; + std::optional categoryIds; }; struct SearchCallbacks { std::function on_succeed; diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp index e99ce3a56..cc3f31e32 100644 --- a/launcher/modplatform/flame/FlameAPI.cpp +++ b/launcher/modplatform/flame/FlameAPI.cpp @@ -3,10 +3,12 @@ // SPDX-License-Identifier: GPL-3.0-only #include "FlameAPI.h" +#include #include "FlameModIndex.h" #include "Application.h" #include "Json.h" +#include "modplatform/ModIndex.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" #include "net/NetJob.h" @@ -222,3 +224,42 @@ QList FlameAPI::getSortingMethods() const { return s_sorts; } + +Task::Ptr FlameAPI::getModCategories(std::shared_ptr response) +{ + auto netJob = makeShared(QString("Flame::GetCategories"), APPLICATION->network()); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl("https://api.curseforge.com/v1/categories?gameId=432&classId=6"), response)); + QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; }); + return netJob; +} + +QList FlameAPI::loadModCategories(std::shared_ptr response) +{ + QList categories; + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from categories at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return categories; + } + + try { + auto obj = Json::requireObject(doc); + auto arr = Json::requireArray(obj, "data"); + + for (auto val : arr) { + auto cat = Json::requireObject(val); + auto id = Json::requireInteger(cat, "id"); + auto name = Json::requireString(cat, "name"); + categories.push_back({ name, QString::number(id) }); + } + + } catch (Json::JsonException& e) { + qCritical() << "Failed to parse response from a version request."; + qCritical() << e.what(); + qDebug() << doc; + } + return categories; +}; \ No newline at end of file diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index e22d8f0d8..dfe76f9d5 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include "modplatform/ModIndex.h" @@ -22,6 +23,9 @@ class FlameAPI : public NetworkResourceAPI { Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr response) const; Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr response) const; + static Task::Ptr getModCategories(std::shared_ptr response); + static QList loadModCategories(std::shared_ptr response); + [[nodiscard]] auto getSortingMethods() const -> QList override; static inline auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool @@ -96,6 +100,9 @@ class FlameAPI : public NetworkResourceAPI { get_arguments.append("sortOrder=desc"); if (args.loaders.has_value()) get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(args.loaders.value()))); + if (args.categoryIds.has_value() && !args.categoryIds->empty()) + get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(","))); + get_arguments.append(gameVersionStr); return "https://api.curseforge.com/v1/mods/search?gameId=432&" + get_arguments.join('&'); diff --git a/launcher/modplatform/modrinth/ModrinthAPI.cpp b/launcher/modplatform/modrinth/ModrinthAPI.cpp index f453f5cb9..d3f006e6f 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.cpp +++ b/launcher/modplatform/modrinth/ModrinthAPI.cpp @@ -122,3 +122,41 @@ QList ModrinthAPI::getSortingMethods() const { return s_sorts; } + +Task::Ptr ModrinthAPI::getModCategories(std::shared_ptr response) +{ + auto netJob = makeShared(QString("Modrinth::GetCategories"), APPLICATION->network()); + netJob->addNetAction(Net::ApiDownload::makeByteArray(QUrl(BuildConfig.MODRINTH_PROD_URL + "/tag/category"), response)); + QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Modrinth failed to get categories:" << msg; }); + return netJob; +} + +QList ModrinthAPI::loadModCategories(std::shared_ptr response) +{ + QList categories; + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); + if (parse_error.error != QJsonParseError::NoError) { + qWarning() << "Error while parsing JSON response from categories at " << parse_error.offset + << " reason: " << parse_error.errorString(); + qWarning() << *response; + return categories; + } + + try { + auto arr = Json::requireArray(doc); + + for (auto val : arr) { + auto cat = Json::requireObject(val); + auto name = Json::requireString(cat, "name"); + if (Json::ensureString(cat, "project_type", "") == "mod") + categories.push_back({ name, name }); + } + + } catch (Json::JsonException& e) { + qCritical() << "Failed to parse response from a version request."; + qCritical() << e.what(); + qDebug() << doc; + } + return categories; +}; \ No newline at end of file diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 8e0f9fe22..d1f8f712a 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -30,6 +30,9 @@ class ModrinthAPI : public NetworkResourceAPI { Task::Ptr getProjects(QStringList addonIds, std::shared_ptr response) const override; + static Task::Ptr getModCategories(std::shared_ptr response); + static QList loadModCategories(std::shared_ptr response); + public: [[nodiscard]] auto getSortingMethods() const -> QList override; @@ -56,6 +59,15 @@ class ModrinthAPI : public NetworkResourceAPI { return l.join(','); } + static auto getCategoriesFilters(QStringList categories) -> const QString + { + QStringList l; + for (auto cat : categories) { + l << QString("\"categories:%1\"").arg(cat); + } + return l.join(','); + } + static auto getSideFilters(QString side) -> const QString { if (side.isEmpty() || side == "both") { @@ -99,6 +111,9 @@ class ModrinthAPI : public NetworkResourceAPI { if (!side.isEmpty()) facets_list.append(QString("[%1]").arg(side)); } + if (args.categoryIds.has_value() && !args.categoryIds->empty()) + facets_list.append(QString("[%1]").arg(getCategoriesFilters(args.categoryIds.value()))); + facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type))); return QString("[%1]").arg(facets_list.join(',')); diff --git a/launcher/ui/pages/modplatform/ModModel.cpp b/launcher/ui/pages/modplatform/ModModel.cpp index 64ae7de36..61a01ddd2 100644 --- a/launcher/ui/pages/modplatform/ModModel.cpp +++ b/launcher/ui/pages/modplatform/ModModel.cpp @@ -25,6 +25,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() Q_ASSERT(m_filter); std::optional> versions{}; + std::optional categories{}; auto loaders = profile->getSupportedModLoaders(); // Version filter @@ -32,11 +33,13 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments() versions = m_filter->versions; if (m_filter->loaders) loaders = m_filter->loaders; + if (!m_filter->categoryIds.empty()) + categories = m_filter->categoryIds; auto side = m_filter->side; auto sort = getCurrentSortingMethodByIndex(); - return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side }; + return { ModPlatform::ResourceType::MOD, m_next_search_offset, m_search_term, sort, loaders, versions, side, categories }; } ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(QModelIndex& entry) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index e6106a0de..e4da1a962 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -78,6 +78,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, [&] { m_ui->searchButton->setStyleSheet("text-decoration: none"); }); + prepareProviderCategories(); } /******** Callbacks to events in the UI (set up in the derived classes) ********/ diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index c878bc004..d6a7d64e4 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -61,6 +61,8 @@ class ModPage : public ResourcePage { protected: ModPage(ModDownloadDialog* dialog, BaseInstance& instance); + virtual void prepareProviderCategories(){}; + protected slots: virtual void filterMods(); void triggerSearch() override; diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 66a51decd..7a6046824 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -37,6 +37,10 @@ */ #include "FlameResourcePages.h" +#include +#include +#include "modplatform/ModIndex.h" +#include "modplatform/flame/FlameAPI.h" #include "ui_ResourcePage.h" #include "FlameResourceModels.h" @@ -204,4 +208,14 @@ unique_qobject_ptr FlameModPage::createFilterWidget() return ModFilterWidget::create(&static_cast(m_base_instance), false, this); } +void FlameModPage::prepareProviderCategories() +{ + auto response = std::make_shared(); + auto task = FlameAPI::getModCategories(response); + QObject::connect(task.get(), &Task::succeeded, [this, response]() { + auto categories = FlameAPI::loadModCategories(response); + m_filter_widget->setCategories(categories); + }); + task->start(); +}; } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 88ef404fb..0288023ca 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -96,6 +96,9 @@ class FlameModPage : public ModPage { void openUrl(const QUrl& url) override; unique_qobject_ptr createFilterWidget() override; + + protected: + virtual void prepareProviderCategories() override; }; class FlameResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index ab015ff0e..5b5099863 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -141,4 +141,15 @@ unique_qobject_ptr ModrinthModPage::createFilterWidget() { return ModFilterWidget::create(&static_cast(m_base_instance), true, this); } + +void ModrinthModPage::prepareProviderCategories() +{ + auto response = std::make_shared(); + auto task = ModrinthAPI::getModCategories(response); + QObject::connect(task.get(), &Task::succeeded, [this, response]() { + auto categories = ModrinthAPI::loadModCategories(response); + m_filter_widget->setCategories(categories); + }); + task->start(); +}; } // namespace ResourceDownload diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index 0a715d747..a8dcd1de5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -94,6 +94,9 @@ class ModrinthModPage : public ModPage { [[nodiscard]] inline auto helpPage() const -> QString override { return "Mod-platform"; } unique_qobject_ptr createFilterWidget() override; + + protected: + virtual void prepareProviderCategories() override; }; class ModrinthResourcePackPage : public ResourcePackResourcePage { diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 0dff97510..a3b0a0cc0 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,6 +1,8 @@ #include "ModFilterWidget.h" -#include -#include +#include +#include +#include +#include #include "BaseVersionList.h" #include "meta/Index.h" #include "modplatform/ModIndex.h" @@ -76,6 +78,8 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSuppo connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); + connect(ui->categoriesList, &QListWidget::itemClicked, this, &ModFilterWidget::onCategoryClicked); + setHidden(true); loadVersionList(); prepareBasicFilter(); @@ -238,4 +242,33 @@ void ModFilterWidget::onVersionFilterTextChanged(QString version) emit filterChanged(); } -#include "ModFilterWidget.moc" +void ModFilterWidget::setCategories(QList categories) +{ + ui->categoriesList->clear(); + m_categories = categories; + for (auto cat : categories) { + auto item = new QListWidgetItem(cat.name, ui->categoriesList); + item->setFlags(item->flags() & (~Qt::ItemIsUserCheckable)); + item->setCheckState(Qt::Unchecked); + ui->categoriesList->addItem(item); + } +} + +void ModFilterWidget::onCategoryClicked(QListWidgetItem* item) +{ + if (item) + item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); + m_filter->categoryIds.clear(); + for (auto i = 0; i < ui->categoriesList->count(); i++) { + auto item = ui->categoriesList->item(i); + if (item->checkState() == Qt::Checked) { + auto c = std::find_if(m_categories.cbegin(), m_categories.cend(), [item](auto v) { return v.name == item->text(); }); + if (c != m_categories.cend()) + m_filter->categoryIds << c->id; + } + } + m_filter_changed = true; + emit filterChanged(); +}; + +#include "ModFilterWidget.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index b6edcb21e..402e11ae4 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include "Version.h" @@ -26,11 +28,12 @@ class ModFilterWidget : public QTabWidget { ModPlatform::ModLoaderTypes loaders; QString side; bool hideInstalled; + QStringList categoryIds; bool operator==(const Filter& other) const { return hideInstalled == other.hideInstalled && side == other.side && loaders == other.loaders && versions == other.versions && - releases == other.releases; + releases == other.releases && categoryIds == other.categoryIds; } bool operator!=(const Filter& other) const { return !(*this == other); } }; @@ -45,6 +48,9 @@ class ModFilterWidget : public QTabWidget { void filterChanged(); void filterUnchanged(); + public slots: + void setCategories(QList); + private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); @@ -59,6 +65,7 @@ class ModFilterWidget : public QTabWidget { void onSideFilterChanged(); void onHideInstalledFilterChanged(); void onIncludeSnapshotsChanged(); + void onCategoryClicked(QListWidgetItem* item); private: Ui::ModFilterWidget* ui; @@ -69,4 +76,6 @@ class ModFilterWidget : public QTabWidget { Meta::VersionList::Ptr m_version_list; VersionProxyModel* m_versions_proxy = nullptr; + + QList m_categories; }; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index d962ea44a..56cd33fa6 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -7,7 +7,7 @@ 0 0 460 - 127 + 132 @@ -145,6 +145,44 @@
+ + + Categories + + + + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerItem + + + QListView::LeftToRight + + + true + + + QListView::ListMode + + + 0 + + + + + Others From d151e97e3a24667063ad4fe9cd6f5aca6cdf7b21 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 21 Oct 2023 22:40:37 +0300 Subject: [PATCH 041/139] Fixed some headers Signed-off-by: Trial97 --- launcher/minecraft/mod/MetadataHandler.h | 1 + launcher/minecraft/mod/Mod.cpp | 1 + launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 1 + launcher/minecraft/mod/ModFolderModel.h | 1 + launcher/minecraft/mod/Resource.h | 35 +++++++++++++++++++ launcher/modplatform/ModIndex.cpp | 1 + launcher/modplatform/ResourceAPI.h | 1 + .../modrinth/ModrinthPackIndex.cpp | 1 + launcher/modplatform/packwiz/Packwiz.cpp | 1 + launcher/modplatform/packwiz/Packwiz.h | 1 + launcher/ui/pages/modplatform/ModPage.cpp | 1 + .../ui/pages/modplatform/ResourcePage.cpp | 1 + .../modplatform/flame/FlameResourcePages.cpp | 1 + .../modplatform/flame/FlameResourcePages.h | 1 + .../modrinth/ModrinthResourcePages.cpp | 1 + .../modrinth/ModrinthResourcePages.h | 1 + launcher/ui/widgets/CheckComboBox.cpp | 6 ++-- launcher/ui/widgets/CheckComboBox.h | 2 +- launcher/ui/widgets/ModFilterWidget.cpp | 35 +++++++++++++++++++ launcher/ui/widgets/ModFilterWidget.h | 35 +++++++++++++++++++ 21 files changed, 124 insertions(+), 5 deletions(-) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index ccdd7d559..fb3a10133 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 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 diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index f301cc949..e89131034 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index bf503bc80..e9baeed58 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 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 diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index fb3688d5d..8e31fa34b 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index a243a3c92..31c739e5b 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 101f65271..0710a400b 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index d2113320a..20bd3f827 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 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 diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 97a38ca69..a306dcc52 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -4,6 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 5de645220..a2bda2ec5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 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 diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 1672cdf3d..c8899345e 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 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 diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 50cce9c66..95362bbfe 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 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 diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index e4da1a962..8fd42b427 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 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 diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 158b13c96..35c4afc0d 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad + * Copyright (c) 2023 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 diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp index 7a6046824..d82c76c3a 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.cpp @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 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 diff --git a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h index 0288023ca..6eef3e435 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourcePages.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourcePages.h @@ -5,6 +5,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad + * Copyright (c) 2023 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 diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp index 5b5099863..26fe46a54 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp @@ -4,6 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h index a8dcd1de5..eaf6129a5 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourcePages.h @@ -4,6 +4,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 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 diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index af4594335..8117baa58 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -17,17 +17,15 @@ */ #include "CheckComboBox.h" -#include -#include -#include #include +#include #include #include #include #include +#include #include -#include "BaseVersionList.h" class CheckComboModel : public QIdentityProxyModel { Q_OBJECT diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h index f1d9771cc..4d6daf5a4 100644 --- a/launcher/ui/widgets/CheckComboBox.h +++ b/launcher/ui/widgets/CheckComboBox.h @@ -18,8 +18,8 @@ #pragma once -#include #include +#include class CheckComboBox : public QComboBox { Q_OBJECT diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index a3b0a0cc0..b7da6878a 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "ModFilterWidget.h" #include #include diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 402e11ae4..dd98d9afa 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2023 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 . + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #pragma once #include From 10ae808da90c819df03c9ea78a3248ddb66179d1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 22 Oct 2023 00:06:08 +0300 Subject: [PATCH 042/139] fixed codeql Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index b7da6878a..341823244 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -289,10 +289,10 @@ void ModFilterWidget::setCategories(QList categories) } } -void ModFilterWidget::onCategoryClicked(QListWidgetItem* item) +void ModFilterWidget::onCategoryClicked(QListWidgetItem* itm) { - if (item) - item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); + if (itm) + itm->setCheckState(itm->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); m_filter->categoryIds.clear(); for (auto i = 0; i < ui->categoriesList->count(); i++) { auto item = ui->categoriesList->item(i); From 6a19f2dae82ecda7790e8fcf6cd8cf16f3bcdaf0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 24 Oct 2023 11:19:19 +0300 Subject: [PATCH 043/139] fixed icon import Signed-off-by: Trial97 --- launcher/InstanceImportTask.cpp | 4 +++- launcher/icons/IconUtils.cpp | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 713787902..21ad834b4 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -334,13 +334,15 @@ void InstanceImportTask::processMultiMC() m_instIcon = instance.iconKey(); auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon); + if (importIconPath.isNull() || !QFile::exists(importIconPath)) + importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), "icon.png"); if (!importIconPath.isNull() && QFile::exists(importIconPath)) { // import icon auto iconList = APPLICATION->icons(); if (iconList->iconFileExists(m_instIcon)) { iconList->deleteIcon(m_instIcon); } - iconList->installIcons({ importIconPath }); + iconList->installIcon(importIconPath, m_instIcon); } } emitSucceeded(); diff --git a/launcher/icons/IconUtils.cpp b/launcher/icons/IconUtils.cpp index 99c38f47a..6825dd6da 100644 --- a/launcher/icons/IconUtils.cpp +++ b/launcher/icons/IconUtils.cpp @@ -52,8 +52,7 @@ QString findBestIconIn(const QString& folder, const QString& iconKey) while (it.hasNext()) { it.next(); auto fileInfo = it.fileInfo(); - - if (fileInfo.completeBaseName() == iconKey && isIconSuffix(fileInfo.suffix())) + if ((fileInfo.completeBaseName() == iconKey || fileInfo.fileName() == iconKey) && isIconSuffix(fileInfo.suffix())) return fileInfo.absoluteFilePath(); } return {}; From 263dc5af67b21719a690c3577c3e9d028fc1106a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 25 Oct 2023 19:56:26 +0300 Subject: [PATCH 044/139] Fixed remane and delete of selected skin Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index b3a593454..1b046a781 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -359,7 +359,7 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) int row = idx.row(); if (row < 0 || row >= m_skin_list.size()) return false; - auto skin = m_skin_list[row]; + auto& skin = m_skin_list[row]; auto newName = value.toString(); if (skin.name() != newName) { skin.rename(newName); diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 24197baeb..5419d3eed 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -229,8 +229,10 @@ void SkinManageDialog::on_steveBtn_toggled(bool checked) void SkinManageDialog::accept() { auto skin = m_list.skin(m_selected_skin); - if (!skin) + if (!skin) { reject(); + return; + } auto path = skin->getPath(); ProgressDialog prog(this); @@ -315,7 +317,7 @@ void SkinManageDialog::on_action_Delete_Skin_triggered(bool checked) return; if (m_list.getSkinIndex(m_selected_skin) == m_list.getSelectedAccountSkin()) { - CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning); + CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning)->exec(); return; } From 44cdf3f6979969c2de7953b58fd08836049fe5d5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 13:33:00 +0300 Subject: [PATCH 045/139] Fixed skin variant Signed-off-by: Trial97 --- launcher/minecraft/auth/Parsers.cpp | 2 +- launcher/minecraft/skins/SkinList.cpp | 4 ++-- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/auth/Parsers.cpp b/launcher/minecraft/auth/Parsers.cpp index f6179a93e..a2b002c78 100644 --- a/launcher/minecraft/auth/Parsers.cpp +++ b/launcher/minecraft/auth/Parsers.cpp @@ -347,7 +347,7 @@ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) Skin skinOut; // fill in default skin info ourselves, as this endpoint doesn't provide it bool steve = isDefaultModelSteve(output.id); - skinOut.variant = steve ? "classic" : "slim"; + skinOut.variant = steve ? "CLASSIC" : "SLIM"; skinOut.url = steve ? SKIN_URL_STEVE : SKIN_URL_ALEX; // sadly we can't figure this out, but I don't think it really matters... skinOut.id = "00000000-0000-0000-0000-000000000000"; diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index 1b046a781..0505e4ced 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -106,7 +106,7 @@ bool SkinList::update() auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); - s.setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; @@ -114,7 +114,7 @@ bool SkinList::update() } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); - nskin->setModel(skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + nskin->setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); } } diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 5419d3eed..5c69be0ba 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -460,7 +460,7 @@ void SkinManageDialog::on_userBtn_clicked() dlg.execWithTask(job.get()); SkinModel s(path); - s.setModel(mcProfile.skin.variant == "slim" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(mcProfile.skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setURL(mcProfile.skin.url); if (m_capes.contains(mcProfile.currentCape)) { s.setCapeId(mcProfile.currentCape); From 8b8ea2d27057dd9cebe20586f2ab5fc8426d1c99 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 13:37:25 +0300 Subject: [PATCH 046/139] ensured that the variant is allways uppercase Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinList.cpp | 4 ++-- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/skins/SkinList.cpp b/launcher/minecraft/skins/SkinList.cpp index 0505e4ced..fd883ad52 100644 --- a/launcher/minecraft/skins/SkinList.cpp +++ b/launcher/minecraft/skins/SkinList.cpp @@ -106,7 +106,7 @@ bool SkinList::update() auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); - s.setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; @@ -114,7 +114,7 @@ bool SkinList::update() } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); - nskin->setModel(skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); + nskin->setModel(skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); } } diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 5c69be0ba..9320101c2 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -460,7 +460,7 @@ void SkinManageDialog::on_userBtn_clicked() dlg.execWithTask(job.get()); SkinModel s(path); - s.setModel(mcProfile.skin.variant == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); + s.setModel(mcProfile.skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setURL(mcProfile.skin.url); if (m_capes.contains(mcProfile.currentCape)) { s.setCapeId(mcProfile.currentCape); From 321bbf1fa8bbdf37a5b81aa8c69512e1b04028eb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 26 Oct 2023 19:06:38 +0300 Subject: [PATCH 047/139] fixed asan stuff Signed-off-by: Trial97 --- launcher/minecraft/skins/SkinUpload.cpp | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index 4a88faedf..dc1bc0bf9 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -49,7 +49,7 @@ SkinUpload::SkinUpload(QString token, QString path, QString variant) : NetReques QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { - QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); QHttpPart skin; skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 9320101c2..8028a719c 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -74,7 +74,7 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct) contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); contentsWidget->installEventFilter(this); - contentsWidget->setItemDelegate(new ListViewDelegate()); + contentsWidget->setItemDelegate(new ListViewDelegate(this)); contentsWidget->setAcceptDrops(true); contentsWidget->setDropIndicatorShown(true); From 3cbc63bb9f5265a55294e546f3812b52d4ef7346 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 2 Nov 2023 12:18:24 +0200 Subject: [PATCH 048/139] added size column Signed-off-by: Trial97 --- launcher/minecraft/mod/Mod.cpp | 4 ++- launcher/minecraft/mod/ModFolderModel.cpp | 24 +++++++++++----- launcher/minecraft/mod/ModFolderModel.h | 2 +- launcher/minecraft/mod/Resource.cpp | 7 +++++ launcher/minecraft/mod/Resource.h | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 28 +++++++++++-------- launcher/minecraft/mod/ResourceFolderModel.h | 12 ++++---- .../minecraft/mod/ResourcePackFolderModel.cpp | 19 +++++++++---- .../minecraft/mod/ResourcePackFolderModel.h | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 16 +++++++---- .../minecraft/mod/TexturePackFolderModel.h | 2 +- 11 files changed, 78 insertions(+), 40 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 310946379..374e60d56 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -45,6 +45,7 @@ #include "MetadataHandler.h" #include "Version.h" #include "minecraft/mod/ModDetails.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" static ModPlatform::ProviderCapabilities ProviderCaps; @@ -87,7 +88,8 @@ std::pair Mod::compare(const Resource& other, SortType type) const default: case SortType::ENABLED: case SortType::NAME: - case SortType::DATE: { + case SortType::DATE: + case SortType::SIZE: { auto res = Resource::compare(other, type); if (res.first != 0) return res; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index a5f1489dd..5c6cdb00e 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,6 +52,8 @@ #include "Application.h" #include "Json.h" +#include "StringUtils.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" #include "minecraft/mod/tasks/ModFolderLoadTask.h" @@ -62,12 +64,15 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; + m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size" }); + m_column_names_translated = + QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, + SortType::DATE, SortType::PROVIDER, SortType::SIZE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, - QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, + QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const @@ -105,12 +110,14 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return QVariant(); } case Qt::ToolTipRole: - if (column == NAME_COLUMN) { + if (column == NameColumn) { if (at(row)->isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." @@ -124,7 +131,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const } return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); @@ -154,6 +161,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case DateColumn: case ProviderColumn: case ImageColumn: + case SizeColumn: return columnNames().at(section); default: return QVariant(); @@ -171,6 +179,8 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio return tr("The date and time this mod was last changed (or added)."); case ProviderColumn: return tr("Where the mod was downloaded from."); + case SizeColumn: + return tr("The size of the resource."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 61d840f9b..e830556ab 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -61,7 +61,7 @@ class QFileSystemWatcher; class ModFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index da806f0f4..79e52a881 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -89,6 +89,13 @@ std::pair Resource::compare(const Resource& other, SortType type) con if (dateTimeChanged() < other.dateTimeChanged()) return { -1, type == SortType::DATE }; break; + case SortType::SIZE: { + if (fileinfo().size() > other.fileinfo().size()) + return { 1, type == SortType::SIZE }; + if (fileinfo().size() < other.fileinfo().size()) + return { -1, type == SortType::SIZE }; + break; + } } return { 0, false }; diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c1ed49461..d94e4b368 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -15,7 +15,7 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; -enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; +enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 0503b660b..16ff01227 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -15,6 +15,7 @@ #include "FileSystem.h" #include "QVariantUtils.h" +#include "StringUtils.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "settings/Setting.h" @@ -410,15 +411,17 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: switch (column) { - case NAME_COLUMN: + case NameColumn: return m_resources[row]->name(); - case DATE_COLUMN: + case DateColumn: return m_resources[row]->dateTimeChanged(); + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return {}; } case Qt::ToolTipRole: - if (column == NAME_COLUMN) { + if (column == NameColumn) { if (at(row).isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." @@ -434,14 +437,14 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) + if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); return {}; } case Qt::CheckStateRole: switch (column) { - case ACTIVE_COLUMN: + case ActiveColumn: return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; default: return {}; @@ -480,24 +483,27 @@ QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orien switch (role) { case Qt::DisplayRole: switch (section) { - case ACTIVE_COLUMN: - case NAME_COLUMN: - case DATE_COLUMN: + case ActiveColumn: + case NameColumn: + case DateColumn: + case SizeColumn: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: { switch (section) { - case ACTIVE_COLUMN: + case ActiveColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("Is the resource enabled?"); - case NAME_COLUMN: + case NameColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The name of the resource."); - case DATE_COLUMN: + case DateColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The date and time this resource was last changed (or added)."); + case SizeColumn: + return tr("The size of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 60b8879c0..c282b8b80 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -96,7 +96,7 @@ class ResourceFolderModel : public QAbstractListModel { /* Qt behavior */ /* Basic columns */ - enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, NameColumn, DateColumn, SizeColumn, NUM_COLUMNS }; QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast(size()); } @@ -198,12 +198,12 @@ class ResourceFolderModel : public QAbstractListModel { protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! - QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; - QStringList m_column_names = { "Enable", "Name", "Last Modified" }; - QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; + QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::SIZE }; + QStringList m_column_names = { "Enable", "Name", "Last Modified", "Size" }; + QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Size") }; QList m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, - QHeaderView::ResizeToContents }; - QList m_columnsHideable = { false, false, true }; + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + QList m_columnsHideable = { false, false, true, true }; QDir m_dir; BaseInstance* m_instance; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f27431576..d94751629 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -42,19 +42,21 @@ #include #include "Application.h" +#include "StringUtils.h" #include "Version.h" +#include "minecraft/mod/Resource.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, - QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true }; + m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Size" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Size") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::SIZE }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, + QHeaderView::ResizeToContents, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; + m_columnsHideable = { false, true, false, true, true, true }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const @@ -85,6 +87,8 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const } case DateColumn: return m_resources[row]->dateTimeChanged(); + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return {}; @@ -139,6 +143,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O case PackFormatColumn: case DateColumn: case ImageColumn: + case SizeColumn: return columnNames().at(section); default: return {}; @@ -155,6 +160,8 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); case DateColumn: return tr("The date and time this resource pack was last changed (or added)."); + case SizeColumn: + return tr("The size of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 29c2c5995..755b9c4c6 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -7,7 +7,7 @@ class ResourcePackFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, SizeColumn, NUM_COLUMNS }; explicit ResourcePackFolderModel(const QString& dir, BaseInstance* instance); diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 5c5f2b7c1..ef241351f 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -37,6 +37,7 @@ #include "Application.h" +#include "StringUtils.h" #include "TexturePackFolderModel.h" #include "minecraft/mod/tasks/BasicFolderLoadTask.h" @@ -44,12 +45,12 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; - m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, + m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE }; + m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true }; + m_columnsHideable = { false, true, false, true, true }; } Task* TexturePackFolderModel::createUpdateTask() @@ -77,6 +78,8 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->name(); case DateColumn: return m_resources[row]->dateTimeChanged(); + case SizeColumn: + return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); default: return {}; } @@ -123,6 +126,7 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or case NameColumn: case DateColumn: case ImageColumn: + case SizeColumn: return columnNames().at(section); default: return {}; @@ -138,6 +142,8 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or case DateColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The date and time this resource was last changed (or added)."); + case SizeColumn: + return tr("The size of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index b975d8641..de90f879f 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -44,7 +44,7 @@ class TexturePackFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, SizeColumn, NUM_COLUMNS }; explicit TexturePackFolderModel(const QString& dir, std::shared_ptr instance); From cbb453a0edf5bde883627247b8284f02d18c4493 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 3 Nov 2023 22:55:10 +0200 Subject: [PATCH 049/139] minimize the permisions for extracted files Signed-off-by: Trial97 --- launcher/MMCZip.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 6172fe911..c3acd8eb6 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #if defined(LAUNCHER_APPLICATION) @@ -327,9 +328,20 @@ std::optional extractSubDir(QuaZip* zip, const QString& subdir, con } extracted.append(target_file_path); - QFile::setPermissions(target_file_path, - QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + auto fileInfo = QFileInfo(target_file_path); + if (fileInfo.isFile()) { + auto permissions = fileInfo.permissions(); + auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser | + QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther; + auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; + auto newPermisions = (permissions & maxPermisions) | minPermisions; + if (newPermisions != permissions) { + if (!QFile::setPermissions(target_file_path, permissions)) { + qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path)); + } + } + } qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; } while (zip->goToNextFile()); @@ -582,8 +594,20 @@ auto ExtractZipTask::extractZip() -> ZipResult } extracted.append(target_file_path); - QFile::setPermissions(target_file_path, - QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser); + auto fileInfo = QFileInfo(target_file_path); + if (fileInfo.isFile()) { + auto permissions = fileInfo.permissions(); + auto maxPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser | + QFileDevice::Permission::ReadGroup | QFileDevice::Permission::ReadOther; + auto minPermisions = QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; + + auto newPermisions = (permissions & maxPermisions) | minPermisions; + if (newPermisions != permissions) { + if (!QFile::setPermissions(target_file_path, permissions)) { + logWarning(tr("Could not fix permissions for %1").arg(target_file_path)); + } + } + } qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; } while (m_input->goToNextFile()); From 2b6d8b8a2d7aff89b7677a22980254115293dbd5 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 3 Nov 2023 23:02:17 +0200 Subject: [PATCH 050/139] updated tooltip messages Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 5c6cdb00e..5ba1795fb 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -180,7 +180,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case ProviderColumn: return tr("Where the mod was downloaded from."); case SizeColumn: - return tr("The size of the resource."); + return tr("The size of the mod."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index d94751629..e9c3d3043 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -161,7 +161,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O case DateColumn: return tr("The date and time this resource pack was last changed (or added)."); case SizeColumn: - return tr("The size of the resource."); + return tr("The size of the resource pack."); default: return {}; } From f828ea8929f9ba23f5f2d6a852c9634eebdf7786 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 4 Nov 2023 11:49:05 +0200 Subject: [PATCH 051/139] updated the texture pack tooltips Signed-off-by: Trial97 --- launcher/minecraft/mod/TexturePackFolderModel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index ef241351f..e3f369bb8 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -135,15 +135,15 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or switch (section) { case ActiveColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("Is the resource enabled?"); + return tr("Is the texture pack enabled?"); case NameColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("The name of the resource."); + return tr("The name of the texture pack."); case DateColumn: //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("The date and time this resource was last changed (or added)."); + return tr("The date and time this texture pack was last changed (or added)."); case SizeColumn: - return tr("The size of the resource."); + return tr("The size of the texture pack."); default: return {}; } From 5afe6600eea7d569c6f47dcd98226ca10c40aa62 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 4 Nov 2023 16:49:35 +0200 Subject: [PATCH 052/139] use fs::move instead of qt rename Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 3 +-- launcher/InstanceList.cpp | 3 +-- launcher/modplatform/flame/FlameInstanceCreationTask.cpp | 4 ++-- launcher/modplatform/legacy_ftb/PackInstallTask.cpp | 2 +- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 4 ++-- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index c7d5f85fa..794804985 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -652,8 +652,7 @@ bool move(const QString& source, const QString& dest) if (err) { qWarning() << "Failed to move file:" << QString::fromStdString(err.message()); - qDebug() << "Source file:" << source; - qDebug() << "Destination file:" << dest; + qDebug() << "Source file:" << source << ";Destination file:" << dest; } return err.value() == 0; diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 2b8f34293..8f0cbba79 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -953,7 +953,6 @@ bool InstanceList::commitStagedInstance(const QString& path, if (groupName.isEmpty() && !groupName.isNull()) groupName = QString(); - QDir dir; QString instID; InstancePtr inst; @@ -977,7 +976,7 @@ bool InstanceList::commitStagedInstance(const QString& path, return false; } } else { - if (!dir.rename(path, destination)) { + if (!FS::move(path, destination)) { qWarning() << "Failed to move" << path << "to" << destination; return false; } diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 2a26ce944..7e107f2d2 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -321,7 +321,7 @@ bool FlameCreationTask::createInstance() // Keep index file in case we need it some other time (like when changing versions) QString new_index_place(FS::PathCombine(parent_folder, "manifest.json")); FS::ensureFilePathExists(new_index_place); - QFile::rename(index_path, new_index_place); + FS::move(index_path, new_index_place); } catch (const JSONValidationError& e) { setError(tr("Could not understand pack manifest:\n") + e.cause()); @@ -335,7 +335,7 @@ bool FlameCreationTask::createInstance() Override::createOverrides("overrides", parent_folder, overridePath); QString mcPath = FS::PathCombine(m_stagingPath, "minecraft"); - if (!QFile::rename(overridePath, mcPath)) { + if (!FS::move(overridePath, mcPath)) { setError(tr("Could not rename the overrides folder:\n") + m_pack.overrides); return false; } diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp index 091296751..867773f58 100644 --- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp +++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp @@ -137,7 +137,7 @@ void PackInstallTask::install() QDir unzipMcDir(m_stagingPath + "/unzip/minecraft"); if (unzipMcDir.exists()) { // ok, found minecraft dir, move contents to instance dir - if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) { + if (!FS::move(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) { emitFailed(tr("Failed to move unzipped Minecraft!")); return; } diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index e732ad39c..f01c52d21 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -171,7 +171,7 @@ bool ModrinthCreationTask::createInstance() // Keep index file in case we need it some other time (like when changing versions) QString new_index_place(FS::PathCombine(parent_folder, "modrinth.index.json")); FS::ensureFilePathExists(new_index_place); - QFile::rename(index_path, new_index_place); + FS::move(index_path, new_index_place); auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft"); @@ -181,7 +181,7 @@ bool ModrinthCreationTask::createInstance() Override::createOverrides("overrides", parent_folder, override_path); // Apply the overrides - if (!QFile::rename(override_path, mcPath)) { + if (!FS::move(override_path, mcPath)) { setError(tr("Could not rename the overrides folder:\n") + "overrides"); return false; } From 1d67fc66466566955719d4ab599d8b5482182f46 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 5 Nov 2023 16:47:33 +0200 Subject: [PATCH 053/139] added special case for windows Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 794804985..5bb10b069 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -643,6 +643,19 @@ void ExternalLinkFileProcess::runLinkFile() qDebug() << "Process exited"; } +bool moveByCopy(const QString& source, const QString& dest) +{ + if (!copy(source, dest)()) { // copy + qDebug() << "Copy of" << source << "to" << dest << "failed!"; + return false; + } + if (!deletePath(source)) { // remove original + qDebug() << "Deletion of" << source << "failed!"; + return false; + }; + return true; +} + bool move(const QString& source, const QString& dest) { std::error_code err; @@ -650,12 +663,14 @@ bool move(const QString& source, const QString& dest) ensureFilePathExists(dest); fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err); - if (err) { - qWarning() << "Failed to move file:" << QString::fromStdString(err.message()); - qDebug() << "Source file:" << source << ";Destination file:" << dest; + if (err.value() != 0) { + if (moveByCopy(source, dest)) + return true; + qDebug() << "Move of" << source << "to" << dest << "failed!"; + qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value()); + return false; } - - return err.value() == 0; + return true; } bool deletePath(QString path) From b4bfc03e8b25d8eeea250560c1fc5e1c055677b2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 9 Nov 2023 21:48:46 +0200 Subject: [PATCH 054/139] Added button to refresh themes and catpacks Signed-off-by: Trial97 --- launcher/ui/themes/ThemeManager.cpp | 10 +++++++++ launcher/ui/themes/ThemeManager.h | 2 ++ .../ui/widgets/ThemeCustomizationWidget.cpp | 21 +++++++++++++++++++ .../ui/widgets/ThemeCustomizationWidget.h | 1 + .../ui/widgets/ThemeCustomizationWidget.ui | 7 +++++++ 5 files changed, 41 insertions(+) diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index 0bcac100c..cb90bc826 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -313,3 +313,13 @@ void ThemeManager::initializeCatPacks() } } } + +void ThemeManager::refresh() +{ + m_themes.clear(); + m_icons.clear(); + m_catPacks.clear(); + + initializeThemes(); + initializeCatPacks(); +}; \ No newline at end of file diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index b5c66677b..8ed587806 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -55,6 +55,8 @@ class ThemeManager { QString getCatPack(QString catName = ""); QList getValidCatPacks(); + void refresh(); + private: std::map> m_themes; std::map m_icons; diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index 0de97441f..a0e682bb9 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -39,6 +39,8 @@ ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(pa [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getApplicationThemesFolder().path()); }); connect(ui->catPackFolder, &QPushButton::clicked, this, [] { DesktopServices::openDirectory(APPLICATION->themeManager()->getCatPacksFolder().path()); }); + + connect(ui->refreshButton, &QPushButton::clicked, this, &ThemeCustomizationWidget::refresh); } ThemeCustomizationWidget::~ThemeCustomizationWidget() @@ -169,3 +171,22 @@ void ThemeCustomizationWidget::retranslate() { ui->retranslateUi(this); } + +void ThemeCustomizationWidget::refresh() +{ + applySettings(); + disconnect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); + disconnect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyWidgetTheme); + disconnect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyCatTheme); + APPLICATION->themeManager()->refresh(); + ui->iconsComboBox->clear(); + ui->widgetStyleComboBox->clear(); + ui->backgroundCatComboBox->clear(); + loadSettings(); + connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); + connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &ThemeCustomizationWidget::applyWidgetTheme); + connect(ui->backgroundCatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme); +}; \ No newline at end of file diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.h b/launcher/ui/widgets/ThemeCustomizationWidget.h index cef5fb6c6..6977b8495 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.h +++ b/launcher/ui/widgets/ThemeCustomizationWidget.h @@ -44,6 +44,7 @@ class ThemeCustomizationWidget : public QWidget { void applyIconTheme(int index); void applyWidgetTheme(int index); void applyCatTheme(int index); + void refresh(); signals: int currentIconThemeChanged(int index); diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui index 4503181c2..322f9d6a7 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -167,6 +167,13 @@ + + + + Refresh + + + From 50a4d61ded300c47c727f1104d17b2960ff5d0c6 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 Nov 2023 23:26:41 +0200 Subject: [PATCH 055/139] Fixed demo mode Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 87 +++++++++++++------- launcher/LaunchController.h | 2 + launcher/minecraft/auth/AuthSession.cpp | 11 ++- launcher/minecraft/auth/AuthSession.h | 2 +- launcher/minecraft/auth/MinecraftAccount.cpp | 2 + 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index f59cdeda8..6d5a9e93e 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -129,12 +129,63 @@ void LaunchController::decideAccount() } } +bool LaunchController::askPlayDemo() +{ + QMessageBox box(m_parentWidget); + box.setWindowTitle(tr("Play demo?")); + box.setText( + tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play " + "the demo?")); + box.setIcon(QMessageBox::Warning); + auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); + auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); + box.setDefaultButton(cancelButton); + + box.exec(); + return box.clickedButton() == demoButton; +} + +QString LaunchController::askOfflineName(QString playerName, bool demo, bool& ok) +{ + // we ask the user for a player name + QString message = tr("Choose your offline mode player name."); + if (demo) { + message = tr("Choose your demo mode player name."); + } + + QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); + QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName; + QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); + if (!ok) + return {}; + if (name.length()) { + usedname = name; + APPLICATION->settings()->set("LastOfflinePlayerName", usedname); + } + return usedname; +} + void LaunchController::login() { decideAccount(); - // if no account is selected, we bail if (!m_accountToUse) { + // if no account is selected, ask about demo + if (!m_demo) { + m_demo = askPlayDemo(); + } + if (m_demo) { + // we ask the user for a player name + bool ok = false; + auto name = askOfflineName("Player", m_demo, ok); + if (ok) { + m_session = std::make_shared(); + m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString().remove(QRegularExpression("[{}-]"))); + launchInstance(); + return; + } + } + // if no account is selected, we bail emitFailed(tr("No account selected for launch.")); return; } @@ -175,24 +226,12 @@ void LaunchController::login() if (!m_session->wants_online) { // we ask the user for a player name bool ok = false; - - QString message = tr("Choose your offline mode player name."); - if (m_session->demo) { - message = tr("Choose your demo mode player name."); - } - - QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); - QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; - QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); + auto name = askOfflineName(m_session->player_name, m_session->demo, ok); if (!ok) { tryagain = false; break; } - if (name.length()) { - usedname = name; - APPLICATION->settings()->set("LastOfflinePlayerName", usedname); - } - m_session->MakeOffline(usedname); + m_session->MakeOffline(name); // offline flavored game from here :3 } if (m_accountToUse->ownsMinecraft()) { @@ -212,20 +251,10 @@ void LaunchController::login() return; } else { // play demo ? - QMessageBox box(m_parentWidget); - box.setWindowTitle(tr("Play demo?")); - box.setText( - tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play " - "the demo?")); - box.setIcon(QMessageBox::Warning); - auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); - auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); - box.setDefaultButton(cancelButton); - - box.exec(); - if (box.clickedButton() == demoButton) { - // play demo here - m_session->MakeDemo(); + if (!m_session->demo) { + m_session->demo = askPlayDemo(); + } + if (m_session->demo) { // play demo here launchInstance(); } else { emitFailed(tr("Launch cancelled - account does not own Minecraft.")); diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index f1c88afb7..02b1e0d33 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -74,6 +74,8 @@ class LaunchController : public Task { void login(); void launchInstance(); void decideAccount(); + bool askPlayDemo(); + QString askOfflineName(QString playerName, bool demo, bool& ok); private slots: void readyForLaunch(); diff --git a/launcher/minecraft/auth/AuthSession.cpp b/launcher/minecraft/auth/AuthSession.cpp index 37534f983..3657befec 100644 --- a/launcher/minecraft/auth/AuthSession.cpp +++ b/launcher/minecraft/auth/AuthSession.cpp @@ -30,8 +30,13 @@ bool AuthSession::MakeOffline(QString offline_playername) return true; } -void AuthSession::MakeDemo() +void AuthSession::MakeDemo(QString name, QString u) { - player_name = "Player"; + wants_online = false; demo = true; -} + uuid = u; + session = "-"; + access_token = "0"; + player_name = name; + status = PlayableOnline; // needs online to download the assets +}; \ No newline at end of file diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h index cec238033..54e7d69e0 100644 --- a/launcher/minecraft/auth/AuthSession.h +++ b/launcher/minecraft/auth/AuthSession.h @@ -10,7 +10,7 @@ class QNetworkAccessManager; struct AuthSession { bool MakeOffline(QString offline_playername); - void MakeDemo(); + void MakeDemo(QString name, QString uuid); QString serializeUserProperties(); diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index 545d06aed..c3c29f598 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -269,6 +269,8 @@ void MinecraftAccount::fillSession(AuthSessionPtr session) session->player_name = data.profileName(); // profile ID session->uuid = data.profileId(); + if (session->uuid.isEmpty()) + session->uuid = uuidFromUsername(session->player_name).toString().remove(QRegularExpression("[{}-]")); // 'legacy' or 'mojang', depending on account type session->user_type = typeString(); if (!session->access_token.isEmpty()) { From 4d93f4adb144079449c05edf939226a6bb215921 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 Nov 2023 12:10:47 +0200 Subject: [PATCH 056/139] Fixed folder size Signed-off-by: Trial97 --- launcher/minecraft/World.cpp | 2 +- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/Resource.cpp | 20 +++++++++++++++++-- launcher/minecraft/mod/Resource.h | 2 ++ .../minecraft/mod/ResourceFolderModel.cpp | 2 +- .../minecraft/mod/ResourcePackFolderModel.cpp | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 2 +- 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index 1a680ac56..1eba148a5 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -206,8 +206,8 @@ int64_t calculateWorldSize(const QFileInfo& file) QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); int64_t total = 0; while (it.hasNext()) { - total += it.fileInfo().size(); it.next(); + total += it.fileInfo().size(); } return total; } diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 5ba1795fb..8f79a4e00 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -111,7 +111,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return QVariant(); } diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 79e52a881..0115cf7ca 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -1,5 +1,6 @@ #include "Resource.h" +#include #include #include @@ -18,6 +19,20 @@ void Resource::setFile(QFileInfo file_info) parseFile(); } +qint64 calculateFileSize(const QFileInfo& file) +{ + if (file.isDir()) { + QDirIterator it(file.absoluteFilePath(), QDir::Files); + qint64 total = 0; + while (it.hasNext()) { + it.next(); + total += it.fileInfo().size(); + } + return total; + } + return file.size(); +} + void Resource::parseFile() { QString file_name{ m_file_info.fileName() }; @@ -26,6 +41,7 @@ void Resource::parseFile() m_internal_id = file_name; + m_size = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -90,9 +106,9 @@ std::pair Resource::compare(const Resource& other, SortType type) con return { -1, type == SortType::DATE }; break; case SortType::SIZE: { - if (fileinfo().size() > other.fileinfo().size()) + if (size() > other.size()) return { 1, type == SortType::SIZE }; - if (fileinfo().size() < other.fileinfo().size()) + if (size() < other.size()) return { -1, type == SortType::SIZE }; break; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index d94e4b368..61949f917 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -45,6 +45,7 @@ class Resource : public QObject { [[nodiscard]] auto internal_id() const -> QString { return m_internal_id; } [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } + [[nodiscard]] qint64 size() const { return m_size; } [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } @@ -117,4 +118,5 @@ class Resource : public QObject { bool m_is_resolving = false; bool m_is_resolved = false; int m_resolution_ticket = 0; + qint64 m_size = 0; }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 16ff01227..648bf84db 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -416,7 +416,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index e9c3d3043..cf2eb50df 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -88,7 +88,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return {}; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index e3f369bb8..63fb1f8b3 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -79,7 +79,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->fileinfo().size(), true); + return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); default: return {}; } From 215465e833e6faa17053bbaaf24b20ed99655d01 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 15 Nov 2023 14:11:34 +0200 Subject: [PATCH 057/139] added subdirectories to iteration Signed-off-by: Trial97 --- launcher/minecraft/mod/Resource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 0115cf7ca..72652cefb 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -22,7 +22,7 @@ void Resource::setFile(QFileInfo file_info) qint64 calculateFileSize(const QFileInfo& file) { if (file.isDir()) { - QDirIterator it(file.absoluteFilePath(), QDir::Files); + QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); qint64 total = 0; while (it.hasNext()) { it.next(); From 26931475aeb0ed4f0e6e3d935095d9b1fa69b651 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 19 Nov 2023 15:58:30 +0200 Subject: [PATCH 058/139] made folder size reflect the number of elements Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- launcher/minecraft/mod/Resource.cpp | 25 ++++++++++--------- launcher/minecraft/mod/Resource.h | 4 +-- .../minecraft/mod/ResourceFolderModel.cpp | 2 +- .../minecraft/mod/ResourcePackFolderModel.cpp | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 369ad3936..4e98d1520 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -110,7 +110,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const return provider.value(); } case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return QVariant(); } diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 72652cefb..d8727db0a 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -5,6 +5,7 @@ #include #include "FileSystem.h" +#include "StringUtils.h" Resource::Resource(QObject* parent) : QObject(parent) {} @@ -19,18 +20,18 @@ void Resource::setFile(QFileInfo file_info) parseFile(); } -qint64 calculateFileSize(const QFileInfo& file) +QString calculateFileSize(const QFileInfo& file) { if (file.isDir()) { - QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); - qint64 total = 0; - while (it.hasNext()) { - it.next(); - total += it.fileInfo().size(); - } - return total; + auto dir = QDir(file.absoluteFilePath()); + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + auto count = dir.count(); + auto str = QObject::tr("item"); + if (count != 1) + str = QObject::tr("items"); + return QString("%1 %2").arg(QString::number(count), str); } - return file.size(); + return StringUtils::humanReadableFileSize(file.size(), true); } void Resource::parseFile() @@ -41,7 +42,7 @@ void Resource::parseFile() m_internal_id = file_name; - m_size = calculateFileSize(m_file_info); + m_size_str = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -106,9 +107,9 @@ std::pair Resource::compare(const Resource& other, SortType type) con return { -1, type == SortType::DATE }; break; case SortType::SIZE: { - if (size() > other.size()) + if (fileinfo().size() > other.fileinfo().size()) return { 1, type == SortType::SIZE }; - if (size() < other.size()) + if (fileinfo().size() < other.fileinfo().size()) return { -1, type == SortType::SIZE }; break; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 61949f917..029afbd5b 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -45,7 +45,7 @@ class Resource : public QObject { [[nodiscard]] auto internal_id() const -> QString { return m_internal_id; } [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } - [[nodiscard]] qint64 size() const { return m_size; } + [[nodiscard]] QString sizeStr() const { return m_size_str; } [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } @@ -118,5 +118,5 @@ class Resource : public QObject { bool m_is_resolving = false; bool m_is_resolved = false; int m_resolution_ticket = 0; - qint64 m_size = 0; + QString m_size_str; }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 6a9c5b7dd..8e14f4e80 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -417,7 +417,7 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 7a6138833..7ad7b3038 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -88,7 +88,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return {}; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 38db9d8ff..a042c9113 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -79,7 +79,7 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const case DateColumn: return m_resources[row]->dateTimeChanged(); case SizeColumn: - return StringUtils::humanReadableFileSize(m_resources[row]->size(), true); + return m_resources[row]->sizeStr(); default: return {}; } From 85b4e1f24f444627f48fe2e13b6cb34c45aa7361 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 22 Nov 2023 19:23:24 +0200 Subject: [PATCH 059/139] Made column hidden by default Signed-off-by: Trial97 --- launcher/minecraft/mod/ModFolderModel.cpp | 1 + launcher/minecraft/mod/ResourceFolderModel.cpp | 4 ++++ launcher/minecraft/mod/ResourceFolderModel.h | 1 + launcher/minecraft/mod/ResourcePackFolderModel.cpp | 1 + launcher/minecraft/mod/TexturePackFolderModel.cpp | 1 + 5 files changed, 8 insertions(+) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 98e21b9f0..6a948e2ec 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -75,6 +75,7 @@ ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true, true, true, true, true }; + m_columnsHiddenByDefault = { false, false, false, false, false, false, true, true, true, true }; } QVariant ModFolderModel::data(const QModelIndex& index, int role) const diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 4a3ee9922..8a0797c58 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -528,6 +528,10 @@ void ResourceFolderModel::saveColumns(QTreeView* tree) void ResourceFolderModel::loadColumns(QTreeView* tree) { + for (auto i = 0; i < m_columnsHiddenByDefault.size(); ++i) { + tree->setColumnHidden(i, m_columnsHiddenByDefault[i]); + } + auto const setting_name = QString("UI/%1_Page/Columns").arg(id()); auto setting = (m_instance->settings()->contains(setting_name)) ? m_instance->settings()->getSetting(setting_name) : m_instance->settings()->registerSetting(setting_name); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index b3f6d9a58..77bece636 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -203,6 +203,7 @@ class ResourceFolderModel : public QAbstractListModel { QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; QList m_columnsHideable = { false, false, true }; + QList m_columnsHiddenByDefault = { false, false, false }; QDir m_dir; BaseInstance* m_instance; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 693b8af05..3fcbca4ce 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -55,6 +55,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstanc m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true }; + m_columnsHiddenByDefault = { false, false, false, false, false }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index f210501c7..1a35a3795 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -49,6 +49,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true }; + m_columnsHiddenByDefault = { false, false, false, false }; } Task* TexturePackFolderModel::createUpdateTask() From ac223a29ef61643a2313914d180801ce6dcae139 Mon Sep 17 00:00:00 2001 From: deadmeu Date: Sun, 23 Apr 2023 00:29:49 +1000 Subject: [PATCH 060/139] refactor: shorten desktop entry comment field Signed-off-by: deadmeu --- program_info/org.prismlauncher.PrismLauncher.desktop.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/org.prismlauncher.PrismLauncher.desktop.in b/program_info/org.prismlauncher.PrismLauncher.desktop.in index f08f2ba43..0995c7c2a 100644 --- a/program_info/org.prismlauncher.PrismLauncher.desktop.in +++ b/program_info/org.prismlauncher.PrismLauncher.desktop.in @@ -1,7 +1,7 @@ [Desktop Entry] Version=1.0 Name=Prism Launcher -Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. +Comment=Discover, manage, and play Minecraft instances Type=Application Terminal=false Exec=@Launcher_APP_BINARY_NAME@ From 648aa18e6f84be7fbabc330de09beff9f170afa3 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Tue, 12 Dec 2023 14:22:23 -0800 Subject: [PATCH 061/139] add: refresh on load Signed-off-by: theMackabu --- launcher/ui/widgets/ThemeCustomizationWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.cpp b/launcher/ui/widgets/ThemeCustomizationWidget.cpp index a0e682bb9..79932e20b 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.cpp +++ b/launcher/ui/widgets/ThemeCustomizationWidget.cpp @@ -27,6 +27,7 @@ ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget* parent) : QWidget(pa { ui->setupUi(this); loadSettings(); + ThemeCustomizationWidget::refresh(); connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme); connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, From 6a619003288a06c0f664c5454dc4b69a65b4235b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 13 Dec 2023 17:48:11 +0200 Subject: [PATCH 062/139] Fixed curseforge version filter Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 341823244..f419537c7 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -177,6 +177,7 @@ void ModFilterWidget::prepareBasicFilter() auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); m_filter->versions.push_front(Version{ def }); ui->versionsCb->setCheckedItems({ def }); + ui->versionsSimpleCb->setCurrentIndex(ui->versionsSimpleCb->findText(def)); } void ModFilterWidget::onIncludeSnapshotsChanged() From 13d29ac6f466776f13b01f90ba805af0490a41ca Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 22 Dec 2023 23:35:48 +0200 Subject: [PATCH 063/139] Updated the size sort code Signed-off-by: Trial97 --- launcher/minecraft/mod/Resource.cpp | 13 +++++++------ launcher/minecraft/mod/Resource.h | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index d8727db0a..2fe7e87d4 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "FileSystem.h" #include "StringUtils.h" @@ -20,7 +21,7 @@ void Resource::setFile(QFileInfo file_info) parseFile(); } -QString calculateFileSize(const QFileInfo& file) +std::tuple calculateFileSize(const QFileInfo& file) { if (file.isDir()) { auto dir = QDir(file.absoluteFilePath()); @@ -29,9 +30,9 @@ QString calculateFileSize(const QFileInfo& file) auto str = QObject::tr("item"); if (count != 1) str = QObject::tr("items"); - return QString("%1 %2").arg(QString::number(count), str); + return { QString("%1 %2").arg(QString::number(count), str), -count }; } - return StringUtils::humanReadableFileSize(file.size(), true); + return { StringUtils::humanReadableFileSize(file.size(), true), file.size() }; } void Resource::parseFile() @@ -42,7 +43,7 @@ void Resource::parseFile() m_internal_id = file_name; - m_size_str = calculateFileSize(m_file_info); + std::tie(m_size_str, m_size_info) = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -107,9 +108,9 @@ std::pair Resource::compare(const Resource& other, SortType type) con return { -1, type == SortType::DATE }; break; case SortType::SIZE: { - if (fileinfo().size() > other.fileinfo().size()) + if (sizeInfo() > other.sizeInfo()) return { 1, type == SortType::SIZE }; - if (fileinfo().size() < other.fileinfo().size()) + if (sizeInfo() < other.sizeInfo()) return { -1, type == SortType::SIZE }; break; } diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 029afbd5b..74f4d006e 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -46,6 +46,7 @@ class Resource : public QObject { [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } [[nodiscard]] QString sizeStr() const { return m_size_str; } + [[nodiscard]] qint64 sizeInfo() const { return m_size_info; } [[nodiscard]] virtual auto name() const -> QString { return m_name; } [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } @@ -119,4 +120,5 @@ class Resource : public QObject { bool m_is_resolved = false; int m_resolution_ticket = 0; QString m_size_str; + qint64 m_size_info; }; From f77749e4486f3a19a75b74f933e6d9de9c0c0a44 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 21 Jan 2024 21:24:47 +0200 Subject: [PATCH 064/139] Resized Refresh theme button Signed-off-by: Trial97 --- .../ui/widgets/ThemeCustomizationWidget.ui | 283 ++++++++++-------- 1 file changed, 157 insertions(+), 126 deletions(-) diff --git a/launcher/ui/widgets/ThemeCustomizationWidget.ui b/launcher/ui/widgets/ThemeCustomizationWidget.ui index 322f9d6a7..1faa45c4f 100644 --- a/launcher/ui/widgets/ThemeCustomizationWidget.ui +++ b/launcher/ui/widgets/ThemeCustomizationWidget.ui @@ -13,7 +13,7 @@ Form - + QLayout::SetMinimumSize @@ -29,150 +29,181 @@ 0 - - - - &Icons - - - iconsComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - - - - - View icon themes folder. - + + + + - + &Icons - - - .. - - - true + + iconsComboBox - - - - - - &Widgets - - - widgetStyleComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View icon themes folder. + + + + + + + + + true + + + + - - - - View widget themes folder. - + + - + &Widgets - - - .. - - - true + + widgetStyleComboBox - - - - - - The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. - - - C&at - - - backgroundCatComboBox - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + View widget themes folder. + + + + + + + + + true + + + + + + + The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. + + C&at + + + backgroundCatComboBox + - - - - View cat packs folder. - - - - - - - .. - - - true - - + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar. + + + + + + + View cat packs folder. + + + + + + + + + true + + + + - - - - Refresh - - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refresh all + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + From 9bbeb5ef3ae389113bc7219b61600ebadcb4ce7b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 22 Jan 2024 21:26:46 +0200 Subject: [PATCH 065/139] Made updater interval configurable Signed-off-by: Trial97 --- launcher/ui/pages/global/LauncherPage.cpp | 2 ++ launcher/ui/pages/global/LauncherPage.ui | 27 +++++++++++++++++++++++ launcher/updater/PrismExternalUpdater.cpp | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 78c44380a..fc062023c 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -185,6 +185,7 @@ void LauncherPage::applySettings() // Updates if (APPLICATION->updater()) { APPLICATION->updater()->setAutomaticallyChecksForUpdates(ui->autoUpdateCheckBox->isChecked()); + APPLICATION->updater()->setUpdateCheckInterval(ui->updateIntervalSpinBox->value() * 3600); } s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked()); @@ -234,6 +235,7 @@ void LauncherPage::loadSettings() // Updates if (APPLICATION->updater()) { ui->autoUpdateCheckBox->setChecked(APPLICATION->updater()->getAutomaticallyChecksForUpdates()); + ui->updateIntervalSpinBox->setValue(APPLICATION->updater()->getUpdateCheckInterval() / 3600); } // Toolbar/menu bar settings (not applicable if native menu bar is present) diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 18b52e1b8..68f4e7436 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -58,6 +58,33 @@ + + + + + + Update interval + + + + + + + Set it to 0 to only check on launch + + + h + + + 0 + + + 99999999 + + + + + diff --git a/launcher/updater/PrismExternalUpdater.cpp b/launcher/updater/PrismExternalUpdater.cpp index bee72e3a0..dc1aae872 100644 --- a/launcher/updater/PrismExternalUpdater.cpp +++ b/launcher/updater/PrismExternalUpdater.cpp @@ -257,7 +257,7 @@ void PrismExternalUpdater::setBetaAllowed(bool allowed) void PrismExternalUpdater::resetAutoCheckTimer() { - if (priv->autoCheck) { + if (priv->autoCheck && priv->updateInterval > 0) { int timeoutDuration = 0; auto now = QDateTime::currentDateTime(); if (priv->lastCheck.isValid()) { From 6b637a8797dad184d5ed73a93f00e9f86da22ee0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 22 Jan 2024 22:32:42 +0200 Subject: [PATCH 066/139] Display minecraft version if not mentioned for modrinth packs Signed-off-by: Trial97 --- .../modplatform/modrinth/ModrinthPackManifest.cpp | 4 ++++ launcher/modplatform/modrinth/ModrinthPackManifest.h | 1 + .../ui/pages/modplatform/modrinth/ModrinthPage.cpp | 11 ++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index 7846e966d..f360df43a 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -131,6 +131,10 @@ auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion file.name = Json::requireString(obj, "name"); file.version = Json::requireString(obj, "version_number"); + auto gameVersions = Json::ensureArray(obj, "game_versions"); + if (!gameVersions.isEmpty()) { + file.gameVersion = Json::ensureString(gameVersions[0]); + } file.version_type = ModPlatform::IndexedVersionType(Json::requireString(obj, "version_type")); file.changelog = Json::ensureString(obj, "changelog"); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 1ffd31d83..2bd61c5d9 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -84,6 +84,7 @@ struct ModpackExtra { struct ModpackVersion { QString name; QString version; + QString gameVersion; ModPlatform::IndexedVersionType version_type; QString changelog; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index fffa21940..452416edd 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -222,11 +222,12 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI } for (auto version : current.versions) { auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; - if (!version.name.contains(version.version)) - ui->versionSelectionBox->addItem(QString("%1 — %2%3").arg(version.name, version.version, release_type), - QVariant(version.id)); - else - ui->versionSelectionBox->addItem(QString("%1%2").arg(version.name, release_type), QVariant(version.id)); + auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion) + ? QString(" for %1").arg(version.gameVersion) + : ""; + auto versionStr = !version.name.contains(version.version) ? version.version : ""; + ui->versionSelectionBox->addItem(QString("%1%2 — %3%4").arg(version.name, mcVersion, versionStr, release_type), + QVariant(version.id)); } QVariant current_updated; From b54410b48cf10821fc3229d5d6989645e95c66f0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Jan 2024 19:10:39 +0200 Subject: [PATCH 067/139] Added gameVersion for curseforge mod packs Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f1fd9b5d8..0074a9225 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -178,7 +178,11 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde for (auto version : current.versions) { auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; - ui->versionSelectionBox->addItem(QString("%1%2").arg(version.version, release_type), QVariant(version.downloadUrl)); + auto mcVersion = !version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion) + ? QString(" for %1").arg(version.mcVersion) + : ""; + ui->versionSelectionBox->addItem(QString("%1%2%3").arg(version.version, mcVersion, release_type), + QVariant(version.downloadUrl)); } QVariant current_updated; From 031a9f4738df8977d8273968c2b3def6cc425fa2 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 10 Feb 2024 11:03:51 +0200 Subject: [PATCH 068/139] Replaced QFile::remove with FS::deletePath Signed-off-by: Trial97 --- launcher/Application.cpp | 9 ++------ launcher/BaseInstaller.cpp | 3 ++- launcher/InstanceCreationTask.cpp | 3 ++- launcher/MMCZip.cpp | 22 +++++++++---------- launcher/icons/IconList.cpp | 2 +- launcher/meta/BaseEntity.cpp | 4 ++-- launcher/minecraft/Component.cpp | 2 +- launcher/minecraft/PackProfile.cpp | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 2 +- .../atlauncher/ATLPackInstallTask.cpp | 4 ++-- .../modplatform/helpers/OverrideUtils.cpp | 2 +- .../updater/prismupdater/PrismUpdater.cpp | 11 +++------- 12 files changed, 29 insertions(+), 37 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 42343ff8f..25ee8fa69 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -389,20 +389,15 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log"; static const QString logBase = FS::PathCombine("logs", baseLogFile); - auto moveFile = [](const QString& oldName, const QString& newName) { - QFile::remove(newName); - QFile::copy(oldName, newName); - QFile::remove(oldName); - }; if (FS::ensureFolderPathExists("logs")) { // if this did not fail for (auto i = 0; i <= 4; i++) if (auto oldName = baseLogFile.arg(i); QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there - moveFile(oldName, logBase.arg(i)); + FS::move(oldName, logBase.arg(i)); } for (auto i = 4; i > 0; i--) - moveFile(logBase.arg(i - 1), logBase.arg(i)); + FS::move(logBase.arg(i - 1), logBase.arg(i)); logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { diff --git a/launcher/BaseInstaller.cpp b/launcher/BaseInstaller.cpp index 1ff86ed40..96a3b5ebe 100644 --- a/launcher/BaseInstaller.cpp +++ b/launcher/BaseInstaller.cpp @@ -16,6 +16,7 @@ #include #include "BaseInstaller.h" +#include "FileSystem.h" #include "minecraft/MinecraftInstance.h" BaseInstaller::BaseInstaller() {} @@ -42,7 +43,7 @@ bool BaseInstaller::add(MinecraftInstance* to) bool BaseInstaller::remove(MinecraftInstance* from) { - return QFile::remove(filename(from->instanceRoot())); + return FS::deletePath(filename(from->instanceRoot())); } QString BaseInstaller::filename(const QString& root) const diff --git a/launcher/InstanceCreationTask.cpp b/launcher/InstanceCreationTask.cpp index 73dc17891..9688fa112 100644 --- a/launcher/InstanceCreationTask.cpp +++ b/launcher/InstanceCreationTask.cpp @@ -2,6 +2,7 @@ #include #include +#include "FileSystem.h" InstanceCreationTask::InstanceCreationTask() = default; @@ -47,7 +48,7 @@ void InstanceCreationTask::executeTask() if (!QFile::exists(path)) continue; qDebug() << "Removing" << path; - if (!QFile::remove(path)) { + if (!FS::deletePath(path)) { qCritical() << "Couldn't remove the old conflicting files."; emitFailed(tr("Failed to remove old conflicting files.")); return; diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index b81106a59..b6bfac25d 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -122,7 +122,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, QuaZip zip(fileCompressed); QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); if (!zip.open(QuaZip::mdCreate)) { - QFile::remove(fileCompressed); + FS::deletePath(fileCompressed); return false; } @@ -130,7 +130,7 @@ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, zip.close(); if (zip.getZipError() != 0) { - QFile::remove(fileCompressed); + FS::deletePath(fileCompressed); return false; } @@ -143,7 +143,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListtype() == ResourceType::ZIPFILE) { if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { zipOut.close(); - QFile::remove(targetJarPath); + FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } @@ -170,7 +170,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo(); if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); - QFile::remove(targetJarPath); + FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } @@ -193,7 +193,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo().fileName() << "to the jar."; return false; } @@ -201,7 +201,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo().fileName() << "to the jar."; return false; } @@ -209,7 +209,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList ZipResult void ExportToZipTask::finish() { if (m_build_zip_future.isCanceled()) { - QFile::remove(m_output_path); + FS::deletePath(m_output_path); emitAborted(); } else if (auto result = m_build_zip_future.result(); result.has_value()) { - QFile::remove(m_output_path); + FS::deletePath(m_output_path); emitFailed(result.value()); } else { emitSucceeded(); diff --git a/launcher/icons/IconList.cpp b/launcher/icons/IconList.cpp index 5576b9745..e4157ea2d 100644 --- a/launcher/icons/IconList.cpp +++ b/launcher/icons/IconList.cpp @@ -322,7 +322,7 @@ const MMCIcon* IconList::icon(const QString& key) const bool IconList::deleteIcon(const QString& key) { - return iconFileExists(key) && QFile::remove(icon(key)->getFilePath()); + return iconFileExists(key) && FS::deletePath(icon(key)->getFilePath()); } bool IconList::trashIcon(const QString& key) diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 5f9804e48..8a99e3303 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -15,6 +15,7 @@ #include "BaseEntity.h" +#include "FileSystem.h" #include "Json.h" #include "net/ApiDownload.h" #include "net/HttpMetaCache.h" @@ -83,8 +84,7 @@ bool Meta::BaseEntity::loadLocalFile() } catch (const Exception& e) { qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); // just make sure it's gone and we never consider it again. - QFile::remove(fname); - return false; + return !FS::deletePath(fname); } } diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index 79ea7a06d..ad2e4023c 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -336,7 +336,7 @@ bool Component::revert() bool result = true; // just kill the file and reload if (QFile::exists(filename)) { - result = QFile::remove(filename); + result = FS::deletePath(filename); } if (result) { // file gone... diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp index 180f8aa30..4b17cdf07 100644 --- a/launcher/minecraft/PackProfile.cpp +++ b/launcher/minecraft/PackProfile.cpp @@ -839,7 +839,7 @@ bool PackProfile::installCustomJar_internal(QString filepath) QFileInfo jarInfo(finalPath); if (jarInfo.exists()) { - if (!QFile::remove(finalPath)) { + if (!FS::deletePath(finalPath)) { return false; } } diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 9157f35f0..5bea720d0 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -111,7 +111,7 @@ bool ResourceFolderModel::installResource(QString original_path) case ResourceType::ZIPFILE: case ResourceType::LITEMOD: { if (QFile::exists(new_path) || QFile::exists(new_path + QString(".disabled"))) { - if (!QFile::remove(new_path)) { + if (!FS::deletePath(new_path)) { qCritical() << "Cleaning up new location (" << new_path << ") was unsuccessful!"; return false; } diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 8ae8145de..57660aa6d 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -282,7 +282,7 @@ void PackInstallTask::deleteExistingFiles() // Delete the files for (const auto& item : filesToDelete) { - QFile::remove(item); + FS::deletePath(item); } } @@ -987,7 +987,7 @@ bool PackInstallTask::extractMods(const QMap& toExtract, // the copy from the Configs.zip QFileInfo fileInfo(to); if (fileInfo.exists()) { - if (!QFile::remove(to)) { + if (!FS::deletePath(to)) { qWarning() << "Failed to delete" << to; return false; } diff --git a/launcher/modplatform/helpers/OverrideUtils.cpp b/launcher/modplatform/helpers/OverrideUtils.cpp index 65b5f7603..60983a5cf 100644 --- a/launcher/modplatform/helpers/OverrideUtils.cpp +++ b/launcher/modplatform/helpers/OverrideUtils.cpp @@ -10,7 +10,7 @@ void createOverrides(const QString& name, const QString& parent_folder, const QS { QString file_path(FS::PathCombine(parent_folder, name + ".txt")); if (QFile::exists(file_path)) - QFile::remove(file_path); + FS::deletePath(file_path); FS::ensureFilePathExists(file_path); diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 5fe22bdd0..8948b3e82 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -352,15 +352,10 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar FS::ensureFolderPathExists(FS::PathCombine(m_dataPath, "logs")); static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); - auto moveFile = [](const QString& oldName, const QString& newName) { - QFile::remove(newName); - QFile::copy(oldName, newName); - QFile::remove(oldName); - }; if (FS::ensureFolderPathExists("logs")) { // enough history to track both launches of the updater during a portable install - moveFile(logBase.arg(1), logBase.arg(2)); - moveFile(logBase.arg(0), logBase.arg(1)); + FS::move(logBase.arg(1), logBase.arg(2)); + FS::move(logBase.arg(0), logBase.arg(1)); } logFile = std::unique_ptr(new QFile(logBase.arg(0))); @@ -924,7 +919,7 @@ bool PrismUpdaterApp::callAppImageUpdate() void PrismUpdaterApp::clearUpdateLog() { - QFile::remove(m_updateLogPath); + FS::deletePath(m_updateLogPath); } void PrismUpdaterApp::logUpdate(const QString& msg) From 344398f2382b879689aef7d4d02785faf0d9bff8 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 26 Feb 2024 21:20:14 +0200 Subject: [PATCH 069/139] Updated ftb app import instance detection Signed-off-by: Trial97 --- .../modplatform/import_ftb/ImportFTBPage.cpp | 10 ++- .../modplatform/import_ftb/ListModel.cpp | 90 ++++++++++++------- .../pages/modplatform/import_ftb/ListModel.h | 16 ++-- 3 files changed, 76 insertions(+), 40 deletions(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index ac06f4cdd..c72a5a9da 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -21,6 +21,7 @@ #include "ui_ImportFTBPage.h" #include +#include #include #include "FileSystem.h" #include "ListModel.h" @@ -58,7 +59,14 @@ ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidg connect(ui->searchEdit, &QLineEdit::textChanged, this, &ImportFTBPage::triggerSearch); connect(ui->browseButton, &QPushButton::clicked, this, [this] { - auto path = listModel->getPath(); + auto paths = listModel->getPosiblePaths(); + QString path; + for (auto p : paths) { + if (p != "" && QFileInfo::exists(p)) { + path = p; + break; + } + } QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), path, QFileDialog::ShowDirsOnly); if (!dir.isEmpty()) listModel->setPath(dir); diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index e058937a6..ad03d571b 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -24,7 +24,9 @@ #include #include #include "Application.h" +#include "Exception.h" #include "FileSystem.h" +#include "Json.h" #include "StringUtils.h" #include "modplatform/import_ftb/PackHelpers.h" #include "ui/widgets/ProjectItem.h" @@ -41,27 +43,54 @@ QString getStaticPath() #else partialPath = QDir::homePath(); #endif - return FS::PathCombine(partialPath, ".ftba"); + return FS::PathCombine(partialPath, ".ftba", "instances"); } -static const QString FTB_APP_PATH = FS::PathCombine(getStaticPath(), "instances"); +QString getDynamicPath() +{ + auto settingsPath = FS::PathCombine(QDir::homePath(), ".ftba", "bin", "settings.json"); + if (!QFileInfo::exists(settingsPath)) { + qWarning() << "The ftb app setings doesn't exist."; + return {}; + } + try { + auto doc = Json::requireDocument(FS::read(settingsPath)); + return Json::requireString(Json::requireObject(doc), "instanceLocation"); + } catch (const Exception& e) { + qCritical() << "Could not read ftb settings file: " << e.cause(); + } + return {}; +} + +ListModel::ListModel(QObject* parent) : QAbstractListModel(parent), m_static_path(getStaticPath()), m_dynamic_path(getDynamicPath()) {} void ListModel::update() { beginResetModel(); - modpacks.clear(); + m_modpacks.clear(); - QString instancesPath = getPath(); - if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { - QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, - QDirIterator::FollowSymlinks); - while (directoryIterator.hasNext()) { - auto modpack = parseDirectory(directoryIterator.next()); - if (!modpack.path.isEmpty()) - modpacks.append(modpack); + auto paths = getPosiblePaths(); + paths.removeDuplicates(); + for (auto instancesPath : paths) { + if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { + QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, + QDirIterator::FollowSymlinks); + while (directoryIterator.hasNext()) { + auto currentPath = directoryIterator.next(); + bool wasAdded = false; + for (auto pack : m_modpacks) { + if (pack.path == currentPath) { + wasAdded = true; + break; + } + } + if (!wasAdded) { + auto modpack = parseDirectory(currentPath); + if (!modpack.path.isEmpty()) + m_modpacks.append(modpack); + } + } } - } else { - qDebug() << "Couldn't find ftb instances folder: " << instancesPath; } endResetModel(); @@ -70,11 +99,11 @@ void ListModel::update() QVariant ListModel::data(const QModelIndex& index, int role) const { int pos = index.row(); - if (pos >= modpacks.size() || pos < 0 || !index.isValid()) { + if (pos >= m_modpacks.size() || pos < 0 || !index.isValid()) { return QVariant(); } - auto pack = modpacks.at(pos); + auto pack = m_modpacks.at(pos); if (role == Qt::ToolTipRole) { } @@ -110,9 +139,9 @@ QVariant ListModel::data(const QModelIndex& index, int role) const FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent) { - currentSorting = Sorting::ByGameVersion; - sortings.insert(tr("Sort by Name"), Sorting::ByName); - sortings.insert(tr("Sort by Game Version"), Sorting::ByGameVersion); + m_currentSorting = Sorting::ByGameVersion; + m_sortings.insert(tr("Sort by Name"), Sorting::ByName); + m_sortings.insert(tr("Sort by Game Version"), Sorting::ByGameVersion); } bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const @@ -120,12 +149,12 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value(); Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value(); - if (currentSorting == Sorting::ByGameVersion) { + if (m_currentSorting == Sorting::ByGameVersion) { Version lv(leftPack.mcVersion); Version rv(rightPack.mcVersion); return lv < rv; - } else if (currentSorting == Sorting::ByName) { + } else if (m_currentSorting == Sorting::ByName) { return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; } @@ -136,39 +165,39 @@ bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) co bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const { - if (searchTerm.isEmpty()) { + if (m_searchTerm.isEmpty()) { return true; } QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); Modpack pack = sourceModel()->data(index, Qt::UserRole).value(); - return pack.name.contains(searchTerm, Qt::CaseInsensitive); + return pack.name.contains(m_searchTerm, Qt::CaseInsensitive); } void FilterModel::setSearchTerm(const QString term) { - searchTerm = term.trimmed(); + m_searchTerm = term.trimmed(); invalidate(); } const QMap FilterModel::getAvailableSortings() { - return sortings; + return m_sortings; } QString FilterModel::translateCurrentSorting() { - return sortings.key(currentSorting); + return m_sortings.key(m_currentSorting); } void FilterModel::setSorting(Sorting s) { - currentSorting = s; + m_currentSorting = s; invalidate(); } FilterModel::Sorting FilterModel::getCurrentSorting() { - return currentSorting; + return m_currentSorting; } void ListModel::setPath(QString path) { @@ -176,11 +205,8 @@ void ListModel::setPath(QString path) update(); } -QString ListModel::getPath() +QStringList ListModel::getPosiblePaths() { - auto path = APPLICATION->settings()->get("FTBAppInstancesPath").toString(); - if (path.isEmpty() || !QFileInfo(path).exists()) - path = FTB_APP_PATH; - return path; + return { APPLICATION->settings()->get("FTBAppInstancesPath").toString(), m_dynamic_path, m_static_path }; } } // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h index ed33a88f3..393836b26 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -42,28 +42,30 @@ class FilterModel : public QSortFilterProxyModel { bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; private: - QMap sortings; - Sorting currentSorting; - QString searchTerm; + QMap m_sortings; + Sorting m_currentSorting; + QString m_searchTerm; }; class ListModel : public QAbstractListModel { Q_OBJECT public: - ListModel(QObject* parent) : QAbstractListModel(parent) {} + ListModel(QObject* parent); virtual ~ListModel() = default; - int rowCount(const QModelIndex& parent) const { return modpacks.size(); } + int rowCount(const QModelIndex& parent) const { return m_modpacks.size(); } int columnCount(const QModelIndex& parent) const { return 1; } QVariant data(const QModelIndex& index, int role) const; void update(); - QString getPath(); + QStringList getPosiblePaths(); void setPath(QString path); private: - ModpackList modpacks; + ModpackList m_modpacks; + const QString m_static_path; + const QString m_dynamic_path; }; } // namespace FTBImportAPP \ No newline at end of file From 27e76d0dcb69d0f34fa64fca89964b231c194387 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 27 Feb 2024 17:11:19 +0200 Subject: [PATCH 070/139] Removed static path Signed-off-by: Trial97 --- .../modplatform/import_ftb/ImportFTBPage.cpp | 11 +-- .../modplatform/import_ftb/ListModel.cpp | 69 ++++++++++--------- .../pages/modplatform/import_ftb/ListModel.h | 5 +- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index c72a5a9da..db59fe10a 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -59,15 +59,8 @@ ImportFTBPage::ImportFTBPage(NewInstanceDialog* dialog, QWidget* parent) : QWidg connect(ui->searchEdit, &QLineEdit::textChanged, this, &ImportFTBPage::triggerSearch); connect(ui->browseButton, &QPushButton::clicked, this, [this] { - auto paths = listModel->getPosiblePaths(); - QString path; - for (auto p : paths) { - if (p != "" && QFileInfo::exists(p)) { - path = p; - break; - } - } - QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), path, QFileDialog::ShowDirsOnly); + QString dir = QFileDialog::getExistingDirectory(this, tr("Select FTBApp instances directory"), listModel->getUserPath(), + QFileDialog::ShowDirsOnly); if (!dir.isEmpty()) listModel->setPath(dir); }); diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index ad03d571b..52671ea8e 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -33,22 +33,18 @@ namespace FTBImportAPP { -QString getStaticPath() +QString getFTBRoot() { - QString partialPath; + QString partialPath = QDir::homePath(); #if defined(Q_OS_OSX) - partialPath = FS::PathCombine(QDir::homePath(), "Library/Application Support"); -#elif defined(Q_OS_WIN32) - partialPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); -#else - partialPath = QDir::homePath(); + partialPath = FS::PathCombine(partialPath, "Library/Application Support"); #endif - return FS::PathCombine(partialPath, ".ftba", "instances"); + return FS::PathCombine(partialPath, ".ftba"); } QString getDynamicPath() { - auto settingsPath = FS::PathCombine(QDir::homePath(), ".ftba", "bin", "settings.json"); + auto settingsPath = FS::PathCombine(getFTBRoot(), "bin", "settings.json"); if (!QFileInfo::exists(settingsPath)) { qWarning() << "The ftb app setings doesn't exist."; return {}; @@ -62,36 +58,40 @@ QString getDynamicPath() return {}; } -ListModel::ListModel(QObject* parent) : QAbstractListModel(parent), m_static_path(getStaticPath()), m_dynamic_path(getDynamicPath()) {} +ListModel::ListModel(QObject* parent) : QAbstractListModel(parent), m_instances_path(getDynamicPath()) {} void ListModel::update() { beginResetModel(); m_modpacks.clear(); - auto paths = getPosiblePaths(); - paths.removeDuplicates(); - for (auto instancesPath : paths) { - if (auto instancesInfo = QFileInfo(instancesPath); instancesInfo.exists() && instancesInfo.isDir()) { - QDirIterator directoryIterator(instancesPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, - QDirIterator::FollowSymlinks); - while (directoryIterator.hasNext()) { - auto currentPath = directoryIterator.next(); - bool wasAdded = false; - for (auto pack : m_modpacks) { - if (pack.path == currentPath) { - wasAdded = true; - break; - } - } - if (!wasAdded) { - auto modpack = parseDirectory(currentPath); - if (!modpack.path.isEmpty()) - m_modpacks.append(modpack); - } + auto wasPathAdded = [this](QString path) { + for (auto pack : m_modpacks) { + if (pack.path == path) + return true; + } + return false; + }; + + auto scanPath = [this, wasPathAdded](QString path) { + if (path.isEmpty()) + return; + if (auto instancesInfo = QFileInfo(path); !instancesInfo.exists() || !instancesInfo.isDir()) + return; + QDirIterator directoryIterator(path, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Hidden, + QDirIterator::FollowSymlinks); + while (directoryIterator.hasNext()) { + auto currentPath = directoryIterator.next(); + if (!wasPathAdded(currentPath)) { + auto modpack = parseDirectory(currentPath); + if (!modpack.path.isEmpty()) + m_modpacks.append(modpack); } } - } + }; + + scanPath(APPLICATION->settings()->get("FTBAppInstancesPath").toString()); + scanPath(m_instances_path); endResetModel(); } @@ -205,8 +205,11 @@ void ListModel::setPath(QString path) update(); } -QStringList ListModel::getPosiblePaths() +QString ListModel::getUserPath() { - return { APPLICATION->settings()->get("FTBAppInstancesPath").toString(), m_dynamic_path, m_static_path }; + auto path = APPLICATION->settings()->get("FTBAppInstancesPath").toString(); + if (path.isEmpty()) + path = m_instances_path; + return path; } } // namespace FTBImportAPP \ No newline at end of file diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.h b/launcher/ui/pages/modplatform/import_ftb/ListModel.h index 393836b26..a842ac8ff 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.h +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.h @@ -60,12 +60,11 @@ class ListModel : public QAbstractListModel { void update(); - QStringList getPosiblePaths(); + QString getUserPath(); void setPath(QString path); private: ModpackList m_modpacks; - const QString m_static_path; - const QString m_dynamic_path; + const QString m_instances_path; }; } // namespace FTBImportAPP \ No newline at end of file From 71998cb7d99504d5a6dc16f0c2af606f10c33a94 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 1 Apr 2024 15:22:09 +0100 Subject: [PATCH 071/139] Rework filter widget Signed-off-by: TheKodeToad --- launcher/ui/widgets/ModFilterWidget.cpp | 198 ++++++------- launcher/ui/widgets/ModFilterWidget.h | 10 +- launcher/ui/widgets/ModFilterWidget.ui | 374 ++++++++++-------------- 3 files changed, 250 insertions(+), 332 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index f419537c7..b44a573d8 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -46,9 +46,9 @@ #include "Application.h" #include "minecraft/PackProfile.h" -unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) +unique_qobject_ptr ModFilterWidget::create(MinecraftInstance* instance, bool extended, QWidget* parent) { - return unique_qobject_ptr(new ModFilterWidget(instance, extendedSupport, parent)); + return unique_qobject_ptr(new ModFilterWidget(instance, extended, parent)); } class VersionBasicModel : public QIdentityProxyModel { @@ -65,55 +65,50 @@ class VersionBasicModel : public QIdentityProxyModel { } }; -ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent) +ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWidget* parent) : QTabWidget(parent), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()) { ui->setupUi(this); m_versions_proxy = new VersionProxyModel(this); - m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter("(release)", false)); + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release")); auto proxy = new VersionBasicModel(this); proxy->setSourceModel(m_versions_proxy); - if (!extendedSupport) { - ui->versionsSimpleCb->setModel(proxy); - ui->versionsCb->hide(); - ui->snapshotsCb->hide(); - ui->envBox->hide(); + if (extended) { + ui->versions->setSourceModel(proxy); + ui->versions->setSeparator(", "); + ui->version->hide(); } else { - ui->versionsCb->setSourceModel(proxy); - ui->versionsCb->setSeparator("| "); - ui->versionsSimpleCb->hide(); + ui->version->setModel(proxy); + ui->versions->hide(); + ui->showAllVersions->hide(); + ui->environmentGroup->hide(); } - ui->versionsCb->setStyleSheet("combobox-popup: 0;"); - ui->versionsSimpleCb->setStyleSheet("combobox-popup: 0;"); - connect(ui->snapshotsCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onIncludeSnapshotsChanged); - connect(ui->versionsCb, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); - connect(ui->versionsSimpleCb, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); + ui->versions->setStyleSheet("combobox-popup: 0;"); + ui->version->setStyleSheet("combobox-popup: 0;"); + connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); + connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); - connect(ui->neoForgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->forgeCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->fabricCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->quiltCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->liteLoaderCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->cauldronCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - ui->liteLoaderCb->hide(); - ui->cauldronCb->hide(); + connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); + connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); - connect(ui->serverEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); - connect(ui->clientEnv, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + if (extended) { + connect(ui->clientSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + connect(ui->serverSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged); + } - connect(ui->hide_installed, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); - - connect(ui->releaseCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - connect(ui->betaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged); - - connect(ui->categoriesList, &QListWidget::itemClicked, this, &ModFilterWidget::onCategoryClicked); + connect(ui->hideInstalled, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged); setHidden(true); loadVersionList(); @@ -147,8 +142,8 @@ void ModFilterWidget::loadVersionList() auto task = m_version_list->getLoadTask(); connect(task.get(), &Task::failed, [this] { - ui->versionsCb->setEnabled(false); - ui->snapshotsCb->setEnabled(false); + ui->versions->setEnabled(false); + ui->showAllVersions->setEnabled(false); }); connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit); @@ -165,35 +160,35 @@ void ModFilterWidget::loadVersionList() void ModFilterWidget::prepareBasicFilter() { m_filter->hideInstalled = false; - m_filter->side = ""; // or "both"t + m_filter->side = ""; // or "both" auto loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); - ui->neoForgeCb->setChecked(loaders & ModPlatform::NeoForge); - ui->forgeCb->setChecked(loaders & ModPlatform::Forge); - ui->fabricCb->setChecked(loaders & ModPlatform::Fabric); - ui->quiltCb->setChecked(loaders & ModPlatform::Quilt); - ui->liteLoaderCb->setChecked(loaders & ModPlatform::LiteLoader); - ui->cauldronCb->setChecked(loaders & ModPlatform::Cauldron); + ui->neoForge->setChecked(loaders & ModPlatform::NeoForge); + ui->forge->setChecked(loaders & ModPlatform::Forge); + ui->fabric->setChecked(loaders & ModPlatform::Fabric); + ui->quilt->setChecked(loaders & ModPlatform::Quilt); m_filter->loaders = loaders; auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); - m_filter->versions.push_front(Version{ def }); - ui->versionsCb->setCheckedItems({ def }); - ui->versionsSimpleCb->setCurrentIndex(ui->versionsSimpleCb->findText(def)); + m_filter->versions.emplace_front(def); + ui->versions->setCheckedItems({ def }); + ui->version->setCurrentIndex(ui->version->findText(def)); } -void ModFilterWidget::onIncludeSnapshotsChanged() +void ModFilterWidget::onShowAllVersionsChanged() { - QString filter = "(release)"; - if (ui->snapshotsCb->isChecked()) - filter += "|(snapshot)"; - m_versions_proxy->setFilter(BaseVersionList::TypeRole, new RegexpFilter(filter, false)); + if (ui->showAllVersions->isChecked()) + m_versions_proxy->clearFilters(); + else + m_versions_proxy->setFilter(BaseVersionList::TypeRole, new ExactFilter("release")); } void ModFilterWidget::onVersionFilterChanged(int) { - auto versions = ui->versionsCb->checkedItems(); + auto versions = ui->versions->checkedItems(); m_filter->versions.clear(); - for (auto version : versions) - m_filter->versions.push_back(version); + + for (const QString& version : versions) + m_filter->versions.emplace_back(version); + m_filter_changed = true; emit filterChanged(); } @@ -201,18 +196,14 @@ void ModFilterWidget::onVersionFilterChanged(int) void ModFilterWidget::onLoadersFilterChanged() { ModPlatform::ModLoaderTypes loaders; - if (ui->neoForgeCb->isChecked()) + if (ui->neoForge->isChecked()) loaders |= ModPlatform::NeoForge; - if (ui->forgeCb->isChecked()) + if (ui->forge->isChecked()) loaders |= ModPlatform::Forge; - if (ui->fabricCb->isChecked()) + if (ui->fabric->isChecked()) loaders |= ModPlatform::Fabric; - if (ui->quiltCb->isChecked()) + if (ui->quilt->isChecked()) loaders |= ModPlatform::Quilt; - if (ui->cauldronCb->isChecked()) - loaders |= ModPlatform::Cauldron; - if (ui->liteLoaderCb->isChecked()) - loaders |= ModPlatform::LiteLoader; m_filter_changed = loaders != m_filter->loaders; m_filter->loaders = loaders; if (m_filter_changed) @@ -224,14 +215,17 @@ void ModFilterWidget::onLoadersFilterChanged() void ModFilterWidget::onSideFilterChanged() { QString side; - if (ui->serverEnv->isChecked()) - side = "server"; - if (ui->clientEnv->isChecked()) { - if (side.isEmpty()) + + if (ui->clientSide->isChecked() != ui->serverSide->isChecked()) { + if (ui->clientSide->isChecked()) side = "client"; else - side = ""; // or both + side = "server"; + } else { + // both are checked or none are checked; in either case no filtering will happen + side = ""; } + m_filter_changed = side != m_filter->side; m_filter->side = side; if (m_filter_changed) @@ -242,7 +236,7 @@ void ModFilterWidget::onSideFilterChanged() void ModFilterWidget::onHideInstalledFilterChanged() { - auto hide = ui->hide_installed->isChecked(); + auto hide = ui->hideInstalled->isChecked(); m_filter_changed = hide != m_filter->hideInstalled; m_filter->hideInstalled = hide; if (m_filter_changed) @@ -251,60 +245,36 @@ void ModFilterWidget::onHideInstalledFilterChanged() emit filterUnchanged(); } -void ModFilterWidget::onReleaseFilterChanged() -{ - std::list releases; - if (ui->releaseCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Release)); - if (ui->betaCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Beta)); - if (ui->alphaCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Alpha)); - if (ui->unknownCb->isChecked()) - releases.push_back(ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::VersionType::Unknown)); - m_filter_changed = releases != m_filter->releases; - m_filter->releases = releases; - if (m_filter_changed) - emit filterChanged(); - else - emit filterUnchanged(); -} - -void ModFilterWidget::onVersionFilterTextChanged(QString version) +void ModFilterWidget::onVersionFilterTextChanged(const QString& version) { m_filter->versions.clear(); - m_filter->versions.push_front(version); + m_filter->versions.emplace_back(version); m_filter_changed = true; emit filterChanged(); } -void ModFilterWidget::setCategories(QList categories) +void ModFilterWidget::setCategories(const QList& categories) { - ui->categoriesList->clear(); m_categories = categories; - for (auto cat : categories) { - auto item = new QListWidgetItem(cat.name, ui->categoriesList); - item->setFlags(item->flags() & (~Qt::ItemIsUserCheckable)); - item->setCheckState(Qt::Unchecked); - ui->categoriesList->addItem(item); + + delete ui->categoryGroup->layout(); + auto layout = new QVBoxLayout(ui->categoryGroup); + + for (const auto& category : categories) { + auto checkbox = new QCheckBox(category.name); + layout->addWidget(checkbox); + + const QString id = category.id; + connect(checkbox, &QCheckBox::toggled, this, [this, id](bool checked) { + if (checked) + m_filter->categoryIds.append(id); + else + m_filter->categoryIds.removeOne(id); + + m_filter_changed = true; + emit filterChanged(); + }); } } -void ModFilterWidget::onCategoryClicked(QListWidgetItem* itm) -{ - if (itm) - itm->setCheckState(itm->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); - m_filter->categoryIds.clear(); - for (auto i = 0; i < ui->categoriesList->count(); i++) { - auto item = ui->categoriesList->item(i); - if (item->checkState() == Qt::Checked) { - auto c = std::find_if(m_categories.cbegin(), m_categories.cend(), [item](auto v) { return v.name == item->text(); }); - if (c != m_categories.cend()) - m_filter->categoryIds << c->id; - } - } - m_filter_changed = true; - emit filterChanged(); -}; - #include "ModFilterWidget.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index dd98d9afa..4358dd91d 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -73,7 +73,7 @@ class ModFilterWidget : public QTabWidget { bool operator!=(const Filter& other) const { return !(*this == other); } }; - static unique_qobject_ptr create(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); + static unique_qobject_ptr create(MinecraftInstance* instance, bool extended, QWidget* parent = nullptr); virtual ~ModFilterWidget(); auto getFilter() -> std::shared_ptr; @@ -84,7 +84,7 @@ class ModFilterWidget : public QTabWidget { void filterUnchanged(); public slots: - void setCategories(QList); + void setCategories(const QList&); private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); @@ -94,13 +94,11 @@ class ModFilterWidget : public QTabWidget { private slots: void onVersionFilterChanged(int); - void onVersionFilterTextChanged(QString version); - void onReleaseFilterChanged(); + void onVersionFilterTextChanged(const QString& version); void onLoadersFilterChanged(); void onSideFilterChanged(); void onHideInstalledFilterChanged(); - void onIncludeSnapshotsChanged(); - void onCategoryClicked(QListWidgetItem* item); + void onShowAllVersionsChanged(); private: Ui::ModFilterWidget* ui; diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 56cd33fa6..27f3b8610 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -1,237 +1,187 @@ ModFilterWidget - + 0 0 - 460 - 132 + 553 + 604 - - - 0 - 0 - + + Form - - 0 - - - - Minecraft versions - - - - - - Include Snapshots + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 553 + 604 + - - - - - - - 0 - 0 - - - - 5 - - - QComboBox::AdjustToContentsOnFirstShow - - - - - - - - - - - Loaders - - - - QLayout::SetMinimumSize - - - - - Fabric - - - - - - - NeoForge - - - - - - - Forge - - - - - - - false - - - Cauldron - - - - - - - Quilt - - - - - - - false - - - LiteLoader - - - - - - - - Release type - - - - - - Release - - - - - - - Beta - - - - - - - Alpha - - - - - - - Unknown - - - - - - - - Categories - - - - - - QAbstractScrollArea::AdjustToContents - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::ScrollPerItem - - - QListView::LeftToRight - - - true - - - QListView::ListMode - - - 0 - - - - - - - - Others - - - - - - Environments - - - - QLayout::SetDefaultConstraint - + - - - Client + + + Category + + + false + + + false - + + + Loader + + + false + + + false + + + + + + NeoForge + + + + + + + Forge + + + + + + + Fabric + + + + + + + Quilt + + + + + + + + + + Version + + + false + + + false + + + + + + + + + + + + Show all versions + + + + + + + + + + Environment + + + false + + + false + + + + + + Client + + + + + + + Server + + + + + + + + - Server + Hide installed items + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - - Instaled status - - - - - - Hide already installed - - - - - - - - + + + From 71904b52c1cd6addd467a44b9dffd9a2f96f30e4 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 1 Apr 2024 15:31:10 +0100 Subject: [PATCH 072/139] Make option groups plural Signed-off-by: TheKodeToad --- launcher/ui/widgets/ModFilterWidget.ui | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 27f3b8610..5f1e661e4 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -47,7 +47,7 @@ - Category + Categories false @@ -60,7 +60,7 @@ - Loader + Loaders false @@ -103,7 +103,7 @@ - Version + Versions false @@ -112,12 +112,6 @@ false - - - - - - @@ -125,13 +119,19 @@ + + + + + + - Environment + Environments false From 186a6d0c523cf63461fb4d883f0e1ead026bcb6d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 1 Apr 2024 17:00:10 +0100 Subject: [PATCH 073/139] Make filters vertical Signed-off-by: TheKodeToad --- launcher/ui/pages/modplatform/ModPage.cpp | 3 +- .../ui/pages/modplatform/ResourcePage.cpp | 6 +- launcher/ui/pages/modplatform/ResourcePage.ui | 72 +++++++++++-------- launcher/ui/widgets/ModFilterWidget.ui | 17 +++-- 4 files changed, 57 insertions(+), 41 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 8fd42b427..3561afd38 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -68,10 +68,9 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) if (m_filter_widget) disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); + m_ui->horizontalLayout->replaceWidget(m_filter_widget == nullptr ? m_ui->filterWidget : m_filter_widget.get(), widget.get()); m_filter_widget.swap(widget); - m_ui->gridLayout_3->addWidget(m_filter_widget.get(), 0, 0, 1, m_ui->gridLayout_3->columnCount()); - m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ResourcePage::updateVersionList); diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 35c4afc0d..112b01b20 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -71,7 +71,7 @@ ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_in m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - m_ui->gridLayout_3->addWidget(&m_fetch_progress, 0, 0, 1, m_ui->gridLayout_3->columnCount()); + layout()->replaceWidget(m_ui->fetchProgress, &m_fetch_progress); m_ui->packView->setItemDelegate(new ProjectItemDelegate(this)); m_ui->packView->installEventFilter(this); @@ -93,8 +93,10 @@ void ResourcePage::retranslate() void ResourcePage::openedImpl() { - if (!supportsFiltering()) + if (!supportsFiltering()) { m_ui->resourceFilterButton->setVisible(false); + m_ui->filterWidget->hide(); + } //: String in the search bar of the mod downloading dialog m_ui->searchEdit->setPlaceholderText(tr("Search for %1...").arg(resourcesString())); diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index 73a9d3b1a..b4f48f074 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -11,19 +11,12 @@ - - - - - - false - - - false - - + + + + - + Qt::ScrollBarAlwaysOff @@ -39,19 +32,19 @@ + + + + false + + + false + + + - - - - Search - - - - - - - + @@ -74,13 +67,6 @@ - - - - Filter options - - - @@ -88,6 +74,26 @@ + + + + + + + Search + + + + + + + + + + Filter options + + + @@ -96,6 +102,12 @@ QTextBrowser
ui/widgets/ProjectDescriptionPage.h
+ + ProgressWidget + QWidget +
ui/widgets/ProgressWidget.h
+ 1 +
searchEdit diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 5f1e661e4..13727167f 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -6,10 +6,16 @@ 0 0 - 553 - 604 + 310 + 600 + + + 310 + 16777215 + + Form @@ -28,9 +34,6 @@ - - QFrame::NoFrame - true @@ -39,8 +42,8 @@ 0 0 - 553 - 604 + 308 + 598 From 1c3c9d24573d4f3cf25580129d3611f5082dcc29 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 7 Apr 2024 13:48:34 +0300 Subject: [PATCH 074/139] Added the new ftb settings path Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/import_ftb/ListModel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index 52671ea8e..f3c737977 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -44,7 +44,9 @@ QString getFTBRoot() QString getDynamicPath() { - auto settingsPath = FS::PathCombine(getFTBRoot(), "bin", "settings.json"); + auto settingsPath = FS::PathCombine(getFTBRoot(), "storage", "settings.json"); + if (!QFileInfo::exists(settingsPath)) + settingsPath = FS::PathCombine(getFTBRoot(), "bin", "settings.json"); if (!QFileInfo::exists(settingsPath)) { qWarning() << "The ftb app setings doesn't exist."; return {}; From 1961e2081fc8f0787622820a33543751b8aef0a7 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 25 Apr 2024 00:48:40 +0300 Subject: [PATCH 075/139] Removed scroll from version checkbox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 4 ++-- launcher/ui/widgets/ModFilterWidget.cpp | 9 +++++++++ launcher/ui/widgets/ModFilterWidget.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 8117baa58..189f7ebb4 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -68,10 +68,10 @@ class CheckComboModel : public QIdentityProxyModel { QStringList checked; }; -CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",") +CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ") { QLineEdit* lineEdit = new QLineEdit(this); - lineEdit->setReadOnly(false); + lineEdit->setReadOnly(true); setLineEdit(lineEdit); lineEdit->disconnect(this); setInsertPolicy(QComboBox::NoInsert); diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index b44a573d8..ead573aee 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -89,6 +89,8 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi ui->versions->setStyleSheet("combobox-popup: 0;"); ui->version->setStyleSheet("combobox-popup: 0;"); + ui->versions->installEventFilter(this); + ui->version->installEventFilter(this); connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); @@ -115,6 +117,13 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi prepareBasicFilter(); } +bool ModFilterWidget::eventFilter(QObject* obj, QEvent* evt) +{ + if ((obj != ui->versions && obj != ui->version) || evt->type() != QEvent::Wheel) + return QTabWidget::eventFilter(obj, evt); + return true; +} + auto ModFilterWidget::getFilter() -> std::shared_ptr { m_filter_changed = false; diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 4358dd91d..35125c910 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -85,6 +85,7 @@ class ModFilterWidget : public QTabWidget { public slots: void setCategories(const QList&); + bool eventFilter(QObject* obj, QEvent* evt); private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); From dedea2c05d8503fea0d6b7c0adea25e64c8d511c Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Thu, 25 Apr 2024 14:56:22 -0400 Subject: [PATCH 076/139] Added a naive implementation of a
inserter Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 426067bf6..8b1f11f33 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -24,7 +24,15 @@ QString markdownToHTML(const QString& markdown) char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE); QString htmlStr(buffer); - + int first_pos = htmlStr.indexOf(""); + int img_pos = 0; + while( first_pos != -1 ) + { + img_pos = htmlStr.indexOf(""); + first_pos = htmlStr.indexOf("", first_pos+5); + } free(buffer); return htmlStr; From 2cfe482298826bf0bdb948cca65f72930e142859 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Thu, 25 Apr 2024 15:40:24 -0400 Subject: [PATCH 077/139] Formatting and fixed img_pos allowed as negative Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 8b1f11f33..31e026dc7 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -24,16 +24,20 @@ QString markdownToHTML(const QString& markdown) char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE); QString htmlStr(buffer); - int first_pos = htmlStr.indexOf(""); - int img_pos = 0; - while( first_pos != -1 ) - { - img_pos = htmlStr.indexOf(""); - first_pos = htmlStr.indexOf("", first_pos+5); - } + free(buffer); + + // Insert a breakpoint between a and tag as this can cause visual bugs + int first_pos = htmlStr.indexOf(""); + int img_pos; + while (first_pos != -1) { + img_pos = htmlStr.indexOf(" -1) // 5 is the size of the tag + htmlStr.insert(img_pos, "
"); + + first_pos = htmlStr.indexOf("", first_pos + 5); + } return htmlStr; } \ No newline at end of file From e1ed317c137c2ea3f4f54cb5a1c65e9f309d3ad1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 26 Apr 2024 00:16:48 +0300 Subject: [PATCH 078/139] Updated CheckComboBox to look more like QComboBox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 56 +++++++++++++------------ launcher/ui/widgets/CheckComboBox.h | 5 ++- launcher/ui/widgets/ModFilterWidget.cpp | 9 ---- launcher/ui/widgets/ModFilterWidget.h | 1 - 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 189f7ebb4..f8411dd79 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -26,6 +26,7 @@ #include #include #include +#include class CheckComboModel : public QIdentityProxyModel { Q_OBJECT @@ -70,12 +71,6 @@ class CheckComboModel : public QIdentityProxyModel { CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(", ") { - QLineEdit* lineEdit = new QLineEdit(this); - lineEdit->setReadOnly(true); - setLineEdit(lineEdit); - lineEdit->disconnect(this); - setInsertPolicy(QComboBox::NoInsert); - view()->installEventFilter(this); view()->window()->installEventFilter(this); view()->viewport()->installEventFilter(this); @@ -89,9 +84,9 @@ void CheckComboBox::setSourceModel(QAbstractItemModel* new_model) model()->disconnect(this); QComboBox::setModel(proxy); connect(this, QOverload::of(&QComboBox::activated), this, &CheckComboBox::toggleCheckState); - connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::updateCheckedItems); - connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::updateCheckedItems); - connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::updateCheckedItems); + connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::emitCheckedItemsChanged); + connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::emitCheckedItemsChanged); + connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::emitCheckedItemsChanged); } void CheckComboBox::hidePopup() @@ -100,15 +95,9 @@ void CheckComboBox::hidePopup() QComboBox::hidePopup(); } -void CheckComboBox::updateCheckedItems() +void CheckComboBox::emitCheckedItemsChanged() { - QStringList items = checkedItems(); - if (items.isEmpty()) - setEditText(defaultText()); - else - setEditText(items.join(separator())); - - emit checkedItemsChanged(items); + emit checkedItemsChanged(checkedItems()); } QString CheckComboBox::defaultText() const @@ -118,10 +107,7 @@ QString CheckComboBox::defaultText() const void CheckComboBox::setDefaultText(const QString& text) { - if (m_default_text != text) { - m_default_text = text; - updateCheckedItems(); - } + m_default_text = text; } QString CheckComboBox::separator() const @@ -131,10 +117,7 @@ QString CheckComboBox::separator() const void CheckComboBox::setSeparator(const QString& separator) { - if (m_separator != separator) { - m_separator = separator; - updateCheckedItems(); - } + m_separator = separator; } bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) @@ -157,6 +140,8 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) case QEvent::MouseButtonRelease: containerMousePress = false; break; + case QEvent::Wheel: + return receiver == this; default: break; } @@ -170,7 +155,7 @@ void CheckComboBox::toggleCheckState(int index) Qt::CheckState state = static_cast(value.toInt()); setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole); } - updateCheckedItems(); + emitCheckedItemsChanged(); } Qt::CheckState CheckComboBox::itemCheckState(int index) const @@ -198,4 +183,23 @@ void CheckComboBox::setCheckedItems(const QStringList& items) } } +void CheckComboBox::paintEvent(QPaintEvent*) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + QStringList items = checkedItems(); + if (items.isEmpty()) + opt.currentText = defaultText(); + else + opt.currentText = items.join(separator()); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} + #include "CheckComboBox.moc" \ No newline at end of file diff --git a/launcher/ui/widgets/CheckComboBox.h b/launcher/ui/widgets/CheckComboBox.h index 4d6daf5a4..876c6e3e1 100644 --- a/launcher/ui/widgets/CheckComboBox.h +++ b/launcher/ui/widgets/CheckComboBox.h @@ -49,8 +49,11 @@ class CheckComboBox : public QComboBox { signals: void checkedItemsChanged(const QStringList& items); + protected: + void paintEvent(QPaintEvent*) override; + private: - void updateCheckedItems(); + void emitCheckedItemsChanged(); bool eventFilter(QObject* receiver, QEvent* event) override; void toggleCheckState(int index); diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index ead573aee..b44a573d8 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -89,8 +89,6 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi ui->versions->setStyleSheet("combobox-popup: 0;"); ui->version->setStyleSheet("combobox-popup: 0;"); - ui->versions->installEventFilter(this); - ui->version->installEventFilter(this); connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); @@ -117,13 +115,6 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi prepareBasicFilter(); } -bool ModFilterWidget::eventFilter(QObject* obj, QEvent* evt) -{ - if ((obj != ui->versions && obj != ui->version) || evt->type() != QEvent::Wheel) - return QTabWidget::eventFilter(obj, evt); - return true; -} - auto ModFilterWidget::getFilter() -> std::shared_ptr { m_filter_changed = false; diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 35125c910..4358dd91d 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -85,7 +85,6 @@ class ModFilterWidget : public QTabWidget { public slots: void setCategories(const QList&); - bool eventFilter(QObject* obj, QEvent* evt); private: ModFilterWidget(MinecraftInstance* instance, bool extendedSupport, QWidget* parent = nullptr); From 6ecd2c7f314d0e5fe7c8ce18ab520e3a38e8d717 Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:51:23 -0400 Subject: [PATCH 079/139] Update launcher/Markdown.cpp Co-authored-by: Alexandru Ionut Tripon Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/Markdown.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 31e026dc7..f9081057c 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -26,18 +26,23 @@ QString markdownToHTML(const QString& markdown) QString htmlStr(buffer); free(buffer); - - // Insert a breakpoint between a and tag as this can cause visual bugs - int first_pos = htmlStr.indexOf(""); - int img_pos; - while (first_pos != -1) { - img_pos = htmlStr.indexOf(" -1) // 5 is the size of the tag - htmlStr.insert(img_pos, "
"); + int pos = htmlStr.indexOf(""); + int imgPos; + while (pos != -1) { + pos = pos + 5; // 5 is the size of the tag + imgPos = htmlStr.indexOf("", first_pos + 5); + auto textBetween = htmlStr.mid(pos, imgPos - pos).trimmed(); // trim all white spaces + + if (textBetween.isEmpty()) + htmlStr.insert(pos, "
"); + + pos = htmlStr.indexOf("", pos); } + return htmlStr; } \ No newline at end of file From a988415028fafb1414420573b13d4e1113efe03e Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:59:51 -0400 Subject: [PATCH 080/139] Pleasing the format check Co-authored-by: Alexandru Ionut Tripon Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/Markdown.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index f9081057c..1e7fb9f35 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -43,6 +43,5 @@ QString markdownToHTML(const QString& markdown) pos = htmlStr.indexOf("", pos); } - return htmlStr; } \ No newline at end of file From 5348a90a15676ed178f790648d60f4d3c3c23cf1 Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Fri, 26 Apr 2024 18:21:17 -0400 Subject: [PATCH 081/139] Fix img tag allowing img.... Co-authored-by: TheKodeToad Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/Markdown.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 1e7fb9f35..4ee1c0d28 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -31,7 +31,7 @@ QString markdownToHTML(const QString& markdown) int imgPos; while (pos != -1) { pos = pos + 5; // 5 is the size of the tag - imgPos = htmlStr.indexOf(" Date: Tue, 30 Apr 2024 22:40:04 -0400 Subject: [PATCH 082/139] Moved html patch to StringUtils Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 18 +----------------- launcher/StringUtils.cpp | 20 ++++++++++++++++++++ launcher/StringUtils.h | 2 ++ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index 4ee1c0d28..a624791df 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -26,22 +26,6 @@ QString markdownToHTML(const QString& markdown) QString htmlStr(buffer); free(buffer); - - int pos = htmlStr.indexOf(""); - int imgPos; - while (pos != -1) { - pos = pos + 5; // 5 is the size of the tag - imgPos = htmlStr.indexOf(""); - - pos = htmlStr.indexOf("", pos); - } - + return htmlStr; } \ No newline at end of file diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 72ccdfbff..db53f353e 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -212,3 +212,23 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular right = s.mid(end); return qMakePair(left, right); } + +QString StringUtils::htmlListPatch(QString htmlStr) +{ + int pos = htmlStr.indexOf(""); + int imgPos; + while (pos != -1) { + pos = pos + 5; // 5 is the size of the tag + imgPos = htmlStr.indexOf(""); + + pos = htmlStr.indexOf("", pos); + } + return htmlStr; +} \ No newline at end of file diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 9d2bdd85e..624ee41a3 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -85,4 +85,6 @@ QPair splitFirst(const QString& s, const QString& sep, Qt::Cas QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); QPair splitFirst(const QString& s, const QRegularExpression& re); +QString htmlListPatch(QString htmlStr); + } // namespace StringUtils From e3d64608b947eeb774a4ff317c3984bcac8d83be Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Tue, 30 Apr 2024 23:28:50 -0400 Subject: [PATCH 083/139] Switched to a Regex expression Signed-off-by: SabrePenguin --- launcher/StringUtils.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index db53f353e..125b8ca6f 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -215,11 +215,13 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular QString StringUtils::htmlListPatch(QString htmlStr) { - int pos = htmlStr.indexOf(""); - int imgPos; + QRegularExpression match("|"); + int pos = htmlStr.indexOf(match); + int imgPos, dist; while (pos != -1) { - pos = pos + 5; // 5 is the size of the tag - imgPos = htmlStr.indexOf("", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index + pos = pos + dist; + imgPos = htmlStr.indexOf(""); - pos = htmlStr.indexOf("", pos); + pos = htmlStr.indexOf(match, pos); } return htmlStr; } \ No newline at end of file From b9c010ffb2bcf8c08a38014079064eeba0af2fd9 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Tue, 30 Apr 2024 23:43:36 -0400 Subject: [PATCH 084/139] Added patch to all setHtml calls Signed-off-by: SabrePenguin --- launcher/ui/dialogs/AboutDialog.cpp | 5 +++-- launcher/ui/dialogs/ExportToModListDialog.cpp | 5 +++-- launcher/ui/dialogs/ModUpdateDialog.cpp | 3 ++- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 3 ++- launcher/ui/pages/instance/ManagedPackPage.cpp | 8 +++++--- launcher/ui/pages/modplatform/ResourcePage.cpp | 5 +++-- launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp | 3 ++- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 3 ++- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 7 +++++-- launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp | 3 ++- launcher/ui/pages/modplatform/technic/TechnicPage.cpp | 3 ++- launcher/updater/prismupdater/UpdaterDialogs.cpp | 3 ++- 12 files changed, 33 insertions(+), 18 deletions(-) diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 17b79ecaa..40c852c37 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -39,6 +39,7 @@ #include "BuildConfig.h" #include "Markdown.h" #include "ui_AboutDialog.h" +#include "StringUtils.h" #include #include @@ -139,10 +140,10 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia setWindowTitle(tr("About %1").arg(launcherName)); QString chtml = getCreditsHtml(); - ui->creditsText->setHtml(chtml); + ui->creditsText->setHtml(StringUtils::htmlListPatch(chtml)); QString lhtml = getLicenseHtml(); - ui->licenseText->setHtml(lhtml); + ui->licenseText->setHtml(StringUtils::htmlListPatch(lhtml)); ui->urlLabel->setOpenExternalLinks(true); diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index a343f555a..ab85cf584 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -26,6 +26,7 @@ #include "minecraft/mod/ModFolderModel.h" #include "modplatform/helpers/ExportToModList.h" #include "ui_ExportToModListDialog.h" +#include "StringUtils.h" #include #include @@ -143,10 +144,10 @@ void ExportToModListDialog::triggerImp() case ExportToModList::CUSTOM: return; case ExportToModList::HTML: - ui->resultText->setHtml(txt); + ui->resultText->setHtml(StringUtils::htmlListPatch(txt)); break; case ExportToModList::MARKDOWN: - ui->resultText->setHtml(markdownToHTML(txt)); + ui->resultText->setHtml(StringUtils::htmlListPatch(markdownToHTML(txt))); break; case ExportToModList::PLAINTXT: break; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 54893d775..a08b36ec1 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -7,6 +7,7 @@ #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" #include "ui_ReviewMessageBox.h" +#include "StringUtils.h" #include "Markdown.h" @@ -473,7 +474,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri break; } - changelog_area->setHtml(text); + changelog_area->setHtml(StringUtils::htmlListPatch(text)); changelog_area->setOpenExternalLinks(true); changelog_area->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); changelog_area->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 5eebe87a3..797b763c2 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -26,6 +26,7 @@ #include "BuildConfig.h" #include "Markdown.h" #include "ui_UpdateAvailableDialog.h" +#include "StringUtils.h" UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, const QString& availableVersion, @@ -43,7 +44,7 @@ UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, ui->icon->setPixmap(APPLICATION->getThemedIcon("checkupdate").pixmap(64)); auto releaseNotesHtml = markdownToHTML(releaseNotes); - ui->releaseNotes->setHtml(releaseNotesHtml); + ui->releaseNotes->setHtml(StringUtils::htmlListPatch(releaseNotesHtml)); ui->releaseNotes->setOpenExternalLinks(true); connect(ui->skipButton, &QPushButton::clicked, this, [this]() { diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 2210d0263..9c3fcc354 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -20,6 +20,7 @@ #include "InstanceTask.h" #include "Json.h" #include "Markdown.h" +#include "StringUtils.h" #include "modplatform/modrinth/ModrinthPackManifest.h" @@ -332,7 +333,7 @@ void ModrinthManagedPackPage::suggestVersion() } auto version = m_pack.versions.at(index); - ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8())); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(markdownToHTML(version.changelog.toUtf8()))); ManagedPackPage::suggestVersion(); } @@ -420,7 +421,7 @@ void FlameManagedPackPage::parseManagedPack() "Don't worry though, it will ask you to update this instance instead, so you'll not lose this instance!" ""); - ui->changelogTextBrowser->setHtml(message); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(message)); return; } @@ -502,7 +503,8 @@ void FlameManagedPackPage::suggestVersion() } auto version = m_pack.versions.at(index); - ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId)); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch( + m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId))); ManagedPackPage::suggestVersion(); } diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index ae48e5523..b9c706c6c 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -45,6 +45,7 @@ #include "Markdown.h" +#include "StringUtils.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ResourceModel.h" #include "ui/widgets/ProjectItem.h" @@ -234,8 +235,8 @@ void ResourcePage::updateUi() text += "
"; - m_ui->packDescription->setHtml( - text + (current_pack->extraData.body.isEmpty() ? current_pack->description : markdownToHTML(current_pack->extraData.body))); + m_ui->packDescription->setHtml(StringUtils::htmlListPatch( + text + (current_pack->extraData.body.isEmpty() ? current_pack->description : markdownToHTML(current_pack->extraData.body)))); m_ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index e492830c6..d79b7621a 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -39,6 +39,7 @@ #include "ui_AtlPage.h" #include "BuildConfig.h" +#include "StringUtils.h" #include "AtlUserInteractionSupportImpl.h" #include "modplatform/atlauncher/ATLPackInstallTask.h" @@ -144,7 +145,7 @@ void AtlPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex selected = filterModel->data(first, Qt::UserRole).value(); - ui->packDescription->setHtml(selected.description.replace("\n", "
")); + ui->packDescription->setHtml(StringUtils::htmlListPatch(selected.description.replace("\n", "
"))); for (const auto& version : selected.versions) { ui->versionSelectionBox->addItem(version.version); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f1fd9b5d8..e851e47be 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -46,6 +46,7 @@ #include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" +#include "StringUtils.h" #include "net/ApiDownload.h" @@ -292,6 +293,6 @@ void FlamePage::updateUi() text += "
"; text += api.getModDescription(current.addonId).toUtf8(); - ui->packDescription->setHtml(text + current.description); + ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description)); ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 0ecaf4625..683d2e81d 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -37,6 +37,7 @@ #include "Page.h" #include "ui/widgets/ProjectItem.h" #include "ui_Page.h" +#include "StringUtils.h" #include @@ -260,8 +261,10 @@ void Page::onPackSelectionChanged(Modpack* pack) { ui->versionSelectionBox->clear(); if (pack) { - currentModpackInfo->setHtml("Pack by " + pack->author + "" + "
Minecraft " + pack->mcVersion + "
" + "
" + - pack->description + "
  • " + pack->mods.replace(";", "
  • ") + "
"); + currentModpackInfo->setHtml(StringUtils::htmlListPatch( + "Pack by " + pack->author + "" + "
Minecraft " + pack->mcVersion + + "
" + "
" + + pack->description + "
  • " + pack->mods.replace(";", "
  • ") + "
")); bool currentAdded = false; for (int i = 0; i < pack->oldVersions.size(); i++) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index da5fe1e7b..b44afda7e 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -44,6 +44,7 @@ #include "InstanceImportTask.h" #include "Json.h" #include "Markdown.h" +#include "StringUtils.h" #include "ui/widgets/ProjectItem.h" @@ -304,7 +305,7 @@ void ModrinthPage::updateUI() text += markdownToHTML(current.extra.body.toUtf8()); - ui->packDescription->setHtml(text + current.description); + ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description)); ui->packDescription->flush(); } diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 6b1ec8cb5..72b7814c9 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -47,6 +47,7 @@ #include "TechnicModel.h" #include "modplatform/technic/SingleZipPackInstallTask.h" #include "modplatform/technic/SolderPackInstallTask.h" +#include "StringUtils.h" #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" @@ -233,7 +234,7 @@ void TechnicPage::metadataLoaded() text += "

"; - ui->packDescription->setHtml(text + current.description); + ui->packDescription->setHtml(StringUtils::htmlListPatch(text + current.description)); // Strip trailing forward-slashes from Solder URL's if (current.isSolder) { diff --git a/launcher/updater/prismupdater/UpdaterDialogs.cpp b/launcher/updater/prismupdater/UpdaterDialogs.cpp index 395b658db..06dc161b1 100644 --- a/launcher/updater/prismupdater/UpdaterDialogs.cpp +++ b/launcher/updater/prismupdater/UpdaterDialogs.cpp @@ -26,6 +26,7 @@ #include #include "Markdown.h" +#include "StringUtils.h" SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const QList& releases, QWidget* parent) : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) @@ -96,7 +97,7 @@ void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidget QString body = markdownToHTML(release.body.toUtf8()); m_selectedRelease = release; - ui->changelogTextBrowser->setHtml(body); + ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(body)); } SelectReleaseAssetDialog::SelectReleaseAssetDialog(const QList& assets, QWidget* parent) From 51f4ede7977c3bb6d0d2f3866d3faf57f3b43dab Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Wed, 1 May 2024 00:11:53 -0400 Subject: [PATCH 085/139] Fixing CI format issues Signed-off-by: SabrePenguin --- launcher/ui/dialogs/AboutDialog.cpp | 2 +- launcher/ui/dialogs/ExportToModListDialog.cpp | 2 +- launcher/ui/dialogs/ModUpdateDialog.cpp | 2 +- launcher/ui/dialogs/UpdateAvailableDialog.cpp | 2 +- launcher/ui/pages/instance/ManagedPackPage.cpp | 4 ++-- launcher/ui/pages/modplatform/flame/FlamePage.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 9 ++++----- launcher/ui/pages/modplatform/technic/TechnicPage.cpp | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 40c852c37..b652ba991 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -38,8 +38,8 @@ #include "Application.h" #include "BuildConfig.h" #include "Markdown.h" -#include "ui_AboutDialog.h" #include "StringUtils.h" +#include "ui_AboutDialog.h" #include #include diff --git a/launcher/ui/dialogs/ExportToModListDialog.cpp b/launcher/ui/dialogs/ExportToModListDialog.cpp index ab85cf584..7debf0cd8 100644 --- a/launcher/ui/dialogs/ExportToModListDialog.cpp +++ b/launcher/ui/dialogs/ExportToModListDialog.cpp @@ -22,11 +22,11 @@ #include #include "FileSystem.h" #include "Markdown.h" +#include "StringUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/helpers/ExportToModList.h" #include "ui_ExportToModListDialog.h" -#include "StringUtils.h" #include #include diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index a08b36ec1..6649bee4e 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -3,11 +3,11 @@ #include "CustomMessageBox.h" #include "ProgressDialog.h" #include "ScrollMessageBox.h" +#include "StringUtils.h" #include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" #include "ui_ReviewMessageBox.h" -#include "StringUtils.h" #include "Markdown.h" diff --git a/launcher/ui/dialogs/UpdateAvailableDialog.cpp b/launcher/ui/dialogs/UpdateAvailableDialog.cpp index 797b763c2..810a1f089 100644 --- a/launcher/ui/dialogs/UpdateAvailableDialog.cpp +++ b/launcher/ui/dialogs/UpdateAvailableDialog.cpp @@ -25,8 +25,8 @@ #include "Application.h" #include "BuildConfig.h" #include "Markdown.h" -#include "ui_UpdateAvailableDialog.h" #include "StringUtils.h" +#include "ui_UpdateAvailableDialog.h" UpdateAvailableDialog::UpdateAvailableDialog(const QString& currentVersion, const QString& availableVersion, diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 9c3fcc354..a47403926 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -503,8 +503,8 @@ void FlameManagedPackPage::suggestVersion() } auto version = m_pack.versions.at(index); - ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch( - m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId))); + ui->changelogTextBrowser->setHtml( + StringUtils::htmlListPatch(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId))); ManagedPackPage::suggestVersion(); } diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index e851e47be..d3473412a 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -43,10 +43,10 @@ #include "FlameModel.h" #include "InstanceImportTask.h" #include "Json.h" +#include "StringUtils.h" #include "modplatform/flame/FlameAPI.h" #include "ui/dialogs/NewInstanceDialog.h" #include "ui/widgets/ProjectItem.h" -#include "StringUtils.h" #include "net/ApiDownload.h" diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 683d2e81d..80345883c 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -35,9 +35,9 @@ */ #include "Page.h" +#include "StringUtils.h" #include "ui/widgets/ProjectItem.h" #include "ui_Page.h" -#include "StringUtils.h" #include @@ -261,10 +261,9 @@ void Page::onPackSelectionChanged(Modpack* pack) { ui->versionSelectionBox->clear(); if (pack) { - currentModpackInfo->setHtml(StringUtils::htmlListPatch( - "Pack by " + pack->author + "" + "
Minecraft " + pack->mcVersion + - "
" + "
" + - pack->description + "
  • " + pack->mods.replace(";", "
  • ") + "
")); + currentModpackInfo->setHtml(StringUtils::htmlListPatch("Pack by " + pack->author + "" + "
Minecraft " + pack->mcVersion + + "
" + "
" + + pack->description + "
  • " + pack->mods.replace(";", "
  • ") + "
")); bool currentAdded = false; for (int i = 0; i < pack->oldVersions.size(); i++) { diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 72b7814c9..391c10122 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -44,10 +44,10 @@ #include "BuildConfig.h" #include "Json.h" +#include "StringUtils.h" #include "TechnicModel.h" #include "modplatform/technic/SingleZipPackInstallTask.h" #include "modplatform/technic/SolderPackInstallTask.h" -#include "StringUtils.h" #include "Application.h" #include "modplatform/technic/SolderPackManifest.h" From 814f84efab9af2fa59156d3e6101ce7fd8cffcb9 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Wed, 1 May 2024 00:21:32 -0400 Subject: [PATCH 086/139] Fixed more clang formatting Signed-off-by: SabrePenguin --- launcher/Markdown.cpp | 2 +- launcher/StringUtils.cpp | 2 +- launcher/ui/pages/modplatform/legacy_ftb/Page.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/Markdown.cpp b/launcher/Markdown.cpp index a624791df..426067bf6 100644 --- a/launcher/Markdown.cpp +++ b/launcher/Markdown.cpp @@ -26,6 +26,6 @@ QString markdownToHTML(const QString& markdown) QString htmlStr(buffer); free(buffer); - + return htmlStr; } \ No newline at end of file diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 125b8ca6f..e05affde1 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -219,7 +219,7 @@ QString StringUtils::htmlListPatch(QString htmlStr) int pos = htmlStr.indexOf(match); int imgPos, dist; while (pos != -1) { - dist = htmlStr.indexOf(">", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index + dist = htmlStr.indexOf(">", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index pos = pos + dist; imgPos = htmlStr.indexOf("versionSelectionBox->clear(); if (pack) { currentModpackInfo->setHtml(StringUtils::htmlListPatch("Pack by " + pack->author + "" + "
Minecraft " + pack->mcVersion + - "
" + "
" + - pack->description + "
  • " + pack->mods.replace(";", "
  • ") + "
")); + "
" + "
" + pack->description + "
  • " + + pack->mods.replace(";", "
  • ") + "
")); bool currentAdded = false; for (int i = 0; i < pack->oldVersions.size(); i++) { From ce873e4a0dd517fd68cb49bab037362c9616672b Mon Sep 17 00:00:00 2001 From: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> Date: Wed, 1 May 2024 12:49:34 -0400 Subject: [PATCH 087/139] Regex correction Co-authored-by: TheKodeToad Signed-off-by: SabrePenguin <147069705+SabrePenguin@users.noreply.github.com> --- launcher/StringUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index e05affde1..82147c16a 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -215,7 +215,7 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular QString StringUtils::htmlListPatch(QString htmlStr) { - QRegularExpression match("|"); + QRegularExpression match("<\\s/\\s*ul\\s*>"); int pos = htmlStr.indexOf(match); int imgPos, dist; while (pos != -1) { From 0c76e7ab20f24ecc0a804732aca6ccf3ebd1e0c3 Mon Sep 17 00:00:00 2001 From: SabrePenguin Date: Wed, 1 May 2024 13:00:55 -0400 Subject: [PATCH 088/139] Made Regex static and const, removed dist Signed-off-by: SabrePenguin --- launcher/StringUtils.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 82147c16a..edda9f247 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -213,14 +213,14 @@ QPair StringUtils::splitFirst(const QString& s, const QRegular return qMakePair(left, right); } +static const QRegularExpression ulMatcher("<\\s*/\\s*ul\\s*>"); + QString StringUtils::htmlListPatch(QString htmlStr) { - QRegularExpression match("<\\s/\\s*ul\\s*>"); - int pos = htmlStr.indexOf(match); - int imgPos, dist; + int pos = htmlStr.indexOf(ulMatcher); + int imgPos; while (pos != -1) { - dist = htmlStr.indexOf(">", pos) - pos + 1; // Get the size of the tag. Add one for zeroeth index - pos = pos + dist; + pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the tag. Add one for zeroeth index imgPos = htmlStr.indexOf(""); - pos = htmlStr.indexOf(match, pos); + pos = htmlStr.indexOf(ulMatcher, pos); } return htmlStr; } \ No newline at end of file From d795ba25e745912c9772bd084eb98f5f039d041d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 May 2024 20:55:26 +0300 Subject: [PATCH 089/139] Delete instaces tmp diectory on startup Signed-off-by: Trial97 --- launcher/Application.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index bb8751ccc..f696d9022 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1209,6 +1209,12 @@ void Application::performMainStartupAction() qDebug() << "<> Updater started."; } + { // delete instances tmp dirctory + auto instDir = m_settings->get("InstanceDir").toString(); + const QString tempRoot = FS::PathCombine(instDir, ".tmp"); + FS::deletePath(tempRoot); + } + if (!m_urlsToImport.isEmpty()) { qDebug() << "<> Importing from url:" << m_urlsToImport; m_mainWindow->processURLs(m_urlsToImport); From c15f6fcd2a8a380b59f7f49cae5010cc93efd386 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 16 May 2024 17:21:30 +0300 Subject: [PATCH 090/139] Fix portable if manifest is missing Signed-off-by: Trial97 --- launcher/updater/prismupdater/PrismUpdater.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 5fe22bdd0..e9c75e080 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -474,8 +474,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar target_dir = QDir(m_rootPath).absoluteFilePath(".."); } - QMetaObject::invokeMethod( - this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); + QMetaObject::invokeMethod(this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); @@ -1118,7 +1117,6 @@ void PrismUpdaterApp::backupAppDir() "Qt*.dll", }); } - file_list.append("portable.txt"); logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); From 051e7886837090ba62ad81b6f835372649a93b69 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Thu, 16 May 2024 19:45:00 +0300 Subject: [PATCH 091/139] Update launcher/updater/prismupdater/PrismUpdater.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/updater/prismupdater/PrismUpdater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index e9c75e080..054206c67 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -474,7 +474,8 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar target_dir = QDir(m_rootPath).absoluteFilePath(".."); } - QMetaObject::invokeMethod(this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); + QMetaObject::invokeMethod( + this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection);Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); From ab5f628453de6fc9301f73413027846451a78bd0 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Thu, 16 May 2024 19:45:43 +0300 Subject: [PATCH 092/139] Update launcher/updater/prismupdater/PrismUpdater.cpp Signed-off-by: Alexandru Ionut Tripon --- launcher/updater/prismupdater/PrismUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 054206c67..f3cc2ad56 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -475,7 +475,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar } QMetaObject::invokeMethod( - this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection);Qt::QueuedConnection); + this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); From 0f2736306eec092e7dd4e7eb05ab0c7911515927 Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Sat, 18 May 2024 10:36:15 +0300 Subject: [PATCH 093/139] Update launcher/ui/dialogs/skins/SkinManageDialog.ui Co-authored-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com> Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/dialogs/skins/SkinManageDialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.ui b/launcher/ui/dialogs/skins/SkinManageDialog.ui index c2ce9143c..ed8b7e530 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.ui +++ b/launcher/ui/dialogs/skins/SkinManageDialog.ui @@ -43,7 +43,7 @@ - Clasic + Classic true From 2ac6efaa4acac61a6c3e45948188181b4fdb118b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 18 May 2024 11:56:43 +0300 Subject: [PATCH 094/139] Fix reggresion Signed-off-by: Trial97 --- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 0307476e4..301df2af4 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1213,7 +1213,7 @@ void MainWindow::on_actionViewCentralModsFolder_triggered() void MainWindow::on_actionViewSkinsFolder_triggered() { - DesktopServices::openDirectory(APPLICATION->settings()->get("SkinsDir").toString(), true); + DesktopServices::openPath(APPLICATION->settings()->get("SkinsDir").toString(), true); } void MainWindow::on_actionViewIconThemeFolder_triggered() diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 8028a719c..9e71b867c 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -133,7 +133,7 @@ void SkinManageDialog::delayed_scroll(QModelIndex model_index) void SkinManageDialog::on_openDirBtn_clicked() { - DesktopServices::openDirectory(m_list.getDir(), true); + DesktopServices::openPath(m_list.getDir(), true); } void SkinManageDialog::on_fileBtn_clicked() From 83fdfe809134955e573a259b30232be355d66be7 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 18 May 2024 16:52:29 +0300 Subject: [PATCH 095/139] fix regression Signed-off-by: Trial97 --- launcher/ui/themes/ThemeManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index e5daf787e..1cb83ca26 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -318,7 +318,7 @@ void ThemeManager::refresh() { m_themes.clear(); m_icons.clear(); - m_catPacks.clear(); + m_cat_packs.clear(); initializeThemes(); initializeCatPacks(); From 428889a68d67aa4e110b7de9346474e27a4f3ab9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 19 May 2024 19:35:47 +0300 Subject: [PATCH 096/139] removed + from bad chars in filenames Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 70704e1d3..22d1ae60c 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -801,7 +801,7 @@ QString NormalizePath(QString path) } } -static const QString BAD_PATH_CHARS = "\"?<>:;*|!+\r\n"; +static const QString BAD_PATH_CHARS = "\"?<>:;*|!\r\n"; static const QString BAD_FILENAME_CHARS = BAD_PATH_CHARS + "\\/"; QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) From 08918be11eaefd708634bf75ea6f688f0c3f3df9 Mon Sep 17 00:00:00 2001 From: Fourmisain <8464472+Fourmisain@users.noreply.github.com> Date: Mon, 20 May 2024 20:35:05 +0200 Subject: [PATCH 097/139] add detection for IBM Semeru Java runtime Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com> --- launcher/java/JavaUtils.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index e767eff89..ccc20f35c 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -283,6 +283,16 @@ QList JavaUtils::FindJavaPaths() QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + // IBM Semeru + QList SEMERUJRE32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); + QList SEMERUJRE64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); + QList SEMERUJDK32s = + this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); + QList SEMERUJDK64s = + this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); + // Microsoft QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); @@ -300,6 +310,7 @@ QList JavaUtils::FindJavaPaths() java_candidates.append(NEWJRE64s); java_candidates.append(ADOPTOPENJRE64s); java_candidates.append(ADOPTIUMJRE64s); + java_candidates.append(SEMERUJRE64s); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); @@ -308,6 +319,7 @@ QList JavaUtils::FindJavaPaths() java_candidates.append(ADOPTOPENJDK64s); java_candidates.append(FOUNDATIONJDK64s); java_candidates.append(ADOPTIUMJDK64s); + java_candidates.append(SEMERUJDK64s); java_candidates.append(MICROSOFTJDK64s); java_candidates.append(ZULU64s); java_candidates.append(LIBERICA64s); @@ -316,6 +328,7 @@ QList JavaUtils::FindJavaPaths() java_candidates.append(NEWJRE32s); java_candidates.append(ADOPTOPENJRE32s); java_candidates.append(ADOPTIUMJRE32s); + java_candidates.append(SEMERUJRE32s); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); @@ -324,6 +337,7 @@ QList JavaUtils::FindJavaPaths() java_candidates.append(ADOPTOPENJDK32s); java_candidates.append(FOUNDATIONJDK32s); java_candidates.append(ADOPTIUMJDK32s); + java_candidates.append(SEMERUJDK32s); java_candidates.append(ZULU32s); java_candidates.append(LIBERICA32s); From af157c1613c697cd0cbff78abe28d6e958372775 Mon Sep 17 00:00:00 2001 From: Fourmisain <8464472+Fourmisain@users.noreply.github.com> Date: Mon, 20 May 2024 23:13:18 +0200 Subject: [PATCH 098/139] fix x86 detection on 64 bit windows Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com> --- launcher/java/JavaUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index ccc20f35c..f7c7c6e5f 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -207,7 +207,7 @@ QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix; HKEY newKey; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &newKey) == + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) == ERROR_SUCCESS) { // Read the JavaHome value to find where Java is installed. DWORD valueSz = 0; From dbe4adc034a578484a6c4b7ba8d21c7c4d0bbb56 Mon Sep 17 00:00:00 2001 From: Fourmisain <8464472+Fourmisain@users.noreply.github.com> Date: Tue, 21 May 2024 10:51:27 +0200 Subject: [PATCH 099/139] detect IBM Semeru Certified Edition on linux Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com> --- launcher/java/JavaUtils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index f7c7c6e5f..2c9c9a579 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -424,6 +424,7 @@ QList JavaUtils::FindJavaPaths() // manually installed JDKs in /opt scanJavaDirs("/opt/jdk"); scanJavaDirs("/opt/jdks"); + scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition // flatpak scanJavaDirs("/app/jdk"); From 23095b70a4cead08e8b0e38bdf62d4d14482c41d Mon Sep 17 00:00:00 2001 From: Fourmisain <8464472+Fourmisain@users.noreply.github.com> Date: Tue, 21 May 2024 13:40:20 +0200 Subject: [PATCH 100/139] please the pre-commit check Signed-off-by: Fourmisain <8464472+Fourmisain@users.noreply.github.com> --- launcher/java/JavaUtils.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 2c9c9a579..3b32b54c7 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -284,14 +284,10 @@ QList JavaUtils::FindJavaPaths() this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); // IBM Semeru - QList SEMERUJRE32s = - this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); - QList SEMERUJRE64s = - this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); - QList SEMERUJDK32s = - this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); - QList SEMERUJDK64s = - this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); + QList SEMERUJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); + QList SEMERUJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); + QList SEMERUJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); + QList SEMERUJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); // Microsoft QList MICROSOFTJDK64s = @@ -424,7 +420,7 @@ QList JavaUtils::FindJavaPaths() // manually installed JDKs in /opt scanJavaDirs("/opt/jdk"); scanJavaDirs("/opt/jdks"); - scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition + scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition // flatpak scanJavaDirs("/app/jdk"); From 2b949a8059a133d06a626bae8de6bf580825532b Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 22 May 2024 10:53:24 +0300 Subject: [PATCH 101/139] improve login account checking Signed-off-by: Trial97 --- launcher/LaunchController.cpp | 2 +- launcher/minecraft/auth/MinecraftAccount.cpp | 2 -- launcher/minecraft/auth/MinecraftAccount.h | 2 +- launcher/ui/MainWindow.cpp | 23 -------------------- launcher/ui/MainWindow.h | 1 - 5 files changed, 2 insertions(+), 28 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 824564a2d..30e5d215e 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -84,7 +84,7 @@ void LaunchController::decideAccount() // Find an account to use. auto accounts = APPLICATION->accounts(); - if (accounts->count() <= 0) { + if (accounts->count() <= 0 || !accounts->anyAccountIsValid()) { // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), tr("In order to play Minecraft, you must have at least one Microsoft " diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp index eabe9d98d..5b063604c 100644 --- a/launcher/minecraft/auth/MinecraftAccount.cpp +++ b/launcher/minecraft/auth/MinecraftAccount.cpp @@ -83,8 +83,6 @@ MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username) account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]")); - account->data.minecraftEntitlement.ownsMinecraft = true; - account->data.minecraftEntitlement.canPlayMinecraft = true; account->data.minecraftProfile.id = uuidFromUsername(username).toString().remove(QRegularExpression("[{}-]")); account->data.minecraftProfile.name = username; account->data.minecraftProfile.validity = Validity::Certain; diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h index b5c623a26..f6fcfada2 100644 --- a/launcher/minecraft/auth/MinecraftAccount.h +++ b/launcher/minecraft/auth/MinecraftAccount.h @@ -116,7 +116,7 @@ class MinecraftAccount : public QObject, public Usable { [[nodiscard]] AccountType accountType() const noexcept { return data.type; } - bool ownsMinecraft() const { return data.minecraftEntitlement.ownsMinecraft; } + bool ownsMinecraft() const { return data.type != AccountType::Offline && data.minecraftEntitlement.ownsMinecraft; } bool hasProfile() const { return data.profileId().size() != 0; } diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 6bbb10532..bd3aa4865 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -871,29 +871,6 @@ void MainWindow::on_actionCopyInstance_triggered() runModalTask(task.get()); } -void MainWindow::finalizeInstance(InstancePtr inst) -{ - view->updateGeometries(); - setSelectedInstanceById(inst->id()); - if (APPLICATION->accounts()->anyAccountIsValid()) { - ProgressDialog loadDialog(this); - auto update = inst->createUpdateTask(Net::Mode::Online); - connect(update.get(), &Task::failed, [this](QString reason) { - QString error = QString("Instance load failed: %1").arg(reason); - CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); - }); - if (update) { - loadDialog.setSkipButton(true, tr("Abort")); - loadDialog.execWithTask(update.get()); - } - } else { - CustomMessageBox::selectable(this, tr("Error"), - tr("The launcher cannot download Minecraft or update instances unless you have at least " - "one account added.\nPlease add a Microsoft account."), - QMessageBox::Warning) - ->show(); - } -} void MainWindow::addInstance(const QString& url, const QMap& extra_info) { diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index 07a6e1eba..f7fdedd15 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -228,7 +228,6 @@ class MainWindow : public QMainWindow { void runModalTask(Task* task); void instanceFromInstanceTask(InstanceTask* task); - void finalizeInstance(InstancePtr inst); private: Ui::MainWindow* ui; From eacd7758e98cf3d8e0c8dd88cf1999331af0ade7 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 22 May 2024 22:49:28 +0300 Subject: [PATCH 102/139] fix pre-commit hook Signed-off-by: Trial97 --- launcher/ui/MainWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index bd3aa4865..6807a4232 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -871,7 +871,6 @@ void MainWindow::on_actionCopyInstance_triggered() runModalTask(task.get()); } - void MainWindow::addInstance(const QString& url, const QMap& extra_info) { QString groupName; From 3f7847ad34abd71aaab2a730b39e7f27b7bef706 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 28 May 2024 14:47:44 +0300 Subject: [PATCH 103/139] made ResourcePage elements resizable Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- launcher/ui/pages/modplatform/ResourcePage.ui | 110 +++++++++--------- launcher/ui/widgets/ModFilterWidget.ui | 21 ++++ 3 files changed, 77 insertions(+), 56 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index fc70e9f60..8eb605f9e 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -68,7 +68,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) if (m_filter_widget) disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); - m_ui->horizontalLayout->replaceWidget(m_filter_widget == nullptr ? m_ui->filterWidget : m_filter_widget.get(), widget.get()); + m_ui->spliter->replaceWidget(0, widget.get()); m_filter_widget.swap(widget); m_filter = m_filter_widget->getFilter(); diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index b4f48f074..7ef94a5ce 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -11,38 +11,62 @@ + + + + Qt::Vertical + + + + + + + + + + Filter options + + + - - - - - - - - Qt::ScrollBarAlwaysOff - - - true - - - - 48 - 48 - - - - - - - - false - - - false - - - - + + + Qt::Horizontal + + + false + + + + + Qt::ScrollBarAlwaysOff + + + true + + + + 48 + 48 + + + + + + false + + + false + + + + + + + + Search + + @@ -67,33 +91,9 @@ - - - - Qt::Vertical - - - - - - - - - - Search - - - - - - - Filter options - - -
diff --git a/launcher/ui/widgets/ModFilterWidget.ui b/launcher/ui/widgets/ModFilterWidget.ui index 13727167f..236847094 100644 --- a/launcher/ui/widgets/ModFilterWidget.ui +++ b/launcher/ui/widgets/ModFilterWidget.ui @@ -10,6 +10,18 @@ 600 + + + 0 + 0 + + + + + 275 + 0 + + 310 @@ -34,6 +46,15 @@ + + + 275 + 0 + + + + QAbstractScrollArea::AdjustToContentsOnFirstShow + true From 5012178a98b702255b28fe356ab7b2cd88498aba Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 28 May 2024 14:48:03 +0300 Subject: [PATCH 104/139] trigger search on filter change Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- launcher/ui/widgets/ModFilterWidget.cpp | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 8eb605f9e..17b8a93ba 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -73,7 +73,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) m_filter = m_filter_widget->getFilter(); - connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ResourcePage::updateVersionList); + connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ModPage::triggerSearch); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index b44a573d8..d59b4ebe5 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -38,9 +38,12 @@ #include #include #include +#include #include "BaseVersionList.h" +#include "Version.h" #include "meta/Index.h" #include "modplatform/ModIndex.h" +#include "ui/widgets/CheckComboBox.h" #include "ui_ModFilterWidget.h" #include "Application.h" @@ -91,6 +94,7 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi ui->version->setStyleSheet("combobox-popup: 0;"); connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged); connect(ui->versions, QOverload::of(&QComboBox::currentIndexChanged), this, &ModFilterWidget::onVersionFilterChanged); + connect(ui->versions, &CheckComboBox::checkedItemsChanged, this, [this] { onVersionFilterChanged(0); }); connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged); connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged); @@ -184,13 +188,19 @@ void ModFilterWidget::onShowAllVersionsChanged() void ModFilterWidget::onVersionFilterChanged(int) { auto versions = ui->versions->checkedItems(); - m_filter->versions.clear(); + versions.sort(); + std::list current_list; for (const QString& version : versions) - m_filter->versions.emplace_back(version); + current_list.emplace_back(version); - m_filter_changed = true; - emit filterChanged(); + m_filter_changed = m_filter->versions.size() != current_list.size() || + !std::equal(m_filter->versions.begin(), m_filter->versions.end(), current_list.begin(), current_list.end()); + m_filter->versions = current_list; + if (m_filter_changed) + emit filterChanged(); + else + emit filterUnchanged(); } void ModFilterWidget::onLoadersFilterChanged() From f3325030880750eb6b8f3940bb5f3fa1aee791ed Mon Sep 17 00:00:00 2001 From: Alexandru Ionut Tripon Date: Wed, 29 May 2024 00:32:17 +0300 Subject: [PATCH 105/139] Apply suggestions from code review Co-authored-by: Tayou Signed-off-by: Alexandru Ionut Tripon --- launcher/ui/pages/modplatform/ModPage.cpp | 2 +- launcher/ui/pages/modplatform/ResourcePage.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 17b8a93ba..3f87e8207 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -68,7 +68,7 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) if (m_filter_widget) disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); - m_ui->spliter->replaceWidget(0, widget.get()); + m_ui->splitter->replaceWidget(0, widget.get()); m_filter_widget.swap(widget); m_filter = m_filter_widget->getFilter(); diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index 7ef94a5ce..3e666c8c1 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -29,7 +29,7 @@ - + Qt::Horizontal From b256d325f7c38cf038d7fba3478b9c4d0b671b1a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 29 May 2024 01:03:17 +0300 Subject: [PATCH 106/139] fix win combobox Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index f8411dd79..644f94795 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -133,12 +133,12 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) QComboBox::hidePopup(); return (keyEvent->key() != Qt::Key_Escape); } + break; } case QEvent::MouseButtonPress: - containerMousePress = (receiver == view()->window()); - break; + /* fallthrough */ case QEvent::MouseButtonRelease: - containerMousePress = false; + containerMousePress = (receiver == view()->window()); break; case QEvent::Wheel: return receiver == this; From 660772d4d7dda6db8cb0d744c3bd96a758570573 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 29 May 2024 01:06:11 +0300 Subject: [PATCH 107/139] swap buttons Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ResourcePage.ui | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index 3e666c8c1..c42325f65 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -11,23 +11,19 @@ - - - - Qt::Vertical - - - - - + + - Filter options + Search + + + @@ -61,13 +57,6 @@ - - - - Search - - - @@ -91,8 +80,12 @@ - - + + + + Filter options + + @@ -110,7 +103,6 @@
- searchEdit searchButton packView packDescription From 29444cfe0fb9e19397646701d059e02e06874862 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 29 May 2024 17:19:15 +0300 Subject: [PATCH 108/139] fix win combobox take#2 Signed-off-by: Trial97 --- launcher/ui/widgets/CheckComboBox.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/launcher/ui/widgets/CheckComboBox.cpp b/launcher/ui/widgets/CheckComboBox.cpp index 644f94795..41def3ba1 100644 --- a/launcher/ui/widgets/CheckComboBox.cpp +++ b/launcher/ui/widgets/CheckComboBox.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -91,7 +92,7 @@ void CheckComboBox::setSourceModel(QAbstractItemModel* new_model) void CheckComboBox::hidePopup() { - if (containerMousePress) + if (!containerMousePress) QComboBox::hidePopup(); } @@ -135,11 +136,11 @@ bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event) } break; } - case QEvent::MouseButtonPress: - /* fallthrough */ - case QEvent::MouseButtonRelease: - containerMousePress = (receiver == view()->window()); + case QEvent::MouseButtonPress: { + auto ev = static_cast(event); + containerMousePress = ev && view()->indexAt(ev->pos()).isValid(); break; + } case QEvent::Wheel: return receiver == this; default: From b8fe93efa35ffcecf41554f38eda1bf486272407 Mon Sep 17 00:00:00 2001 From: Tayou Date: Wed, 29 May 2024 19:52:58 +0200 Subject: [PATCH 109/139] fix & clean ResourcePage.ui Qt is cursed. Signed-off-by: Tayou --- launcher/ui/pages/modplatform/ResourcePage.ui | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index c42325f65..964437cc7 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -11,23 +11,38 @@ - - - - - - - Search + + + + false - - + + + + + + Filter options + + + + + + + + + + Search + + + + - + - Qt::Horizontal + Qt::Orientation::Horizontal false @@ -35,7 +50,7 @@ - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff true @@ -57,7 +72,7 @@ - + @@ -68,7 +83,7 @@ Version selected: - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -80,13 +95,6 @@ - - - - Filter options - - - From 469f051e30994e92902986aebd1d58bcf52d596f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 00:14:37 +0300 Subject: [PATCH 110/139] remove search button Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ModPage.cpp | 12 +++---- .../ui/pages/modplatform/ResourcePackPage.cpp | 1 - .../ui/pages/modplatform/ResourcePage.cpp | 9 ++++- launcher/ui/pages/modplatform/ResourcePage.h | 2 +- launcher/ui/pages/modplatform/ResourcePage.ui | 36 ++++++++----------- .../ui/pages/modplatform/ShaderPackPage.cpp | 1 - .../ui/pages/modplatform/TexturePackPage.h | 1 - launcher/ui/widgets/ModFilterWidget.cpp | 9 ----- launcher/ui/widgets/ModFilterWidget.h | 1 - 9 files changed, 29 insertions(+), 43 deletions(-) diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index 3f87e8207..c9817cdf7 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -58,7 +58,6 @@ namespace ResourceDownload { ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { - connect(m_ui->searchButton, &QPushButton::clicked, this, &ModPage::triggerSearch); connect(m_ui->resourceFilterButton, &QPushButton::clicked, this, &ModPage::filterMods); connect(m_ui->packView, &QListView::doubleClicked, this, &ModPage::onResourceSelected); } @@ -68,16 +67,17 @@ void ModPage::setFilterWidget(unique_qobject_ptr& widget) if (m_filter_widget) disconnect(m_filter_widget.get(), nullptr, nullptr, nullptr); - m_ui->splitter->replaceWidget(0, widget.get()); + auto old = m_ui->splitter->replaceWidget(0, widget.get()); + // because we replaced the widget we also need to delete it + if (old) { + delete old; + } + m_filter_widget.swap(widget); m_filter = m_filter_widget->getFilter(); connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, &ModPage::triggerSearch); - connect(m_filter_widget.get(), &ModFilterWidget::filterChanged, this, - [&] { m_ui->searchButton->setStyleSheet("text-decoration: underline"); }); - connect(m_filter_widget.get(), &ModFilterWidget::filterUnchanged, this, - [&] { m_ui->searchButton->setStyleSheet("text-decoration: none"); }); prepareProviderCategories(); } diff --git a/launcher/ui/pages/modplatform/ResourcePackPage.cpp b/launcher/ui/pages/modplatform/ResourcePackPage.cpp index fc2dc15f3..849ea1111 100644 --- a/launcher/ui/pages/modplatform/ResourcePackPage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePackPage.cpp @@ -15,7 +15,6 @@ namespace ResourceDownload { ResourcePackResourcePage::ResourcePackResourcePage(ResourceDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { - connect(m_ui->searchButton, &QPushButton::clicked, this, &ResourcePackResourcePage::triggerSearch); connect(m_ui->packView, &QListView::doubleClicked, this, &ResourcePackResourcePage::onResourceSelected); } diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 9c2c8990a..713feb254 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -67,11 +67,18 @@ ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_in connect(&m_search_timer, &QTimer::timeout, this, &ResourcePage::triggerSearch); + // hide both progress bars to prevent weird artifact + m_ui->fetchProgress->hide(); + m_fetch_progress.hide(); m_fetch_progress.hideIfInactive(true); m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - layout()->replaceWidget(m_ui->fetchProgress, &m_fetch_progress); + auto old = layout()->replaceWidget(m_ui->fetchProgress, &m_fetch_progress); + // because we replaced the widget we also need to delete it + if (old) { + delete old; + } m_ui->packView->setItemDelegate(new ProjectItemDelegate(this)); m_ui->packView->installEventFilter(this); diff --git a/launcher/ui/pages/modplatform/ResourcePage.h b/launcher/ui/pages/modplatform/ResourcePage.h index 6cc117320..938e549f6 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.h +++ b/launcher/ui/pages/modplatform/ResourcePage.h @@ -84,7 +84,7 @@ class ResourcePage : public QWidget, public BasePage { bool hasSelectedPacks() { return !(m_model->selectedPacks().isEmpty()); } protected slots: - virtual void triggerSearch() {} + virtual void triggerSearch() = 0; void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index 964437cc7..202ba1ceb 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -10,15 +10,8 @@ 685 - - - - - false - - - - + + @@ -30,19 +23,19 @@ - - - - Search - - - - + + + + false + + + + - Qt::Orientation::Horizontal + Qt::Horizontal false @@ -50,7 +43,7 @@ - Qt::ScrollBarPolicy::ScrollBarAlwaysOff + Qt::ScrollBarAlwaysOff true @@ -72,7 +65,7 @@ - + @@ -83,7 +76,7 @@ Version selected: - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -111,7 +104,6 @@ - searchButton packView packDescription sortByBox diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp index 8be068312..ebd8d4ea2 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -16,7 +16,6 @@ namespace ResourceDownload { ShaderPackResourcePage::ShaderPackResourcePage(ShaderPackDownloadDialog* dialog, BaseInstance& instance) : ResourcePage(dialog, instance) { - connect(m_ui->searchButton, &QPushButton::clicked, this, &ShaderPackResourcePage::triggerSearch); connect(m_ui->packView, &QListView::doubleClicked, this, &ShaderPackResourcePage::onResourceSelected); } diff --git a/launcher/ui/pages/modplatform/TexturePackPage.h b/launcher/ui/pages/modplatform/TexturePackPage.h index 948e5286b..42aa921c5 100644 --- a/launcher/ui/pages/modplatform/TexturePackPage.h +++ b/launcher/ui/pages/modplatform/TexturePackPage.h @@ -41,7 +41,6 @@ class TexturePackResourcePage : public ResourcePackResourcePage { protected: TexturePackResourcePage(TexturePackDownloadDialog* dialog, BaseInstance& instance) : ResourcePackResourcePage(dialog, instance) { - connect(m_ui->searchButton, &QPushButton::clicked, this, &TexturePackResourcePage::triggerSearch); connect(m_ui->packView, &QListView::doubleClicked, this, &TexturePackResourcePage::onResourceSelected); } }; diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index d59b4ebe5..6503c684f 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -122,7 +122,6 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended, QWi auto ModFilterWidget::getFilter() -> std::shared_ptr { m_filter_changed = false; - emit filterUnchanged(); return m_filter; } @@ -199,8 +198,6 @@ void ModFilterWidget::onVersionFilterChanged(int) m_filter->versions = current_list; if (m_filter_changed) emit filterChanged(); - else - emit filterUnchanged(); } void ModFilterWidget::onLoadersFilterChanged() @@ -218,8 +215,6 @@ void ModFilterWidget::onLoadersFilterChanged() m_filter->loaders = loaders; if (m_filter_changed) emit filterChanged(); - else - emit filterUnchanged(); } void ModFilterWidget::onSideFilterChanged() @@ -240,8 +235,6 @@ void ModFilterWidget::onSideFilterChanged() m_filter->side = side; if (m_filter_changed) emit filterChanged(); - else - emit filterUnchanged(); } void ModFilterWidget::onHideInstalledFilterChanged() @@ -251,8 +244,6 @@ void ModFilterWidget::onHideInstalledFilterChanged() m_filter->hideInstalled = hide; if (m_filter_changed) emit filterChanged(); - else - emit filterUnchanged(); } void ModFilterWidget::onVersionFilterTextChanged(const QString& version) diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 4358dd91d..fdfd2c8bb 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -81,7 +81,6 @@ class ModFilterWidget : public QTabWidget { signals: void filterChanged(); - void filterUnchanged(); public slots: void setCategories(const QList&); From 5e36def18be96fe70fa807d987bc714070e035dc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 00:36:02 +0300 Subject: [PATCH 111/139] remove search button Signed-off-by: Trial97 --- .../pages/modplatform/atlauncher/AtlPage.ui | 7 ----- .../ui/pages/modplatform/flame/FlamePage.cpp | 1 - .../ui/pages/modplatform/flame/FlamePage.ui | 21 ++++----------- .../modplatform/import_ftb/ImportFTBPage.ui | 7 ----- .../ui/pages/modplatform/legacy_ftb/Page.ui | 7 ----- .../modplatform/modrinth/ModrinthPage.cpp | 3 +-- .../modplatform/modrinth/ModrinthPage.ui | 27 +++++-------------- .../pages/modplatform/technic/TechnicPage.cpp | 1 - .../pages/modplatform/technic/TechnicPage.ui | 7 ----- 9 files changed, 13 insertions(+), 68 deletions(-) diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui index 8b6747331..c8b9aa8c0 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui @@ -68,13 +68,6 @@ - - - - Search - - - diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index f1fd9b5d8..8c2bcefaa 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -55,7 +55,6 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::FlamePage), dialog(dialog), m_fetch_progress(this, false) { ui->setupUi(this); - connect(ui->searchButton, &QPushButton::clicked, this, &FlamePage::triggerSearch); ui->searchEdit->installEventFilter(this); listModel = new Flame::ListModel(this); ui->packView->setModel(listModel); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.ui b/launcher/ui/pages/modplatform/flame/FlamePage.ui index f9e1fe67f..a8d88050a 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.ui +++ b/launcher/ui/pages/modplatform/flame/FlamePage.ui @@ -30,22 +30,11 @@ - - - - - Search and filter... - - - - - - - Search - - - - + + + Search and filter... + + diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui index 6613a5939..1b626745a 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui @@ -60,13 +60,6 @@ - - - - Search - - - diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.ui b/launcher/ui/pages/modplatform/legacy_ftb/Page.ui index 56cba7485..ae8b2ea5d 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.ui +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.ui @@ -23,13 +23,6 @@ - - - - Search - - - diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index da5fe1e7b..94bf8715c 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -58,7 +58,6 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) { ui->setupUi(this); - connect(ui->searchButton, &QPushButton::clicked, this, &ModrinthPage::triggerSearch); ui->searchEdit->installEventFilter(this); m_model = new Modrinth::ModpackListModel(this); ui->packView->setModel(m_model); @@ -75,7 +74,7 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - ui->gridLayout->addWidget(&m_fetch_progress, 2, 0, 1, ui->gridLayout->columnCount()); + ui->gridLayout->addWidget(&m_fetch_progress, 1, 0, 1, ui->gridLayout->columnCount()); ui->sortByBox->addItem(tr("Sort by Relevance")); ui->sortByBox->addItem(tr("Sort by Total Downloads")); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 68b1d4e24..16d2b3835 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -11,6 +11,13 @@ + + + + Search and filter ... + + + @@ -61,24 +68,6 @@ - - - - - - Search and filter ... - - - - - - - Search - - - - - @@ -89,8 +78,6 @@ - searchEdit - searchButton packView packDescription sortByBox diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 6b1ec8cb5..a6bce9ac2 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -57,7 +57,6 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog), m_fetch_progress(this, false) { ui->setupUi(this); - connect(ui->searchButton, &QPushButton::clicked, this, &TechnicPage::triggerSearch); ui->searchEdit->installEventFilter(this); model = new Technic::ListModel(this); ui->packView->setModel(model); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.ui b/launcher/ui/pages/modplatform/technic/TechnicPage.ui index b988eda2b..1e825ff7a 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.ui +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.ui @@ -75,13 +75,6 @@ - - - - Search - - - From 7fed5e9173efa297aa20f308fcedb57a13c1870d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 08:51:36 +0300 Subject: [PATCH 112/139] Capitalize the mod categories Signed-off-by: Trial97 --- launcher/ui/widgets/ModFilterWidget.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 6503c684f..bbb91eac2 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -262,7 +262,14 @@ void ModFilterWidget::setCategories(const QList& categori auto layout = new QVBoxLayout(ui->categoryGroup); for (const auto& category : categories) { - auto checkbox = new QCheckBox(category.name); + auto name = category.name; + name.replace("-", " "); + name.replace("&", "&&"); + auto checkbox = new QCheckBox(name); + auto font = checkbox->font(); + font.setCapitalization(QFont::Capitalize); + checkbox->setFont(font); + layout->addWidget(checkbox); const QString id = category.id; From 8704cd84021049f5999cbc2d875be51a528719ee Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 11:49:09 +0300 Subject: [PATCH 113/139] remove duplicate proggress bar Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/ResourcePage.cpp | 9 ++------- launcher/ui/pages/modplatform/ResourcePage.ui | 13 ------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index 713feb254..4f49cc4f4 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -67,18 +67,13 @@ ResourcePage::ResourcePage(ResourceDownloadDialog* parent, BaseInstance& base_in connect(&m_search_timer, &QTimer::timeout, this, &ResourcePage::triggerSearch); - // hide both progress bars to prevent weird artifact - m_ui->fetchProgress->hide(); + // hide progress bar to prevent weird artifact m_fetch_progress.hide(); m_fetch_progress.hideIfInactive(true); m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - auto old = layout()->replaceWidget(m_ui->fetchProgress, &m_fetch_progress); - // because we replaced the widget we also need to delete it - if (old) { - delete old; - } + m_ui->verticalLayout->insertWidget(1, &m_fetch_progress); m_ui->packView->setItemDelegate(new ProjectItemDelegate(this)); m_ui->packView->installEventFilter(this); diff --git a/launcher/ui/pages/modplatform/ResourcePage.ui b/launcher/ui/pages/modplatform/ResourcePage.ui index 202ba1ceb..491e7d9f0 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.ui +++ b/launcher/ui/pages/modplatform/ResourcePage.ui @@ -25,13 +25,6 @@ - - - - false - - - @@ -96,12 +89,6 @@ QTextBrowser
ui/widgets/ProjectDescriptionPage.h
- - ProgressWidget - QWidget -
ui/widgets/ProgressWidget.h
- 1 -
packView From a7a1b2876569704e842d942976ce39061ee5e899 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 13:49:23 +0300 Subject: [PATCH 114/139] improve invalid path name Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 89 +++++++++++++++++-- launcher/MMCZip.cpp | 2 - launcher/minecraft/Library.cpp | 1 + .../flame/FlameInstanceCreationTask.cpp | 2 - launcher/modplatform/flame/FlameModIndex.cpp | 2 - .../modrinth/ModrinthInstanceCreationTask.cpp | 2 - .../modrinth/ModrinthPackIndex.cpp | 2 - launcher/net/HttpMetaCache.cpp | 2 - 8 files changed, 82 insertions(+), 20 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 22d1ae60c..72df9368a 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -74,6 +74,7 @@ #include #include #else +#include #include #endif @@ -801,25 +802,97 @@ QString NormalizePath(QString path) } } -static const QString BAD_PATH_CHARS = "\"?<>:;*|!\r\n"; -static const QString BAD_FILENAME_CHARS = BAD_PATH_CHARS + "\\/"; +QString removeDuplicates(QString a) +{ + auto b = a.split(""); + b.removeDuplicates(); + return b.join(""); +} + +QString getFileSystemType(const QString& path) +{ + QString fileSystemType; + +#ifdef Q_OS_WIN + wchar_t volume[MAX_PATH + 1] = { 0 }; + if (GetVolumeInformationW((LPCWSTR)path.utf16(), nullptr, 0, nullptr, nullptr, nullptr, volume, MAX_PATH)) { + fileSystemType = QString::fromWCharArray(volume); + } +#elif defined(Q_OS_UNIX) + struct statvfs buf; + if (statvfs(path.toUtf8().constData(), &buf) == 0) { + switch (buf.f_type) { + case 0x4d44: // "MSDOS" + fileSystemType = "FAT32"; + break; + case 0x5346544e: // "NTFS" + fileSystemType = "NTFS"; + break; + case 0x4244: // "HFS+" or "H+" on some systems + case 0x482b: // "HFS+" or "H+" on some systems + fileSystemType = "HFS+"; + break; + case 0x41465342: // "APFS" + fileSystemType = "APFS"; + break; + case 0x65735546: // "exFAT" + fileSystemType = "exFAT"; + break; + default: + break; + } + } +#endif + + return fileSystemType; +} + +static const QString BAD_WIN_CHARS = "\"?<>:*|\r\n"; + +static const QString BAD_FAT32_CHARS = "<>:\"|?*+.,;=[]!"; +static const QString BAD_NTFS_CHARS = "<>:\"|?*"; +static const QString BAD_HFS_CHARS = ":"; +static const QString BAD_EXFAT_CHARS = "<>:\"|?*"; + +static const QString BAD_FILENAME_CHARS = + removeDuplicates(BAD_WIN_CHARS + BAD_FAT32_CHARS + BAD_NTFS_CHARS + BAD_HFS_CHARS + BAD_EXFAT_CHARS) + "\\/"; QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) { for (int i = 0; i < string.length(); i++) if (string.at(i) < ' ' || BAD_FILENAME_CHARS.contains(string.at(i))) string[i] = replaceWith; - return string; } -QString RemoveInvalidPathChars(QString string, QChar replaceWith) +QString RemoveInvalidPathChars(QString path, QChar replaceWith) { - for (int i = 0; i < string.length(); i++) - if (string.at(i) < ' ' || BAD_PATH_CHARS.contains(string.at(i))) - string[i] = replaceWith; + QString invalidChars; +#ifdef Q_OS_WIN + invalidChars = BAD_WIN_CHARS; +#endif - return string; + QString fileSystemType = getFileSystemType(QFileInfo(path).absolutePath()); + + if (fileSystemType == "FAT32") { + invalidChars += BAD_FAT32_CHARS; + } else if (fileSystemType == "NTFS") { + invalidChars += BAD_NTFS_CHARS; + } else if (fileSystemType == "HFS+" || fileSystemType == "APFS") { + invalidChars += BAD_HFS_CHARS; + } else if (fileSystemType == "exFAT") { + invalidChars += BAD_EXFAT_CHARS; + } + + if (invalidChars.size() != 0) { + for (int i = 0; i < path.length(); i++) { + if (path.at(i) < ' ' || invalidChars.contains(path.at(i))) { + path[i] = replaceWith; + } + } + } + + return path; } QString DirNameFromString(QString string, QString inDir) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 9a5ae7a9d..9acde90e1 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -288,9 +288,7 @@ std::optional extractSubDir(QuaZip* zip, const QString& subdir, con do { QString file_name = zip->getCurrentFileName(); -#ifdef Q_OS_WIN file_name = FS::RemoveInvalidPathChars(file_name); -#endif if (!file_name.startsWith(subdir)) continue; diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp index 2c3f2035f..4e30f72d1 100644 --- a/launcher/minecraft/Library.cpp +++ b/launcher/minecraft/Library.cpp @@ -51,6 +51,7 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext, { bool local = isLocal(); auto actualPath = [&](QString relPath) { + relPath = FS::RemoveInvalidPathChars(relPath); QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); if (local && !overridePath.isEmpty()) { QString fileName = out.fileName(); diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index a1f10c156..003203879 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -538,9 +538,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) } for (const auto& result : results) { auto fileName = result.fileName; -#ifdef Q_OS_WIN fileName = FS::RemoveInvalidPathChars(fileName); -#endif auto relpath = FS::PathCombine(result.targetFolder, fileName); if (!result.required && !selectedOptionalMods.contains(relpath)) { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 83a28fa2b..1ca9237f4 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -139,9 +139,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> file.version = Json::requireString(obj, "displayName"); file.downloadUrl = Json::ensureString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); -#ifdef Q_OS_WIN file.fileName = FS::RemoveInvalidPathChars(file.fileName); -#endif ModPlatform::IndexedVersionType::VersionType ver_type; switch (Json::requireInteger(obj, "releaseType")) { diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index e92489999..cbb8e8e67 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -241,9 +241,7 @@ bool ModrinthCreationTask::createInstance() for (auto file : m_files) { auto fileName = file.path; -#ifdef Q_OS_WIN fileName = FS::RemoveInvalidPathChars(fileName); -#endif auto file_path = FS::PathCombine(root_modpack_path, fileName); if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) { // This means we somehow got out of the root folder, so abort here to prevent exploits diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 4671a330d..6c3df0902 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -227,9 +227,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t if (parent.contains("url")) { file.downloadUrl = Json::requireString(parent, "url"); file.fileName = Json::requireString(parent, "filename"); -#ifdef Q_OS_WIN file.fileName = FS::RemoveInvalidPathChars(file.fileName); -#endif file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1); auto hash_list = Json::requireObject(parent, "hashes"); diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 648155412..d8b1c7636 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -84,9 +84,7 @@ auto HttpMetaCache::getEntry(QString base, QString resource_path) -> MetaEntryPt auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr { -#ifdef Q_OS_WIN resource_path = FS::RemoveInvalidPathChars(resource_path); -#endif auto entry = getEntry(base, resource_path); // it's not present? generate a default stale entry if (!entry) { From 44a16c1ca1c340d3f7641fa12b0ffe59cdf92992 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 14:20:35 +0300 Subject: [PATCH 115/139] fix CI Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 84 +++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 53 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 72df9368a..3ded638ae 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -74,7 +74,6 @@ #include #include #else -#include #include #endif @@ -809,53 +808,13 @@ QString removeDuplicates(QString a) return b.join(""); } -QString getFileSystemType(const QString& path) -{ - QString fileSystemType; - -#ifdef Q_OS_WIN - wchar_t volume[MAX_PATH + 1] = { 0 }; - if (GetVolumeInformationW((LPCWSTR)path.utf16(), nullptr, 0, nullptr, nullptr, nullptr, volume, MAX_PATH)) { - fileSystemType = QString::fromWCharArray(volume); - } -#elif defined(Q_OS_UNIX) - struct statvfs buf; - if (statvfs(path.toUtf8().constData(), &buf) == 0) { - switch (buf.f_type) { - case 0x4d44: // "MSDOS" - fileSystemType = "FAT32"; - break; - case 0x5346544e: // "NTFS" - fileSystemType = "NTFS"; - break; - case 0x4244: // "HFS+" or "H+" on some systems - case 0x482b: // "HFS+" or "H+" on some systems - fileSystemType = "HFS+"; - break; - case 0x41465342: // "APFS" - fileSystemType = "APFS"; - break; - case 0x65735546: // "exFAT" - fileSystemType = "exFAT"; - break; - default: - break; - } - } -#endif - - return fileSystemType; -} - static const QString BAD_WIN_CHARS = "\"?<>:*|\r\n"; -static const QString BAD_FAT32_CHARS = "<>:\"|?*+.,;=[]!"; +static const QString BAD_FAT_CHARS = "<>:\"|?*+.,;=[]!"; static const QString BAD_NTFS_CHARS = "<>:\"|?*"; static const QString BAD_HFS_CHARS = ":"; -static const QString BAD_EXFAT_CHARS = "<>:\"|?*"; -static const QString BAD_FILENAME_CHARS = - removeDuplicates(BAD_WIN_CHARS + BAD_FAT32_CHARS + BAD_NTFS_CHARS + BAD_HFS_CHARS + BAD_EXFAT_CHARS) + "\\/"; +static const QString BAD_FILENAME_CHARS = removeDuplicates(BAD_WIN_CHARS + BAD_FAT_CHARS + BAD_NTFS_CHARS + BAD_HFS_CHARS) + "\\/"; QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) { @@ -872,16 +831,35 @@ QString RemoveInvalidPathChars(QString path, QChar replaceWith) invalidChars = BAD_WIN_CHARS; #endif - QString fileSystemType = getFileSystemType(QFileInfo(path).absolutePath()); - - if (fileSystemType == "FAT32") { - invalidChars += BAD_FAT32_CHARS; - } else if (fileSystemType == "NTFS") { - invalidChars += BAD_NTFS_CHARS; - } else if (fileSystemType == "HFS+" || fileSystemType == "APFS") { - invalidChars += BAD_HFS_CHARS; - } else if (fileSystemType == "exFAT") { - invalidChars += BAD_EXFAT_CHARS; + switch (statFS(path).fsType) { + case FilesystemType::FAT: + invalidChars += BAD_FAT_CHARS; + break; + case FilesystemType::NTFS: + invalidChars += BAD_NTFS_CHARS; + break; + // case FilesystemType::REFS: + // case FilesystemType::EXT: + // case FilesystemType::EXT_2_OLD: + // case FilesystemType::EXT_2_3_4: + // case FilesystemType::XFS: + // case FilesystemType::BTRFS: + // case FilesystemType::NFS: + // case FilesystemType::ZFS: + case FilesystemType::APFS: + /* fallthrough */ + case FilesystemType::HFS: + /* fallthrough */ + case FilesystemType::HFSPLUS: + /* fallthrough */ + case FilesystemType::HFSX: + invalidChars += BAD_HFS_CHARS; + break; + // case FilesystemType::FUSEBLK: + // case FilesystemType::F2FS: + // case FilesystemType::UNKNOWN: + default: + break; } if (invalidChars.size() != 0) { From eaccdca02deb536a670a063902e666a2a341751f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 16:21:08 +0300 Subject: [PATCH 116/139] added error message for import skin from user Signed-off-by: Trial97 --- launcher/net/NetJob.cpp | 33 +++++++++++-------- launcher/net/NetJob.h | 2 ++ .../ui/dialogs/skins/SkinManageDialog.cpp | 13 ++++++-- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/launcher/net/NetJob.cpp b/launcher/net/NetJob.cpp index a6b1207c0..a331a9769 100644 --- a/launcher/net/NetJob.cpp +++ b/launcher/net/NetJob.cpp @@ -148,21 +148,28 @@ void NetJob::updateState() void NetJob::emitFailed(QString reason) { #if defined(LAUNCHER_APPLICATION) - auto response = CustomMessageBox::selectable(nullptr, "Confirm retry", - "The tasks failed\n" - "Failed urls\n" + - getFailedFiles().join("\n\t") + - "\n" - "If this continues to happen please check the logs of the application" - "Do you want to retry?", - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + if (m_ask_retry) { + auto response = CustomMessageBox::selectable(nullptr, "Confirm retry", + "The tasks failed.\n" + "Failed urls\n" + + getFailedFiles().join("\n\t") + + ".\n" + "If this continues to happen please check the logs of the application.\n" + "Do you want to retry?", + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); - if (response == QMessageBox::Yes) { - m_try = 0; - executeNextSubTask(); - return; + if (response == QMessageBox::Yes) { + m_try = 0; + executeNextSubTask(); + return; + } } #endif ConcurrentTask::emitFailed(reason); +} + +void NetJob::setAskRetry(bool askRetry) +{ + m_ask_retry = askRetry; } \ No newline at end of file diff --git a/launcher/net/NetJob.h b/launcher/net/NetJob.h index a50d7f91e..af09f03ba 100644 --- a/launcher/net/NetJob.h +++ b/launcher/net/NetJob.h @@ -62,6 +62,7 @@ class NetJob : public ConcurrentTask { auto getFailedActions() -> QList; auto getFailedFiles() -> QList; + void setAskRetry(bool askRetry); public slots: // Qt can't handle auto at the start for some reason? @@ -78,4 +79,5 @@ class NetJob : public ConcurrentTask { shared_qobject_ptr m_network; int m_try = 1; + bool m_ask_retry = true; }; diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 0b60e4248..19a1f6d52 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -345,9 +345,9 @@ void SkinManageDialog::on_urlBtn_clicked() CustomMessageBox::selectable(this, tr("Invalid url"), tr("Invalid url"), QMessageBox::Critical)->show(); return; } - ui->urlLine->setText(""); NetJob::Ptr job{ new NetJob(tr("Download skin"), APPLICATION->network()) }; + job->setAskRetry(false); auto path = FS::PathCombine(m_list.getDir(), url.fileName()); job->addNetAction(Net::Download::makeFile(url, path)); @@ -361,6 +361,7 @@ void SkinManageDialog::on_urlBtn_clicked() QFile::remove(path); return; } + ui->urlLine->setText(""); if (QFileInfo(path).suffix().isEmpty()) { QFile::rename(path, path + ".png"); } @@ -397,11 +398,11 @@ void SkinManageDialog::on_userBtn_clicked() if (user.isEmpty()) { return; } - ui->urlLine->setText(""); MinecraftProfile mcProfile; auto path = FS::PathCombine(m_list.getDir(), user + ".png"); NetJob::Ptr job{ new NetJob(tr("Download user skin"), APPLICATION->network(), 1) }; + job->setAskRetry(false); auto uuidOut = std::make_shared(); auto profileOut = std::make_shared(); @@ -459,6 +460,14 @@ void SkinManageDialog::on_userBtn_clicked() dlg.execWithTask(job.get()); SkinModel s(path); + if (!s.isValid()) { + CustomMessageBox::selectable(this, tr("Usename not found"), tr("Unable to find the skin for '%1'.").arg(user), + QMessageBox::Critical) + ->show(); + QFile::remove(path); + return; + } + ui->urlLine->setText(""); s.setModel(mcProfile.skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setURL(mcProfile.skin.url); if (m_capes.contains(mcProfile.currentCape)) { From d1286bbe909bb51b74ab266ed190682d38e29776 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 30 May 2024 21:54:32 +0300 Subject: [PATCH 117/139] add some comments Signed-off-by: Trial97 --- launcher/FileSystem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 3ded638ae..7a34e8a5d 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -831,14 +831,16 @@ QString RemoveInvalidPathChars(QString path, QChar replaceWith) invalidChars = BAD_WIN_CHARS; #endif + // the null character is ignored in this check as it was not a problem until now switch (statFS(path).fsType) { case FilesystemType::FAT: invalidChars += BAD_FAT_CHARS; break; case FilesystemType::NTFS: + /* fallthrough */ + case FilesystemType::REFS: // similar to NTFS(should be available only on windows) invalidChars += BAD_NTFS_CHARS; break; - // case FilesystemType::REFS: // case FilesystemType::EXT: // case FilesystemType::EXT_2_OLD: // case FilesystemType::EXT_2_3_4: From 2635d7c3be79f888d7a0bdfafda8024432b6fa98 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 31 May 2024 09:07:51 +0300 Subject: [PATCH 118/139] replaced grid with vertical layout on all add modpack pages Signed-off-by: Trial97 --- .../pages/modplatform/atlauncher/AtlPage.ui | 118 +++++++++--------- .../ui/pages/modplatform/flame/FlamePage.cpp | 2 +- .../ui/pages/modplatform/flame/FlamePage.ui | 10 +- .../modplatform/import_ftb/ImportFTBPage.ui | 86 ++++++------- .../ui/pages/modplatform/legacy_ftb/Page.ui | 38 +++--- .../modplatform/modrinth/ModrinthPage.cpp | 2 +- .../modplatform/modrinth/ModrinthPage.ui | 8 +- .../pages/modplatform/technic/TechnicPage.cpp | 2 +- .../pages/modplatform/technic/TechnicPage.ui | 78 ++++++------ 9 files changed, 172 insertions(+), 172 deletions(-) diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui index c8b9aa8c0..0b1411b96 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.ui @@ -10,65 +10,8 @@ 685
- - - - - - - - - - Version selected: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - - true - - - true - - - - - - - true - - - - 96 - 48 - - - - - - - - - - Search and filter... - - - true - - - - + + @@ -86,6 +29,63 @@ + + + + Search and filter... + + + true + + + + + + + + + true + + + + 96 + 48 + + + + + + + + true + + + true + + + + + + + + + + + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + +
diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 8c2bcefaa..dbb5fb8bc 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -71,7 +71,7 @@ FlamePage::FlamePage(NewInstanceDialog* dialog, QWidget* parent) m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - ui->gridLayout->addWidget(&m_fetch_progress, 2, 0, 1, ui->gridLayout->columnCount()); + ui->verticalLayout->insertWidget(2, &m_fetch_progress); // index is used to set the sorting with the curseforge api ui->sortByBox->addItem(tr("Sort by Featured")); diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.ui b/launcher/ui/pages/modplatform/flame/FlamePage.ui index a8d88050a..d4ddb37a4 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.ui +++ b/launcher/ui/pages/modplatform/flame/FlamePage.ui @@ -10,8 +10,8 @@ 600 - - + + @@ -29,14 +29,14 @@ - + Search and filter... - + @@ -66,7 +66,7 @@ - + diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui index 1b626745a..18c604ca4 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.ui @@ -10,45 +10,18 @@ 1011 - - - - - - 16777215 - 16777215 - + + + + + Note: If your FTB instances are not in the default location, select it using the button next to search. + + + Qt::AlignCenter - - - - - - - 265 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + @@ -79,16 +52,43 @@ - - - - Note: If your FTB instances are not in the default location, select it using the button next to search. - - - Qt::AlignCenter + + + + + 16777215 + 16777215 + + + + + + + + 265 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.ui b/launcher/ui/pages/modplatform/legacy_ftb/Page.ui index ae8b2ea5d..544ad77d3 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.ui +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.ui @@ -10,8 +10,8 @@ 602 - - + + @@ -25,7 +25,7 @@ - + 0 @@ -127,22 +127,9 @@ - - - - - - Version selected: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + + + @@ -152,6 +139,19 @@ + + + + Version selected: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 94bf8715c..e76e5bf70 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -74,7 +74,7 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - ui->gridLayout->addWidget(&m_fetch_progress, 1, 0, 1, ui->gridLayout->columnCount()); + ui->verticalLayout->insertWidget(1, &m_fetch_progress); ui->sortByBox->addItem(tr("Sort by Relevance")); ui->sortByBox->addItem(tr("Sort by Total Downloads")); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui index 16d2b3835..7f4f903f6 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui @@ -10,15 +10,15 @@ 600 - - + + Search and filter ... - + @@ -48,7 +48,7 @@ - + diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index a6bce9ac2..43f006a6b 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -70,7 +70,7 @@ TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget* parent) m_fetch_progress.setFixedHeight(24); m_fetch_progress.progressFormat(""); - ui->gridLayout->addWidget(&m_fetch_progress, 2, 0, 1, ui->gridLayout->columnCount()); + ui->verticalLayout->insertWidget(1, &m_fetch_progress); connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TechnicPage::onSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &TechnicPage::onVersionSelectionChanged); diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.ui b/launcher/ui/pages/modplatform/technic/TechnicPage.ui index 1e825ff7a..f4e75ae12 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.ui +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.ui @@ -10,23 +10,41 @@ 405 - - - - - - - - - - Version selected: + + + + + Search and filter... + + + + + + + + + true - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 48 + 48 + - + + + + true + + + + + + + + Qt::Horizontal @@ -42,39 +60,21 @@ - - - - - - - - true + + + + Version selected: - - - 48 - 48 - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - true - - + + - - - - Search and filter... - - - From f4f0a61bf23d5c40fa4310c050e4c39e6a8ce746 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 26 May 2024 09:59:38 +0300 Subject: [PATCH 119/139] fix vanilla technic modpacks Signed-off-by: Trial97 --- launcher/modplatform/technic/TechnicPackProcessor.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/technic/TechnicPackProcessor.cpp b/launcher/modplatform/technic/TechnicPackProcessor.cpp index a47a4811f..9050e14d8 100644 --- a/launcher/modplatform/technic/TechnicPackProcessor.cpp +++ b/launcher/modplatform/technic/TechnicPackProcessor.cpp @@ -83,8 +83,10 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, data = file.readAll(); file.close(); } else { - if (minecraftVersion.isEmpty()) + if (minecraftVersion.isEmpty()) { emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown")); + return; + } components->setComponentVersion("net.minecraft", minecraftVersion, true); components->installJarMods({ modpackJar }); @@ -131,7 +133,9 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, file.close(); } else { // This is the "Vanilla" modpack, excluded by the search code - emit failed(tr("Unable to find a \"version.json\"!")); + components->setComponentVersion("net.minecraft", minecraftVersion, true); + components->saveNow(); + emit succeeded(); return; } From f7120a4f6ff64e8e28096a51f5d99dd2565471bc Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 8 May 2024 14:53:49 +0300 Subject: [PATCH 120/139] Made Custom New Instance scrollable Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/CustomPage.ui | 464 ++++++++++---------- 1 file changed, 241 insertions(+), 223 deletions(-) diff --git a/launcher/ui/pages/modplatform/CustomPage.ui b/launcher/ui/pages/modplatform/CustomPage.ui index fda3e8a2e..ce558f2cf 100644 --- a/launcher/ui/pages/modplatform/CustomPage.ui +++ b/launcher/ui/pages/modplatform/CustomPage.ui @@ -33,231 +33,250 @@ - - - - - 0 - 0 - - - - Qt::Horizontal + + + + true + + + + 0 + 0 + 791 + 551 + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + + + + + + + Filter + + + Qt::AlignCenter + + + + + + + Releases + + + true + + + true + + + + + + + Snapshots + + + true + + + + + + + Betas + + + true + + + + + + + Alphas + + + true + + + + + + + Experiments + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Refresh + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + Mod Loader + + + Qt::AlignCenter + + + + + + + None + + + true + + + loaderBtnGroup + + + + + + + NeoForge + + + loaderBtnGroup + + + + + + + Forge + + + loaderBtnGroup + + + + + + + Fabric + + + loaderBtnGroup + + + + + + + Quilt + + + loaderBtnGroup + + + + + + + LiteLoader + + + loaderBtnGroup + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Refresh + + + + + + + + + - - - - - - - 0 - 0 - - - - - - - - - - Filter - - - Qt::AlignCenter - - - - - - - Releases - - - true - - - true - - - - - - - Snapshots - - - true - - - - - - - Betas - - - true - - - - - - - Alphas - - - true - - - - - - - Experiments - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Refresh - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - Mod Loader - - - Qt::AlignCenter - - - - - - - None - - - true - - - loaderBtnGroup - - - - - - - NeoForge - - - loaderBtnGroup - - - - - - - Forge - - - loaderBtnGroup - - - - - - - Fabric - - - loaderBtnGroup - - - - - - - Quilt - - - loaderBtnGroup - - - - - - - LiteLoader - - - loaderBtnGroup - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Refresh - - - - - - - @@ -273,7 +292,6 @@ - tabWidget releaseFilter snapshotFilter betaFilter From c9c39b8203bbfaf2205847e80a979baaf6c007a7 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 31 May 2024 17:47:39 +0300 Subject: [PATCH 121/139] fix double borders Signed-off-by: Trial97 --- launcher/ui/pages/modplatform/CustomPage.cpp | 1 - launcher/ui/pages/modplatform/CustomPage.ui | 472 +++++++++---------- 2 files changed, 229 insertions(+), 244 deletions(-) diff --git a/launcher/ui/pages/modplatform/CustomPage.cpp b/launcher/ui/pages/modplatform/CustomPage.cpp index d2b73008d..ba22bd2e6 100644 --- a/launcher/ui/pages/modplatform/CustomPage.cpp +++ b/launcher/ui/pages/modplatform/CustomPage.cpp @@ -49,7 +49,6 @@ CustomPage::CustomPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::CustomPage) { ui->setupUi(this); - ui->tabWidget->tabBar()->hide(); connect(ui->versionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedVersion); filterChanged(); connect(ui->alphaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged); diff --git a/launcher/ui/pages/modplatform/CustomPage.ui b/launcher/ui/pages/modplatform/CustomPage.ui index ce558f2cf..39d9aa6dc 100644 --- a/launcher/ui/pages/modplatform/CustomPage.ui +++ b/launcher/ui/pages/modplatform/CustomPage.ui @@ -24,259 +24,245 @@ 0 - - - 0 + + + true - - - - - - - - - true - - - - - 0 - 0 - 791 - 551 - - - - - - - - 0 - 0 - + + + + 0 + 0 + 813 + 605 + + + + + + + + + + 0 + 0 + + + + + + + + + + Filter - - Qt::Horizontal + + Qt::AlignCenter - - - - - - - 0 - 0 - - - - - - - - - - Filter - - - Qt::AlignCenter - - - - - - - Releases - - - true - - - true - - - - - - - Snapshots - - - true - - - - - - - Betas - - - true - - - - - - - Alphas - - - true - - - - - - - Experiments - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Refresh - - - - - - + + + + Releases + + + true + + + true + + - - - - - - - 0 - 0 - - - - - - - - - - Mod Loader - - - Qt::AlignCenter - - - - - - - None - - - true - - - loaderBtnGroup - - - - - - - NeoForge - - - loaderBtnGroup - - - - - - - Forge - - - loaderBtnGroup - - - - - - - Fabric - - - loaderBtnGroup - - - - - - - Quilt - - - loaderBtnGroup - - - - - - - LiteLoader - - - loaderBtnGroup - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Refresh - - - - - - + + + + Snapshots + + + true + + + + + + + Betas + + + true + + + + + + + Alphas + + + true + + + + + + + Experiments + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Refresh + + - + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + + Mod Loader + + + Qt::AlignCenter + + + + + + + None + + + true + + + loaderBtnGroup + + + + + + + NeoForge + + + loaderBtnGroup + + + + + + + Forge + + + loaderBtnGroup + + + + + + + Fabric + + + loaderBtnGroup + + + + + + + Quilt + + + loaderBtnGroup + + + + + + + LiteLoader + + + loaderBtnGroup + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Refresh + + + + + + + From f3509181869a789cb413f6704e276f58e847dcb0 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 31 May 2024 19:03:40 +0300 Subject: [PATCH 122/139] force initial size for the Add Instance dialog Signed-off-by: Trial97 --- launcher/ui/dialogs/NewInstanceDialog.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 3524d43f8..0fb8e320d 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ #include "ui/pages/modplatform/modrinth/ModrinthPage.h" #include "ui/pages/modplatform/technic/TechnicPage.h" #include "ui/widgets/PageContainer.h" + NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, const QMap& extra_info, @@ -127,7 +129,13 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, updateDialogState(); - restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray())); + if (APPLICATION->settings()->get("NewInstanceGeometry").isValid()) { + restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray())); + } else { + auto screen = parent->screen(); + auto geometry = screen->availableSize(); + resize(width(), qMin(geometry.height() - 50, 710)); + } } void NewInstanceDialog::reject() From 83883cadf9dd4e71db257821e0c46ef6421021eb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 31 May 2024 19:30:10 +0300 Subject: [PATCH 123/139] fix qt5 build Signed-off-by: Trial97 --- launcher/ui/dialogs/NewInstanceDialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp index 0fb8e320d..1601708f9 100644 --- a/launcher/ui/dialogs/NewInstanceDialog.cpp +++ b/launcher/ui/dialogs/NewInstanceDialog.cpp @@ -132,7 +132,11 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, if (APPLICATION->settings()->get("NewInstanceGeometry").isValid()) { restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("NewInstanceGeometry").toByteArray())); } else { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) auto screen = parent->screen(); +#else + auto screen = QGuiApplication::primaryScreen(); +#endif auto geometry = screen->availableSize(); resize(width(), qMin(geometry.height() - 50, 710)); } From bbff31bcc069f4ee6718a551f1cb087db54f7e56 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 2 Jun 2024 00:20:40 +0000 Subject: [PATCH 124/139] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/8dc45382d5206bd292f9c2768b8058a8fd8311d9?narHash=sha256-/GJvTdTpuDjNn84j82cU6bXztE0MSkdnTWClUCRub78%3D' (2024-05-16) → 'github:hercules-ci/flake-parts/2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8?narHash=sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw%3D' (2024-06-01) • Updated input 'nixpkgs': 'github:nixos/nixpkgs/47e03a624662ce399e55c45a5f6da698fc72c797?narHash=sha256-9dUxZf8MOqJH3vjbhrz7LH4qTcnRsPSBU1Q50T7q/X8%3D' (2024-05-25) → 'github:nixos/nixpkgs/6132b0f6e344ce2fe34fc051b72fb46e34f668e0?narHash=sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY%3D' (2024-05-30) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 6f7f9a0c5..e7bcc1635 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1715865404, - "narHash": "sha256-/GJvTdTpuDjNn84j82cU6bXztE0MSkdnTWClUCRub78=", + "lastModified": 1717285511, + "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "8dc45382d5206bd292f9c2768b8058a8fd8311d9", + "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8", "type": "github" }, "original": { @@ -75,11 +75,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716619601, - "narHash": "sha256-9dUxZf8MOqJH3vjbhrz7LH4qTcnRsPSBU1Q50T7q/X8=", + "lastModified": 1717112898, + "narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "47e03a624662ce399e55c45a5f6da698fc72c797", + "rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0", "type": "github" }, "original": { From b7008d2880091db64587362a3bb72fb51541be5f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 6 Jun 2024 00:09:08 +0300 Subject: [PATCH 125/139] replace auth server ping Signed-off-by: Trial97 --- buildconfig/BuildConfig.h | 1 - launcher/LaunchController.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 77b6eef54..bda80ac72 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -163,7 +163,6 @@ class Config { QString RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/"; - QString AUTH_BASE = "https://authserver.mojang.com/"; 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 diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index cf8d0a794..e37b2f6e3 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -315,7 +315,7 @@ void LaunchController::launchInstance() online_mode = "online"; // Prepend Server Status - QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; + QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; QString resolved_servers = ""; QHostInfo host_info; From 448ded2387025b11d122d51b0abd907bec0452aa Mon Sep 17 00:00:00 2001 From: coolguy1842 Date: Thu, 6 Jun 2024 16:18:47 +1000 Subject: [PATCH 126/139] simple fix for resizing too small causing an index out of range crash Signed-off-by: coolguy1842 --- launcher/ui/instanceview/VisualGroup.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/launcher/ui/instanceview/VisualGroup.cpp b/launcher/ui/instanceview/VisualGroup.cpp index 7bff727fe..e3bf628dc 100644 --- a/launcher/ui/instanceview/VisualGroup.cpp +++ b/launcher/ui/instanceview/VisualGroup.cpp @@ -66,6 +66,9 @@ void VisualGroup::update() rows[currentRow].height = maxRowHeight; rows[currentRow].top = offsetFromTop; currentRow++; + if(currentRow >= rows.size()) { + currentRow = rows.size() - 1; + } offsetFromTop += maxRowHeight + 5; positionInRow = 0; maxRowHeight = 0; From 71b2c4b1fd0a45e2325d09ca93b0f441ee1b7a50 Mon Sep 17 00:00:00 2001 From: coolguy1842 Date: Thu, 6 Jun 2024 16:32:41 +1000 Subject: [PATCH 127/139] fix formatting Signed-off-by: coolguy1842 --- launcher/ui/instanceview/VisualGroup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/instanceview/VisualGroup.cpp b/launcher/ui/instanceview/VisualGroup.cpp index e3bf628dc..83103c502 100644 --- a/launcher/ui/instanceview/VisualGroup.cpp +++ b/launcher/ui/instanceview/VisualGroup.cpp @@ -66,7 +66,7 @@ void VisualGroup::update() rows[currentRow].height = maxRowHeight; rows[currentRow].top = offsetFromTop; currentRow++; - if(currentRow >= rows.size()) { + if (currentRow >= rows.size()) { currentRow = rows.size() - 1; } offsetFromTop += maxRowHeight + 5; From b096ee6a0c8b3fb4e60b26d9460e4ff5ae684d4f Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 6 Jun 2024 14:12:55 +0300 Subject: [PATCH 129/139] fix file permisions Signed-off-by: Trial97 --- launcher/MMCZip.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 0b9dd16f5..a32c1f5d3 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -342,7 +342,7 @@ std::optional extractSubDir(QuaZip* zip, const QString& subdir, con auto newPermisions = (permissions & maxPermisions) | minPermisions; if (newPermisions != permissions) { - if (!QFile::setPermissions(target_file_path, permissions)) { + if (!QFile::setPermissions(target_file_path, newPermisions)) { qWarning() << (QObject::tr("Could not fix permissions for %1").arg(target_file_path)); } } @@ -608,7 +608,7 @@ auto ExtractZipTask::extractZip() -> ZipResult auto newPermisions = (permissions & maxPermisions) | minPermisions; if (newPermisions != permissions) { - if (!QFile::setPermissions(target_file_path, permissions)) { + if (!QFile::setPermissions(target_file_path, newPermisions)) { logWarning(tr("Could not fix permissions for %1").arg(target_file_path)); } } From 24cf671370e3d3710cdc37773bbae193ae8925b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 9 Jun 2024 00:22:00 +0000 Subject: [PATCH 130/139] chore(nix): update lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:nixos/nixpkgs/6132b0f6e344ce2fe34fc051b72fb46e34f668e0?narHash=sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY%3D' (2024-05-30) → 'github:nixos/nixpkgs/d226935fd75012939397c83f6c385e4d6d832288?narHash=sha256-HV97wqUQv9wvptiHCb3Y0/YH0lJ60uZ8FYfEOIzYEqI%3D' (2024-06-07) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/0e8fcc54b842ad8428c9e705cb5994eaf05c26a0?narHash=sha256-xrsYFST8ij4QWaV6HEokCUNIZLjjLP1bYC60K8XiBVA%3D' (2024-05-20) → 'github:cachix/pre-commit-hooks.nix/cc4d466cb1254af050ff7bdf47f6d404a7c646d1?narHash=sha256-7XfBuLULizXjXfBYy/VV%2BSpYMHreNRHk9nKMsm1bgb4%3D' (2024-06-06) --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index e7bcc1635..810bedea9 100644 --- a/flake.lock +++ b/flake.lock @@ -75,11 +75,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1717112898, - "narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=", + "lastModified": 1717774105, + "narHash": "sha256-HV97wqUQv9wvptiHCb3Y0/YH0lJ60uZ8FYfEOIzYEqI=", "owner": "nixos", "repo": "nixpkgs", - "rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0", + "rev": "d226935fd75012939397c83f6c385e4d6d832288", "type": "github" }, "original": { @@ -103,11 +103,11 @@ ] }, "locked": { - "lastModified": 1716213921, - "narHash": "sha256-xrsYFST8ij4QWaV6HEokCUNIZLjjLP1bYC60K8XiBVA=", + "lastModified": 1717664902, + "narHash": "sha256-7XfBuLULizXjXfBYy/VV+SpYMHreNRHk9nKMsm1bgb4=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "0e8fcc54b842ad8428c9e705cb5994eaf05c26a0", + "rev": "cc4d466cb1254af050ff7bdf47f6d404a7c646d1", "type": "github" }, "original": { From 04b2ac2efa7ccd4a83b88e6305744c39683c72b9 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 9 Jun 2024 20:48:49 +0300 Subject: [PATCH 131/139] update fail url skin messagebox Signed-off-by: Trial97 --- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 19a1f6d52..0ec11c59d 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -355,7 +355,9 @@ void SkinManageDialog::on_urlBtn_clicked() dlg.execWithTask(job.get()); SkinModel s(path); if (!s.isValid()) { - CustomMessageBox::selectable(this, tr("URL is not a valid skin"), tr("Skin images must be 64x64 or 64x32 pixel PNG files."), + CustomMessageBox::selectable(this, tr("URL is not a valid skin"), + QFileInfo::exists(path) ? tr("Skin images must be 64x64 or 64x32 pixel PNG files.") + : tr("Unable to download the skin: '%1'.").arg(ui->urlLine->text()), QMessageBox::Critical) ->show(); QFile::remove(path); From 1e0db46728d9532d2803801d425454ebf2b4cc71 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 9 Jun 2024 21:29:56 +0300 Subject: [PATCH 132/139] add a more verbose error message in case user skin import fails Signed-off-by: Trial97 --- .../ui/dialogs/skins/SkinManageDialog.cpp | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 0ec11c59d..eb22102ee 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -416,18 +416,33 @@ void SkinManageDialog::on_userBtn_clicked() auto getProfile = Net::Download::makeByteArray(QUrl(), profileOut); auto downloadSkin = Net::Download::makeFile(QUrl(), path); + QString failReason; + connect(getUUID.get(), &Task::aborted, uuidLoop.get(), &WaitTask::quit); + connect(getUUID.get(), &Task::failed, this, [&failReason](QString reason) { + qCritical() << "Couldn't get user UUID:" << reason; + failReason = tr("failed to get user UUID"); + }); connect(getUUID.get(), &Task::failed, uuidLoop.get(), &WaitTask::quit); connect(getProfile.get(), &Task::aborted, profileLoop.get(), &WaitTask::quit); connect(getProfile.get(), &Task::failed, profileLoop.get(), &WaitTask::quit); + connect(getProfile.get(), &Task::failed, this, [&failReason](QString reason) { + qCritical() << "Couldn't get user profile:" << reason; + failReason = tr("failed to get user profile"); + }); + connect(downloadSkin.get(), &Task::failed, this, [&failReason](QString reason) { + qCritical() << "Couldn't download skin:" << reason; + failReason = tr("failed to download skin"); + }); - connect(getUUID.get(), &Task::succeeded, this, [uuidLoop, uuidOut, job, getProfile] { + connect(getUUID.get(), &Task::succeeded, this, [uuidLoop, uuidOut, job, getProfile, &failReason] { try { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*uuidOut, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Minecraft skin service at " << parse_error.offset << " reason: " << parse_error.errorString(); + failReason = tr("failed to parse get user UUID response"); uuidLoop->quit(); return; } @@ -436,18 +451,21 @@ void SkinManageDialog::on_userBtn_clicked() if (!id.isEmpty()) { getProfile->setUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + id); } else { + failReason = tr("user id is empty"); job->abort(); } } catch (const Exception& e) { qCritical() << "Couldn't load skin json:" << e.cause(); + failReason = tr("failed to parse get user UUID response"); } uuidLoop->quit(); }); - connect(getProfile.get(), &Task::succeeded, this, [profileLoop, profileOut, job, getProfile, &mcProfile, downloadSkin] { + connect(getProfile.get(), &Task::succeeded, this, [profileLoop, profileOut, job, getProfile, &mcProfile, downloadSkin, &failReason] { if (Parsers::parseMinecraftProfileMojang(*profileOut, mcProfile)) { downloadSkin->setUrl(mcProfile.skin.url); } else { + failReason = tr("failed to parse get user profile response"); job->abort(); } profileLoop->quit(); @@ -463,8 +481,11 @@ void SkinManageDialog::on_userBtn_clicked() SkinModel s(path); if (!s.isValid()) { - CustomMessageBox::selectable(this, tr("Usename not found"), tr("Unable to find the skin for '%1'.").arg(user), - QMessageBox::Critical) + if (failReason.isEmpty()) { + failReason = tr("the skin is invalid"); + } + CustomMessageBox::selectable(this, tr("Usename not found"), + tr("Unable to find the skin for '%1'\n because: %2.").arg(user, failReason), QMessageBox::Critical) ->show(); QFile::remove(path); return; From 242ddbb7e11d2f9681199d1023ce3f1ea9a0b841 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sun, 9 Jun 2024 23:53:40 +0300 Subject: [PATCH 133/139] fix size column sorting Signed-off-by: Trial97 --- launcher/minecraft/mod/DataPack.cpp | 17 ++++------ launcher/minecraft/mod/DataPack.h | 2 +- launcher/minecraft/mod/Mod.cpp | 24 +++++--------- launcher/minecraft/mod/Mod.h | 2 +- launcher/minecraft/mod/Resource.cpp | 31 +++++++++++-------- launcher/minecraft/mod/Resource.h | 7 +++-- .../minecraft/mod/ResourceFolderModel.cpp | 8 ++--- launcher/minecraft/mod/ResourcePack.cpp | 17 ++++------ launcher/minecraft/mod/ResourcePack.h | 2 +- 9 files changed, 48 insertions(+), 62 deletions(-) diff --git a/launcher/minecraft/mod/DataPack.cpp b/launcher/minecraft/mod/DataPack.cpp index fc2d3f68b..4ef13d10d 100644 --- a/launcher/minecraft/mod/DataPack.cpp +++ b/launcher/minecraft/mod/DataPack.cpp @@ -65,29 +65,24 @@ std::pair DataPack::compatibleVersions() const return s_pack_format_versions.constFind(m_pack_format).value(); } -std::pair DataPack::compare(const Resource& other, SortType type) const +int DataPack::compare(const Resource& other, SortType type, Qt::SortOrder order) const { auto const& cast_other = static_cast(other); - switch (type) { - default: { - auto res = Resource::compare(other, type); - if (res.first != 0) - return res; - break; - } + default: + return Resource::compare(other, type, order); case SortType::PACK_FORMAT: { auto this_ver = packFormat(); auto other_ver = cast_other.packFormat(); if (this_ver > other_ver) - return { 1, type == SortType::PACK_FORMAT }; + return 1; if (this_ver < other_ver) - return { -1, type == SortType::PACK_FORMAT }; + return -1; break; } } - return { 0, false }; + return 0; } bool DataPack::applyFilter(QRegularExpression filter) const diff --git a/launcher/minecraft/mod/DataPack.h b/launcher/minecraft/mod/DataPack.h index b3787b238..3bc4888da 100644 --- a/launcher/minecraft/mod/DataPack.h +++ b/launcher/minecraft/mod/DataPack.h @@ -56,7 +56,7 @@ class DataPack : public Resource { bool valid() const override; - [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair override; + [[nodiscard]] int compare(Resource const& other, SortType type, Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; protected: diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 8653cf822..af033c051 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -78,41 +78,33 @@ void Mod::setDetails(const ModDetails& details) m_local_details = details; } -std::pair Mod::compare(const Resource& other, SortType type) const +int Mod::compare(const Resource& other, SortType type, Qt::SortOrder order) const { auto cast_other = dynamic_cast(&other); if (!cast_other) - return Resource::compare(other, type); + return Resource::compare(other, type, order); switch (type) { default: case SortType::ENABLED: case SortType::NAME: case SortType::DATE: - case SortType::SIZE: { - auto res = Resource::compare(other, type); - if (res.first != 0) - return res; - break; - } + case SortType::SIZE: + return Resource::compare(other, type, order); case SortType::VERSION: { auto this_ver = Version(version()); auto other_ver = Version(cast_other->version()); if (this_ver > other_ver) - return { 1, type == SortType::VERSION }; + return 1; if (this_ver < other_ver) - return { -1, type == SortType::VERSION }; + return -1; break; } case SortType::PROVIDER: { - auto compare_result = - QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); - if (compare_result != 0) - return { compare_result, type == SortType::PROVIDER }; - break; + return QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); } } - return { 0, false }; + return 0; } bool Mod::applyFilter(QRegularExpression filter) const diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index e97ee9d3b..ac7fd1be9 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -88,7 +88,7 @@ class Mod : public Resource { bool valid() const override; - [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair override; + [[nodiscard]] int compare(Resource const& other, SortType type, Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; // Delete all the files of this mod diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 2fe7e87d4..68594d741 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -30,7 +30,7 @@ std::tuple calculateFileSize(const QFileInfo& file) auto str = QObject::tr("item"); if (count != 1) str = QObject::tr("items"); - return { QString("%1 %2").arg(QString::number(count), str), -count }; + return { QString("%1 %2").arg(QString::number(count), str), count }; } return { StringUtils::humanReadableFileSize(file.size(), true), file.size() }; } @@ -79,15 +79,15 @@ static void removeThePrefix(QString& string) string = string.trimmed(); } -std::pair Resource::compare(const Resource& other, SortType type) const +int Resource::compare(const Resource& other, SortType type, Qt::SortOrder order) const { switch (type) { default: case SortType::ENABLED: if (enabled() && !other.enabled()) - return { 1, type == SortType::ENABLED }; + return 1; if (!enabled() && other.enabled()) - return { -1, type == SortType::ENABLED }; + return -1; break; case SortType::NAME: { QString this_name{ name() }; @@ -96,27 +96,32 @@ std::pair Resource::compare(const Resource& other, SortType type) con removeThePrefix(this_name); removeThePrefix(other_name); - auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive); - if (compare_result != 0) - return { compare_result, type == SortType::NAME }; - break; + return QString::compare(this_name, other_name, Qt::CaseInsensitive); } case SortType::DATE: if (dateTimeChanged() > other.dateTimeChanged()) - return { 1, type == SortType::DATE }; + return 1; if (dateTimeChanged() < other.dateTimeChanged()) - return { -1, type == SortType::DATE }; + return -1; break; case SortType::SIZE: { + auto order_op = order == Qt::SortOrder::AscendingOrder ? 1 : -1; + if (this->type() != other.type()) { + if (this->type() == ResourceType::FOLDER) + return -1 * order_op; + if (other.type() == ResourceType::FOLDER) + return 1 * order_op; + } + if (sizeInfo() > other.sizeInfo()) - return { 1, type == SortType::SIZE }; + return 1; if (sizeInfo() < other.sizeInfo()) - return { -1, type == SortType::SIZE }; + return -1; break; } } - return { 0, false }; + return 0; } bool Resource::applyFilter(QRegularExpression filter) const diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 74f4d006e..19f0e352b 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -55,10 +55,11 @@ class Resource : public QObject { * > 0: 'this' comes after 'other' * = 0: 'this' is equal to 'other' * < 0: 'this' comes before 'other' - * - * The second argument in the pair is true if the sorting type that decided which one is greater was 'type'. + * the order is used to force items to be allways at top and not used for sorting */ - [[nodiscard]] virtual auto compare(Resource const& other, SortType type = SortType::NAME) const -> std::pair; + [[nodiscard]] virtual int compare(Resource const& other, + SortType type = SortType::NAME, + Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const; /** Returns whether the given filter should filter out 'this' (false), * or if such filter includes the Resource (true). diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 37d92fa26..dde9a67b1 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -615,13 +615,11 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const auto const& resource_left = model->at(source_left.row()); auto const& resource_right = model->at(source_right.row()); - auto compare_result = resource_left.compare(resource_right, column_sort_key); - if (compare_result.first == 0) + auto compare_result = resource_left.compare(resource_right, column_sort_key, sortOrder()); + if (compare_result == 0) return QSortFilterProxyModel::lessThan(source_left, source_right); - if (compare_result.second || sortOrder() != Qt::DescendingOrder) - return (compare_result.first < 0); - return (compare_result.first > 0); + return compare_result < 0; } QString ResourceFolderModel::instDirPath() const diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index 074534405..ac4e1adaa 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -94,29 +94,24 @@ std::pair ResourcePack::compatibleVersions() const return s_pack_format_versions.constFind(m_pack_format).value(); } -std::pair ResourcePack::compare(const Resource& other, SortType type) const +int ResourcePack::compare(const Resource& other, SortType type, Qt::SortOrder order) const { auto const& cast_other = static_cast(other); - switch (type) { - default: { - auto res = Resource::compare(other, type); - if (res.first != 0) - return res; - break; - } + default: + return Resource::compare(other, type, order); case SortType::PACK_FORMAT: { auto this_ver = packFormat(); auto other_ver = cast_other.packFormat(); if (this_ver > other_ver) - return { 1, type == SortType::PACK_FORMAT }; + return 1; if (this_ver < other_ver) - return { -1, type == SortType::PACK_FORMAT }; + return -1; break; } } - return { 0, false }; + return 0; } bool ResourcePack::applyFilter(QRegularExpression filter) const diff --git a/launcher/minecraft/mod/ResourcePack.h b/launcher/minecraft/mod/ResourcePack.h index c06f3793d..b14623d6b 100644 --- a/launcher/minecraft/mod/ResourcePack.h +++ b/launcher/minecraft/mod/ResourcePack.h @@ -44,7 +44,7 @@ class ResourcePack : public Resource { bool valid() const override; - [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair override; + [[nodiscard]] int compare(Resource const& other, SortType type, Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; protected: From 29d32f0d9ae30562b20afef6af52ed883dc17a48 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 10 Jun 2024 00:18:00 +0300 Subject: [PATCH 134/139] Update size order for folders Signed-off-by: Trial97 --- launcher/minecraft/mod/DataPack.cpp | 4 ++-- launcher/minecraft/mod/DataPack.h | 2 +- launcher/minecraft/mod/Mod.cpp | 6 +++--- launcher/minecraft/mod/Mod.h | 2 +- launcher/minecraft/mod/Resource.cpp | 7 +++---- launcher/minecraft/mod/Resource.h | 5 +---- launcher/minecraft/mod/ResourceFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourcePack.cpp | 4 ++-- launcher/minecraft/mod/ResourcePack.h | 2 +- 9 files changed, 15 insertions(+), 19 deletions(-) diff --git a/launcher/minecraft/mod/DataPack.cpp b/launcher/minecraft/mod/DataPack.cpp index 4ef13d10d..ee4d4f1a6 100644 --- a/launcher/minecraft/mod/DataPack.cpp +++ b/launcher/minecraft/mod/DataPack.cpp @@ -65,12 +65,12 @@ std::pair DataPack::compatibleVersions() const return s_pack_format_versions.constFind(m_pack_format).value(); } -int DataPack::compare(const Resource& other, SortType type, Qt::SortOrder order) const +int DataPack::compare(const Resource& other, SortType type) const { auto const& cast_other = static_cast(other); switch (type) { default: - return Resource::compare(other, type, order); + return Resource::compare(other, type); case SortType::PACK_FORMAT: { auto this_ver = packFormat(); auto other_ver = cast_other.packFormat(); diff --git a/launcher/minecraft/mod/DataPack.h b/launcher/minecraft/mod/DataPack.h index 3bc4888da..4855b0203 100644 --- a/launcher/minecraft/mod/DataPack.h +++ b/launcher/minecraft/mod/DataPack.h @@ -56,7 +56,7 @@ class DataPack : public Resource { bool valid() const override; - [[nodiscard]] int compare(Resource const& other, SortType type, Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const override; + [[nodiscard]] int compare(Resource const& other, SortType type) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; protected: diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index af033c051..e92079055 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -78,11 +78,11 @@ void Mod::setDetails(const ModDetails& details) m_local_details = details; } -int Mod::compare(const Resource& other, SortType type, Qt::SortOrder order) const +int Mod::compare(const Resource& other, SortType type) const { auto cast_other = dynamic_cast(&other); if (!cast_other) - return Resource::compare(other, type, order); + return Resource::compare(other, type); switch (type) { default: @@ -90,7 +90,7 @@ int Mod::compare(const Resource& other, SortType type, Qt::SortOrder order) cons case SortType::NAME: case SortType::DATE: case SortType::SIZE: - return Resource::compare(other, type, order); + return Resource::compare(other, type); case SortType::VERSION: { auto this_ver = Version(version()); auto other_ver = Version(cast_other->version()); diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index ac7fd1be9..a0c68688d 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -88,7 +88,7 @@ class Mod : public Resource { bool valid() const override; - [[nodiscard]] int compare(Resource const& other, SortType type, Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const override; + [[nodiscard]] int compare(Resource const& other, SortType type) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; // Delete all the files of this mod diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 68594d741..863b0165f 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -79,7 +79,7 @@ static void removeThePrefix(QString& string) string = string.trimmed(); } -int Resource::compare(const Resource& other, SortType type, Qt::SortOrder order) const +int Resource::compare(const Resource& other, SortType type) const { switch (type) { default: @@ -105,12 +105,11 @@ int Resource::compare(const Resource& other, SortType type, Qt::SortOrder order) return -1; break; case SortType::SIZE: { - auto order_op = order == Qt::SortOrder::AscendingOrder ? 1 : -1; if (this->type() != other.type()) { if (this->type() == ResourceType::FOLDER) - return -1 * order_op; + return -1; if (other.type() == ResourceType::FOLDER) - return 1 * order_op; + return 1; } if (sizeInfo() > other.sizeInfo()) diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 19f0e352b..222f07afa 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -55,11 +55,8 @@ class Resource : public QObject { * > 0: 'this' comes after 'other' * = 0: 'this' is equal to 'other' * < 0: 'this' comes before 'other' - * the order is used to force items to be allways at top and not used for sorting */ - [[nodiscard]] virtual int compare(Resource const& other, - SortType type = SortType::NAME, - Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const; + [[nodiscard]] virtual int compare(Resource const& other, SortType type = SortType::NAME) const; /** Returns whether the given filter should filter out 'this' (false), * or if such filter includes the Resource (true). diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index dde9a67b1..254342ee1 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -615,7 +615,7 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const auto const& resource_left = model->at(source_left.row()); auto const& resource_right = model->at(source_right.row()); - auto compare_result = resource_left.compare(resource_right, column_sort_key, sortOrder()); + auto compare_result = resource_left.compare(resource_right, column_sort_key); if (compare_result == 0) return QSortFilterProxyModel::lessThan(source_left, source_right); diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp index ac4e1adaa..5b26503f5 100644 --- a/launcher/minecraft/mod/ResourcePack.cpp +++ b/launcher/minecraft/mod/ResourcePack.cpp @@ -94,12 +94,12 @@ std::pair ResourcePack::compatibleVersions() const return s_pack_format_versions.constFind(m_pack_format).value(); } -int ResourcePack::compare(const Resource& other, SortType type, Qt::SortOrder order) const +int ResourcePack::compare(const Resource& other, SortType type) const { auto const& cast_other = static_cast(other); switch (type) { default: - return Resource::compare(other, type, order); + return Resource::compare(other, type); case SortType::PACK_FORMAT: { auto this_ver = packFormat(); auto other_ver = cast_other.packFormat(); diff --git a/launcher/minecraft/mod/ResourcePack.h b/launcher/minecraft/mod/ResourcePack.h index b14623d6b..2254fc5c4 100644 --- a/launcher/minecraft/mod/ResourcePack.h +++ b/launcher/minecraft/mod/ResourcePack.h @@ -44,7 +44,7 @@ class ResourcePack : public Resource { bool valid() const override; - [[nodiscard]] int compare(Resource const& other, SortType type, Qt::SortOrder order = Qt::SortOrder::AscendingOrder) const override; + [[nodiscard]] int compare(Resource const& other, SortType type) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; protected: From 1c1e296960783896358d8db51d9e30819f1466ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=AB=E4=B9=90=E7=9A=84=E8=80=81=E9=BC=A0=E5=AE=9D?= =?UTF-8?q?=E5=AE=9D?= Date: Mon, 10 Jun 2024 17:15:38 +0800 Subject: [PATCH 135/139] fix(SkinManageDialog.cpp): typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 快乐的老鼠宝宝 --- launcher/ui/dialogs/skins/SkinManageDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index eb22102ee..d676ac38c 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -484,7 +484,7 @@ void SkinManageDialog::on_userBtn_clicked() if (failReason.isEmpty()) { failReason = tr("the skin is invalid"); } - CustomMessageBox::selectable(this, tr("Usename not found"), + CustomMessageBox::selectable(this, tr("Username not found"), tr("Unable to find the skin for '%1'\n because: %2.").arg(user, failReason), QMessageBox::Critical) ->show(); QFile::remove(path); @@ -497,4 +497,4 @@ void SkinManageDialog::on_userBtn_clicked() s.setCapeId(mcProfile.currentCape); } m_list.updateSkin(&s); -} \ No newline at end of file +} From c1c182fd0c9051a202a0cc7c1d471b9de6997697 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Mon, 10 Jun 2024 12:38:59 +0300 Subject: [PATCH 136/139] update imgur upload Signed-off-by: Trial97 --- launcher/screenshots/ImgurAlbumCreation.cpp | 4 ++-- launcher/screenshots/ImgurUpload.cpp | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/launcher/screenshots/ImgurAlbumCreation.cpp b/launcher/screenshots/ImgurAlbumCreation.cpp index 7e42ff40c..c63c8b39b 100644 --- a/launcher/screenshots/ImgurAlbumCreation.cpp +++ b/launcher/screenshots/ImgurAlbumCreation.cpp @@ -51,7 +51,7 @@ Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr output, QList screenshots) { auto up = makeShared(); - up->m_url = BuildConfig.IMGUR_BASE_URL + "album.json"; + up->m_url = BuildConfig.IMGUR_BASE_URL + "album"; up->m_sink.reset(new Sink(output)); up->m_screenshots = screenshots; return up; @@ -72,7 +72,7 @@ void ImgurAlbumCreation::init() qDebug() << "Setting up imgur upload"; auto api_headers = new Net::StaticHeaderProxy( QList{ { "Content-Type", "application/x-www-form-urlencoded" }, - { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() }, + { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } }); addHeaderProxy(api_headers); } diff --git a/launcher/screenshots/ImgurUpload.cpp b/launcher/screenshots/ImgurUpload.cpp index 15fb043e4..941b92ce6 100644 --- a/launcher/screenshots/ImgurUpload.cpp +++ b/launcher/screenshots/ImgurUpload.cpp @@ -50,9 +50,8 @@ void ImgurUpload::init() { qDebug() << "Setting up imgur upload"; - auto api_headers = new Net::StaticHeaderProxy( - QList{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() }, - { "Accept", "application/json" } }); + auto api_headers = new Net::StaticHeaderProxy(QList{ + { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } }); addHeaderProxy(api_headers); } @@ -70,14 +69,14 @@ QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request) QHttpPart filePart; filePart.setBodyDevice(file); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); - filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\""); + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"; filename=\"" + file->fileName() + "\""); multipart->append(filePart); QHttpPart typePart; typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\""); typePart.setBody("file"); multipart->append(typePart); QHttpPart namePart; - namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\""); + namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"title\""); namePart.setBody(m_fileInfo.baseName().toUtf8()); multipart->append(namePart); @@ -124,7 +123,7 @@ auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot) { auto up = makeShared(m_shot->m_file); - up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "upload.json"); + up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "image"); up->m_sink.reset(new Sink(m_shot)); return up; } From 52338f0e884bd4f7f22cca6ae34ef6dd06f4374b Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 13 Jun 2024 19:55:50 +0200 Subject: [PATCH 137/139] fix(flatpak): disable docs for glfw Signed-off-by: Sefa Eyeoglu --- flatpak/org.prismlauncher.PrismLauncher.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flatpak/org.prismlauncher.PrismLauncher.yml b/flatpak/org.prismlauncher.PrismLauncher.yml index 352992c77..bd09f7fd8 100644 --- a/flatpak/org.prismlauncher.PrismLauncher.yml +++ b/flatpak/org.prismlauncher.PrismLauncher.yml @@ -64,7 +64,8 @@ modules: config-opts: - -DCMAKE_BUILD_TYPE=RelWithDebInfo - -DBUILD_SHARED_LIBS:BOOL=ON - - -DGLFW_USE_WAYLAND=ON + - -DGLFW_USE_WAYLAND:BOOL=ON + - -DGLFW_BUILD_DOCS:BOOL=OFF sources: - type: git url: https://github.com/glfw/glfw.git From a121bfbb15cad179dc922921e8a2cccc2a788fce Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 15 Jun 2024 10:43:21 +0300 Subject: [PATCH 138/139] add size back to texture view Signed-off-by: Trial97 --- launcher/minecraft/mod/TexturePackFolderModel.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 16b7328cf..577b33878 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -45,11 +45,12 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); + m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; - m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; - m_columnsHideable = { false, true, false, true }; + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, + QHeaderView::Interactive }; + m_columnsHideable = { false, true, false, true, true }; } Task* TexturePackFolderModel::createUpdateTask() From f1d1c9e22c393eafa961b4a3e4f9eb80034c17fb Mon Sep 17 00:00:00 2001 From: Trial97 Date: Sat, 15 Jun 2024 11:06:20 +0300 Subject: [PATCH 139/139] forget sort keys Signed-off-by: Trial97 --- launcher/minecraft/mod/TexturePackFolderModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 577b33878..a042c9113 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -47,7 +47,7 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* { m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Size" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Size") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true };