diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 69cf95e3c..bf7fd2355 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -105,6 +105,11 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("ManagedPackVersionName", ""); m_settings->registerSetting("Profiler", ""); + + // Global folders + m_settings->registerSetting("UseGlobalScreenshotsFolder", false); + m_settings->registerSetting("UseGlobalSavesFolder", false); + m_settings->registerSetting("UseGlobalResourcePacksFolder", false); } QString BaseInstance::getPreLaunchCommand() diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 499ec7866..ad92c5bcb 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -269,6 +269,11 @@ class BaseInstance : public QObject, public std::enable_shared_from_this MinecraftInstance::getJarMods() const return mods; } +void MinecraftInstance::applySettings() +{ + // Global directories + m_update_global_directories_task = std::make_shared(this, nullptr); + m_update_global_directories_task->start(); +} + #include "MinecraftInstance.moc" diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 7af0df389..806f58df6 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -51,6 +51,7 @@ class WorldList; class GameOptions; class LaunchStep; class PackProfile; +class UpdateGlobalDirectoriesTask; class MinecraftInstance : public BaseInstance { Q_OBJECT @@ -74,6 +75,7 @@ class MinecraftInstance : public BaseInstance { ////// Directories and files ////// QString jarModsDir() const; + QString screenshotsDir() const; QString resourcePacksDir() const; QString texturePacksDir() const; QString shaderPacksDir() const; @@ -159,11 +161,14 @@ class MinecraftInstance : public BaseInstance { virtual JavaVersion getJavaVersion(); + virtual void applySettings() override; + protected: QMap createCensorFilterFromSession(AuthSessionPtr session); protected: // data std::shared_ptr m_components; + std::shared_ptr m_update_global_directories_task; mutable std::shared_ptr m_loader_mod_list; mutable std::shared_ptr m_core_mod_list; mutable std::shared_ptr m_nil_mod_list; diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp new file mode 100644 index 000000000..56e239d7f --- /dev/null +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp @@ -0,0 +1,133 @@ +#include "UpdateGlobalDirectoriesTask.h" + +#include "Application.h" +#include "FileSystem.h" +#include "minecraft/MinecraftInstance.h" +#include "tasks/ConcurrentTask.h" +#include "ui/dialogs/CustomMessageBox.h" + +class TryCreateSymlinkTask : public Task +{ + public: + explicit TryCreateSymlinkTask(const QString& source, const QString& destination, MinecraftInstance* instance, const QString& setting) + : m_source(source), + m_destination(destination), + m_inst(instance), + m_setting(setting) + { + setObjectName("TryCreateSymlinkTask"); + } + virtual ~TryCreateSymlinkTask() + {} + + protected: + void executeTask() + { + bool create = m_inst->settings()->get(m_setting).toBool(); + + // Check if we have to delete an existing symlink + if (!create) + { + // Safety check + if (FS::isSymLink(m_destination)) + { + FS::deletePath(m_destination); + } + + emitSucceeded(); + return; + } + + // Make sure that symbolic links are supported. + if (!FS::canLink(m_source, m_destination)) + { + fail(tr("Failed to create global folder.\nSymbolic links are not supported on the filesystem")); + return; + } + + // Check if the destination already exists. + if (FS::checkFolderPathExists(m_destination)) + { + // If it's already a symlink, it might already be correct. + if (FS::isSymLink(m_destination)) + { + // If the target of the symlink is already the source, there's nothing to do. + if (FS::getSymLinkTarget(m_destination) == m_source) + { + emitSucceeded(); + return; + } + } + else if (!FS::checkFolderPathEmpty(m_destination)) + { + fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); + return; + } + + FS::deletePath(m_destination); + } + + FS::create_link folderLink(m_source, m_destination); + folderLink.linkRecursively(false); + folderLink(); // TODO: Error check + + emitSucceeded(); + return; + } + + void fail(const QString& reason) + { + m_inst->settings()->set(m_setting, false); + emitFailed(reason); + } + + private: + QString m_source; + QString m_destination; + MinecraftInstance* m_inst; + QString m_setting; +}; + +UpdateGlobalDirectoriesTask::UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent) + : Task(parent), + m_inst(inst), + m_parent(parent) +{} + +UpdateGlobalDirectoriesTask::~UpdateGlobalDirectoriesTask(){} + +void UpdateGlobalDirectoriesTask::executeTask() +{ + auto tasks = makeShared(this, "UpdateGlobalDirectoriesTask"); + + auto screenshotsTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "screenshots"), m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder"); + connect(screenshotsTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(screenshotsTask); + + auto savesTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "saves"), m_inst->worldDir(), m_inst, "UseGlobalSavesFolder"); + connect(savesTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(savesTask); + + auto resoucePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + connect(resoucePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(resoucePacksTask); + + auto texturePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + connect(texturePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(texturePacksTask); + + m_tasks = tasks; + + connect(m_tasks.get(), &Task::succeeded, this, &UpdateGlobalDirectoriesTask::emitSucceeded); + + m_tasks->start(); + +} + + void UpdateGlobalDirectoriesTask::notifyFailed(QString reason) +{ + CustomMessageBox::selectable(m_parent, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); + emit failed(reason); +} + + diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.h b/launcher/minecraft/UpdateGlobalDirectoriesTask.h new file mode 100644 index 000000000..4afadc8ec --- /dev/null +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.h @@ -0,0 +1,22 @@ +#pragma once + +#include "tasks/Task.h" + +class MinecraftInstance; + +class UpdateGlobalDirectoriesTask : public Task { + public: + explicit UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent = 0); + virtual ~UpdateGlobalDirectoriesTask(); + + protected: + virtual void executeTask() override; + + protected slots: + void notifyFailed(QString reason); + + private: + MinecraftInstance* m_inst; + QWidget* m_parent; + Task::Ptr m_tasks; +}; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 8f8dab46d..5c45d25ad 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -38,6 +38,7 @@ #include "ui_ExternalResourcesPage.h" #include "DesktopServices.h" +#include "FileSystem.h" #include "Version.h" #include "minecraft/mod/ResourceFolderModel.h" #include "ui/GuiUtil.h" @@ -138,6 +139,9 @@ void ExternalResourcesPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->actionsToolbar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + + // Enable the symbolic link warning when the folder is a symbolic link + ui->isSymlinkWarning->setVisible(FS::isSymLink(m_model->dir().absolutePath())); } void ExternalResourcesPage::closedImpl() diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 9d6f61db0..4df948727 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -60,13 +60,20 @@ true - QAbstractItemView::DropOnly + QAbstractItemView::DragDropMode::DropOnly true + + + + <html><head/><body><p><span style=" color:#f5c211;">Warning: This is the global folder</span></p></body></html> + + + @@ -74,7 +81,7 @@ Actions - Qt::ToolButtonTextOnly + Qt::ToolButtonStyle::ToolButtonTextOnly true @@ -184,6 +191,11 @@ + + WideBar + QToolBar +
ui/widgets/WideBar.h
+
ModListView QTreeView @@ -195,11 +207,6 @@
ui/widgets/InfoFrame.h
1
- - WideBar - QToolBar -
ui/widgets/WideBar.h
-
treeView diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 76add9402..dfea0a70d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -276,6 +276,13 @@ void InstanceSettingsPage::applySettings() m_settings->reset("OnlineFixes"); } + // Global folders + m_settings->set("UseGlobalScreenshotsFolder", ui->useGlobalScreenshotsFolder->isChecked()); + m_settings->set("UseGlobalSavesFolder", ui->useGlobalSavesFolder->isChecked()); + m_settings->set("UseGlobalResourcePacksFolder", ui->useGlobalResourcePacksFolder->isChecked()); + + m_instance->applySettings(); + // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); } @@ -386,6 +393,10 @@ void InstanceSettingsPage::loadSettings() ui->legacySettingsGroupBox->setChecked(m_settings->get("OverrideLegacySettings").toBool()); ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); + + ui->useGlobalScreenshotsFolder->setChecked(m_settings->get("UseGlobalScreenshotsFolder").toBool()); + ui->useGlobalSavesFolder->setChecked(m_settings->get("UseGlobalSavesFolder").toBool()); + ui->useGlobalResourcePacksFolder->setChecked(m_settings->get("UseGlobalResourcePacksFolder").toBool()); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 9490860ae..4cb73b184 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -36,7 +36,7 @@ - 0 + 6 @@ -218,7 +218,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter maxMemSpinBox @@ -399,7 +399,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -505,7 +505,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -583,7 +583,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -727,10 +727,56 @@ + + + + Qt::FocusPolicy::StrongFocus + + + Global Folders + + + false + + + false + + + + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: After enabling files cannot be automatically restored to their original folder.</span></p></body></html> + + + + + + + Use global screenshots folder + + + + + + + Use global saves folder + + + + + + + Use global resource packs folder + + + + + + - Qt::Vertical + Qt::Orientation::Vertical diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index c3f955733..5d6883212 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -560,6 +560,9 @@ void ScreenshotsPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + + // Enable the symbolic link warning when the screenshots folder is a symbolic link + ui->globalScreenshotsFolderWarninglabel->setVisible(FS::isSymLink(m_folder)); } void ScreenshotsPage::closedImpl() diff --git a/launcher/ui/pages/instance/ScreenshotsPage.ui b/launcher/ui/pages/instance/ScreenshotsPage.ui index 2e2227a29..4dd650f9c 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.ui +++ b/launcher/ui/pages/instance/ScreenshotsPage.ui @@ -11,7 +11,7 @@ - + 0 @@ -24,13 +24,26 @@ 0 + + + + false + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: This is the global screenshots folder</span></p></body></html> + + + 0 + + + - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection - QAbstractItemView::SelectRows + QAbstractItemView::SelectionBehavior::SelectRows @@ -41,7 +54,7 @@ Actions - Qt::ToolButtonTextOnly + Qt::ToolButtonStyle::ToolButtonTextOnly RightToolBarArea diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 4f30e4bb7..b758f4904 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -120,6 +120,9 @@ void WorldListPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + + // Enable the symbolic link warning when the saves folder is a symbolic link + ui->globalSavesFolderWarninglabel->setVisible(FS::isSymLink(FS::PathCombine(m_inst->gameRoot(), "saves"))); } void WorldListPage::closedImpl() diff --git a/launcher/ui/pages/instance/WorldListPage.ui b/launcher/ui/pages/instance/WorldListPage.ui index d74dd0796..3ca9cad8b 100644 --- a/launcher/ui/pages/instance/WorldListPage.ui +++ b/launcher/ui/pages/instance/WorldListPage.ui @@ -11,7 +11,7 @@ - + 0 @@ -24,6 +24,13 @@ 0 + + + + <html><head/><body><p><span style=" color:#f5c211;">Warning: This is the global saves folder</span></p></body></html> + + + @@ -36,7 +43,7 @@ true - QAbstractItemView::DragDrop + QAbstractItemView::DragDropMode::DragDrop true @@ -65,10 +72,10 @@ Actions - Qt::LeftToolBarArea|Qt::RightToolBarArea + Qt::ToolBarArea::LeftToolBarArea|Qt::ToolBarArea::RightToolBarArea - Qt::ToolButtonTextOnly + Qt::ToolButtonStyle::ToolButtonTextOnly false