Create initial global folders feature

Signed-off-by: Naomi <103967@gmail.com>
This commit is contained in:
Naomi 2024-08-17 23:28:55 +02:00
parent 6352362907
commit b3afe43351
17 changed files with 348 additions and 21 deletions

View File

@ -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()

View File

@ -269,6 +269,11 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
bool removeLinkedInstanceId(const QString& id);
bool isLinkedToInstanceId(const QString& id) const;
/**
* \brief Should be called whenever settings have changed that need to be re-applied.
*/
virtual void applySettings() {}
protected:
void changeStatus(Status newStatus);

View File

@ -302,6 +302,8 @@ set(MINECRAFT_SOURCES
minecraft/Library.cpp
minecraft/Library.h
minecraft/MojangDownloadInfo.h
minecraft/UpdateGlobalDirectoriesTask.cpp
minecraft/UpdateGlobalDirectoriesTask.h
minecraft/VanillaInstanceCreationTask.cpp
minecraft/VanillaInstanceCreationTask.h
minecraft/VersionFile.cpp

View File

@ -285,6 +285,16 @@ bool ensureFolderPathExists(const QString folderPathName)
return ensureFolderPathExists(QFileInfo(folderPathName));
}
bool checkFolderPathExists(const QString& folderPathName)
{
return QDir(folderPathName).exists();
}
bool checkFolderPathEmpty(const QString& folderPathName)
{
return QDir(folderPathName).isEmpty(QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System));
}
bool copyFileAttributes(QString src, QString dst)
{
#ifdef Q_OS_WIN32
@ -1717,4 +1727,14 @@ QString getUniqueResourceName(const QString& filePath)
return newFileName;
}
bool isSymLink(const QString& path)
{
return QFileInfo(path).isSymLink();
}
QString getSymLinkTarget(const QString& path)
{
return QFileInfo(path).symLinkTarget();
}
} // namespace FS

View File

@ -99,6 +99,20 @@ bool ensureFolderPathExists(const QFileInfo folderPath);
*/
bool ensureFolderPathExists(const QString folderPathName);
/**
* @brief Check if the given folder exists
* @param folderPathName The path to a folder to check
* @return True if the given folder exists
*/
bool checkFolderPathExists(const QString& folderPathName);
/**
* @brief Check if the given folder is empty or doesn't exist
* @param folderPathName The path to a folder to check
* @return True if the given folder is empty or doesn't exist
*/
bool checkFolderPathEmpty(const QString& folderPathName);
/**
* @brief Copies a directory and it's contents from src to dest
*/
@ -562,4 +576,18 @@ QString getPathNameInLocal8bit(const QString& file);
QString getUniqueResourceName(const QString& filePath);
/**
* @brief Check if a file or folder is a symbolic link
* @param path The path to check
* @return True if the object exists and is an symbolic link
*/
bool isSymLink(const QString& path);
/**
* @brief Get the target of a symbolic link
* @param path The path to check
* @return The target of a symbolic link. Empty if path is not a symbolic link.
*/
QString getSymLinkTarget(const QString& path);
} // namespace FS

View File

@ -86,6 +86,7 @@
#include "PackProfile.h"
#include "minecraft/gameoptions/GameOptions.h"
#include "minecraft/update/FoldersTask.h"
#include "minecraft/UpdateGlobalDirectoriesTask.h"
#include "tools/BaseProfiler.h"
@ -350,6 +351,11 @@ QString MinecraftInstance::nilModsDir() const
return FS::PathCombine(gameRoot(), "nilmods");
}
QString MinecraftInstance::screenshotsDir() const
{
return FS::PathCombine(gameRoot(), "screenshots");
}
QString MinecraftInstance::resourcePacksDir() const
{
return FS::PathCombine(gameRoot(), "resourcepacks");
@ -1229,4 +1235,11 @@ QList<Mod*> MinecraftInstance::getJarMods() const
return mods;
}
void MinecraftInstance::applySettings()
{
// Global directories
m_update_global_directories_task = std::make_shared<UpdateGlobalDirectoriesTask>(this, nullptr);
m_update_global_directories_task->start();
}
#include "MinecraftInstance.moc"

View File

