diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index fce3bb177..e5d69986c 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -346,6 +346,7 @@ set(MINECRAFT_SOURCES minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/ShaderPackFolderModel.h + minecraft/mod/ShaderPackFolderModel.cpp minecraft/mod/tasks/ResourceFolderLoadTask.h minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/LocalModParseTask.h diff --git a/launcher/FileIgnoreProxy.cpp b/launcher/FileIgnoreProxy.cpp index cebe82eda..445c2a881 100644 --- a/launcher/FileIgnoreProxy.cpp +++ b/launcher/FileIgnoreProxy.cpp @@ -266,7 +266,21 @@ bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceP bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const { - return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath())); + if (m_ignoreFiles.contains(fileInfo.fileName())) { + return true; + } + + for (const auto& suffix : m_ignoreFilesSuffixes) { + if (fileInfo.fileName().endsWith(suffix)) { + return true; + } + } + + if (m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()))) { + return true; + } + + return false; } bool FileIgnoreProxy::filterFile(const QFileInfo& file) const diff --git a/launcher/FileIgnoreProxy.h b/launcher/FileIgnoreProxy.h index 5184fc354..0f149ecb6 100644 --- a/launcher/FileIgnoreProxy.h +++ b/launcher/FileIgnoreProxy.h @@ -66,6 +66,7 @@ class FileIgnoreProxy : public QSortFilterProxyModel { // list of file names that need to be removed completely from model inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; } + inline QStringList& ignoreFilesWithSuffix() { return m_ignoreFilesSuffixes; } // list of relative paths that need to be removed completely from model inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; } @@ -85,5 +86,6 @@ class FileIgnoreProxy : public QSortFilterProxyModel { const QString m_root; SeparatorPrefixTree<'/'> m_blocked; QStringList m_ignoreFiles; + QStringList m_ignoreFilesSuffixes; SeparatorPrefixTree<'/'> m_ignoreFilePaths; }; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 4083a7088..273a469d9 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -23,6 +23,7 @@ #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" #include "settings/Setting.h" +#include "tasks/SequentialTask.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" @@ -334,7 +335,20 @@ bool ResourceFolderModel::update() }, Qt::ConnectionType::QueuedConnection); - QThreadPool::globalInstance()->start(m_current_update_task.get()); + Task::Ptr preUpdate{createPreUpdateTask()}; + + if (preUpdate != nullptr) { + auto task = new SequentialTask("ResourceFolderModel::update"); + + task->addTask(preUpdate); + task->addTask(m_current_update_task); + + connect(task, &Task::finished, [task] { task->deleteLater(); }); + + QThreadPool::globalInstance()->start(task); + } else { + QThreadPool::globalInstance()->start(m_current_update_task.get()); + } return true; } diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 5c41fc520..0526b5bbf 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -84,7 +84,7 @@ class ResourceFolderModel : public QAbstractListModel { virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } - QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; } + virtual QDir indexDir() const { return { QString("%1/.index").arg(dir().absolutePath()) }; } /** Given a path in the system, install that resource, moving it to its place in the * instance file hierarchy. @@ -188,6 +188,7 @@ class ResourceFolderModel : public QAbstractListModel { void parseFinished(); protected: + [[nodiscard]] virtual Task* createPreUpdateTask() { return nullptr; } /** This creates a new update task to be executed by update(). * * The task should load and parse all resources necessary, and provide a way of accessing such results. diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.cpp b/launcher/minecraft/mod/ShaderPackFolderModel.cpp new file mode 100644 index 000000000..5cf5d2eb1 --- /dev/null +++ b/launcher/minecraft/mod/ShaderPackFolderModel.cpp @@ -0,0 +1,56 @@ +#include "FileSystem.h" +#include "ShaderPackFolderModel.h" + +namespace { +class ShaderPackIndexMigrateTask : public Task { + Q_OBJECT + public: + ShaderPackIndexMigrateTask(QDir resourceDir, QDir indexDir) : m_resourceDir(std::move(resourceDir)), m_indexDir(std::move(indexDir)) {} + + void executeTask() override + { + if (!m_indexDir.exists()) { + qDebug() << m_indexDir.absolutePath() << "does not exist; nothing to migrate"; + emitSucceeded(); + return; + } + + QStringList pwFiles = m_indexDir.entryList({ "*.pw.toml" }, QDir::Files); + bool movedAll = true; + + for (const auto& file : pwFiles) { + QString src = m_indexDir.filePath(file); + QString dest = m_resourceDir.filePath(file); + + if (FS::move(src, dest)) { + qDebug() << "Moved" << src << "to" << dest; + } else { + movedAll = false; + } + } + + if (!movedAll) { + // FIXME: not shown in the UI + emitFailed(tr("Failed to migrate shaderpack metadata from .index")); + return; + } + + if (!FS::deletePath(m_indexDir.absolutePath())) { + emitFailed(tr("Failed to remove old .index dir")); + return; + } + + emitSucceeded(); + } + + private: + QDir m_resourceDir, m_indexDir; +}; +} // namespace + +Task* ShaderPackFolderModel::createPreUpdateTask() +{ + return new ShaderPackIndexMigrateTask(m_dir, ResourceFolderModel::indexDir()); +} + +#include "ShaderPackFolderModel.moc" diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index cd01f6226..9b0180180 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -21,5 +21,16 @@ class ShaderPackFolderModel : public ResourceFolderModel { return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast(resource)); } + QDir indexDir() const override { return m_dir; } + + Task* createPreUpdateTask() override; + + // avoid watching twice + virtual bool startWatching() override { return ResourceFolderModel::startWatching({ m_dir.absolutePath() }); } + virtual bool stopWatching() override { return ResourceFolderModel::stopWatching({ m_dir.absolutePath() }); } + RESOURCE_HELPERS(ShaderPack); + + private: + QMutex m_migrateLock; }; diff --git a/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp index 98dab9abb..3b98e053b 100644 --- a/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp @@ -136,6 +136,10 @@ void ResourceFolderLoadTask::getFromMetadata() { m_index_dir.refresh(); for (auto entry : m_index_dir.entryList(QDir::Files)) { + if (!entry.endsWith(".pw.toml")) { + continue; + } + auto metadata = Metadata::get(m_index_dir, entry); if (!metadata.isValid()) diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index e6c17972d..17b3ba703 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -95,6 +95,7 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren m_proxy->ignoreFilesWithPath().insert(FS::PathCombine(prefix, path)); } m_proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" }); + m_proxy->ignoreFilesWithSuffix().append(".pw.toml"); m_proxy->setSourceModel(model); m_proxy->loadBlockedPathsFromFile(ignoreFileName()); @@ -103,8 +104,19 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren MinecraftInstance* mcInstance = dynamic_cast(instance.get()); if (mcInstance) { for (auto resourceModel : mcInstance->resourceLists()) { - if (resourceModel && resourceModel->indexDir().exists()) - m_proxy->ignoreFilesWithPath().insert(instanceRoot.relativeFilePath(resourceModel->indexDir().absolutePath())); + if (resourceModel == nullptr) { + continue; + } + + if (!resourceModel->indexDir().exists()) { + continue; + } + + if (resourceModel->dir() == resourceModel->indexDir()) { + continue; + } + + m_proxy->ignoreFilesWithPath().insert(instanceRoot.relativeFilePath(resourceModel->indexDir().absolutePath())); } }