@ -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<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
protected: // data
std::shared_ptr<PackProfile> m_components;
std::shared_ptr<UpdateGlobalDirectoriesTask> m_update_global_directories_task;
mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
mutable std::shared_ptr<ModFolderModel> m_nil_mod_list;

View File

@ -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<ConcurrentTask>(this, "UpdateGlobalDirectoriesTask");
auto screenshotsTask = makeShared<TryCreateSymlinkTask>(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<TryCreateSymlinkTask>(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<TryCreateSymlinkTask>(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<TryCreateSymlinkTask>(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);
}

View File

@ -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;
};

View File

@ -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()

View File

@ -60,13 +60,20 @@
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DropOnly</enum>
<enum>QAbstractItemView::DragDropMode::DropOnly</enum>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLabel" name="isSymlinkWarning">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;Warning: This is the global folder&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="WideBar" name="actionsToolbar">
@ -74,7 +81,7 @@
<string>Actions</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
<enum>Qt::ToolButtonStyle::ToolButtonTextOnly</enum>
</property>
<property name="useDefaultAction" stdset="0">
<bool>true</bool>
@ -184,6 +191,11 @@
</action>
</widget>
<customwidgets>
<customwidget>
<class>WideBar</class>
<extends>QToolBar</extends>
<header>ui/widgets/WideBar.h</header>
</customwidget>
<customwidget>
<class>ModListView</class>
<extends>QTreeView</extends>
@ -195,11 +207,6 @@
<header>ui/widgets/InfoFrame.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>WideBar</class>
<extends>QToolBar</extends>
<header>ui/widgets/WideBar.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>treeView</tabstop>

View File

@ -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()

View File

@ -36,7 +36,7 @@
<item>
<widget class="QTabWidget" name="settingsTabs">
<property name="currentIndex">
<number>0</number>
<number>6</number>
</property>
<widget class="QWidget" name="minecraftPage">
<attribute name="title">
@ -218,7 +218,7 @@
<string notr="true"/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="buddy">
<cstring>maxMemSpinBox</cstring>
@ -399,7 +399,7 @@
<item>
<spacer name="verticalSpacerMinecraft_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -505,7 +505,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -583,7 +583,7 @@
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -727,10 +727,56 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="instanceUseGlobalFolders">
<property name="focusPolicy">
<enum>Qt::FocusPolicy::StrongFocus</enum>
</property>
<property name="title">
<string>Global Folders</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#f5c211;&quot;&gt;Warning&lt;/span&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;: After enabling files cannot be automatically restored to their original folder.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useGlobalScreenshotsFolder">
<property name="text">
<string>Use global screenshots folder</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useGlobalSavesFolder">
<property name="text">
<string>Use global saves folder</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useGlobalResourcePacksFolder">
<property name="text">
<string>Use global resource packs folder</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacerMiscellaneous">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View File

@ -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()

View File

@ -11,7 +11,7 @@
</rect>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
@ -24,13 +24,26 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="globalScreenshotsFolderWarninglabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#f5c211;&quot;&gt;Warning&lt;/span&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;: This is the global screenshots folder&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="listView">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
<enum>QAbstractItemView::SelectionMode::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
</widget>
</item>
@ -41,7 +54,7 @@
<string>Actions</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
<enum>Qt::ToolButtonStyle::ToolButtonTextOnly</enum>
</property>
<attribute name="toolBarArea">
<enum>RightToolBarArea</enum>

View File

@ -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()

View File

@ -11,7 +11,7 @@
</rect>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
@ -24,6 +24,13 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="globalSavesFolderWarninglabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;Warning: This is the global saves folder&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="worldTreeView">
<property name="sizePolicy">
@ -36,7 +43,7 @@
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
<enum>QAbstractItemView::DragDropMode::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
@ -65,10 +72,10 @@
<string>Actions</string>
</property>
<property name="allowedAreas">
<set>Qt::LeftToolBarArea|Qt::RightToolBarArea</set>
<set>Qt::ToolBarArea::LeftToolBarArea|Qt::ToolBarArea::RightToolBarArea</set>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
<enum>Qt::ToolButtonStyle::ToolButtonTextOnly</enum>
</property>
<property name="floatable">
<bool>false</bool>