mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-09-12 13:38:11 -04:00
Restructure updating of shared directories
Signed-off-by: Naomi <103967@gmail.com>
This commit is contained in:
parent
62e4845dca
commit
db4e7d8820
@ -315,8 +315,6 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/Library.cpp
|
minecraft/Library.cpp
|
||||||
minecraft/Library.h
|
minecraft/Library.h
|
||||||
minecraft/MojangDownloadInfo.h
|
minecraft/MojangDownloadInfo.h
|
||||||
minecraft/UpdateSharedDirectoriesTask.cpp
|
|
||||||
minecraft/UpdateSharedDirectoriesTask.h
|
|
||||||
minecraft/VanillaInstanceCreationTask.cpp
|
minecraft/VanillaInstanceCreationTask.cpp
|
||||||
minecraft/VanillaInstanceCreationTask.h
|
minecraft/VanillaInstanceCreationTask.h
|
||||||
minecraft/VersionFile.cpp
|
minecraft/VersionFile.cpp
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
#include "DesktopServices.h"
|
#include "DesktopServices.h"
|
||||||
#include "PSaveFile.h"
|
#include "PSaveFile.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
#include "ui/dialogs/FileConflictDialog.h"
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
@ -674,6 +676,44 @@ bool move(const QString& source, const QString& dest)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool interactiveMove(const QString& source, const QString& destination, bool recursive /* = false */, QWidget* parent /* = nullptr */)
|
||||||
|
{
|
||||||
|
const QFileInfo sourceInfo(source);
|
||||||
|
|
||||||
|
// Make sure the source exists.
|
||||||
|
if (!sourceInfo.exists())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Recursive doesn't make sense if the source isn't a directory.
|
||||||
|
if (recursive && sourceInfo.isDir()) {
|
||||||
|
QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot);
|
||||||
|
|
||||||
|
while (sourceIt.hasNext()) {
|
||||||
|
if (!interactiveMove(sourceIt.next(), FS::PathCombine(destination, sourceIt.fileName()), false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFile(destination).exists()) {
|
||||||
|
FileConflictDialog dialog(source, destination, true, parent);
|
||||||
|
FileConflictDialog::Result result = dialog.execWithResult();
|
||||||
|
|
||||||
|
switch(result) {
|
||||||
|
case FileConflictDialog::Cancel:
|
||||||
|
return false;
|
||||||
|
case FileConflictDialog::ChooseDestination:
|
||||||
|
return FS::deletePath(source);
|
||||||
|
case FileConflictDialog::ChooseSource:
|
||||||
|
FS::deletePath(destination);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FS::move(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
bool deletePath(QString path)
|
bool deletePath(QString path)
|
||||||
{
|
{
|
||||||
std::error_code err;
|
std::error_code err;
|
||||||
@ -1711,4 +1751,51 @@ QString getSymLinkTarget(const QString& path)
|
|||||||
return QFileInfo(path).symLinkTarget();
|
return QFileInfo(path).symLinkTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName /* = "symbolic link" */) {
|
||||||
|
// Make sure that symbolic links are supported.
|
||||||
|
if (!FS::canLink(source, destination)) {
|
||||||
|
CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nSymbolic links are not supported on the filesystem").arg(symlinkName), QMessageBox::Warning, QMessageBox::Ok)->exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the destination already exists.
|
||||||
|
// If it's already a symlink, it might already be correct.
|
||||||
|
if (FS::isSymLink(destination)) {
|
||||||
|
// If the target of the symlink is already the source, there's nothing to do.
|
||||||
|
if (FS::getSymLinkTarget(destination) == source) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FS::deletePath(destination);
|
||||||
|
} else if (QFileInfo::exists(destination)) {
|
||||||
|
if (!FS::checkFolderPathEmpty(destination)) {
|
||||||
|
if (!interactiveMove(destination, source, true)) {
|
||||||
|
CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nEnsure that \"%2\" is empty.").arg(symlinkName).arg(destination), QMessageBox::Warning, QMessageBox::Ok)->exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FS::deletePath(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the source folder exists
|
||||||
|
if (!FS::ensureFolderPathExists(source)) {
|
||||||
|
CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nEnsure that \"%2\" exists.").arg(symlinkName).arg(source), QMessageBox::Warning, QMessageBox::Ok)->exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FS::create_link folderLink(source, destination);
|
||||||
|
folderLink.linkRecursively(false);
|
||||||
|
|
||||||
|
if (!folderLink()) {
|
||||||
|
CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1. Error %2: %3")
|
||||||
|
.arg(symlinkName)
|
||||||
|
.arg(folderLink.getOSError().value())
|
||||||
|
.arg(folderLink.getOSError().message().c_str()),
|
||||||
|
QMessageBox::Warning, QMessageBox::Ok)->exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
@ -293,6 +293,17 @@ class create_link : public QObject {
|
|||||||
*/
|
*/
|
||||||
bool move(const QString& source, const QString& dest);
|
bool move(const QString& source, const QString& dest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move a file or folder, and ask the user what to do in case of a conflict.
|
||||||
|
* @param source What to move.
|
||||||
|
* @param destination Where to move it to.
|
||||||
|
* @param recursive If true, all direct children will be moved 1 by 1.
|
||||||
|
* If false, the source will be directly moved to the destination.
|
||||||
|
* @param parent The parent of the dialog.
|
||||||
|
* @return True if everything could be moved.
|
||||||
|
*/
|
||||||
|
bool interactiveMove(const QString& source, const QString& destination, bool recursive = false, QWidget* parent = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a folder recursively
|
* Delete a folder recursively
|
||||||
*/
|
*/
|
||||||
@ -587,4 +598,6 @@ bool isSymLink(const QString& path);
|
|||||||
*/
|
*/
|
||||||
QString getSymLinkTarget(const QString& path);
|
QString getSymLinkTarget(const QString& path);
|
||||||
|
|
||||||
|
bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName = "symbolic link");
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
@ -85,7 +85,6 @@
|
|||||||
#include "AssetsUtils.h"
|
#include "AssetsUtils.h"
|
||||||
#include "MinecraftLoadAndCheck.h"
|
#include "MinecraftLoadAndCheck.h"
|
||||||
#include "PackProfile.h"
|
#include "PackProfile.h"
|
||||||
#include "minecraft/UpdateSharedDirectoriesTask.h"
|
|
||||||
#include "minecraft/gameoptions/GameOptions.h"
|
#include "minecraft/gameoptions/GameOptions.h"
|
||||||
#include "minecraft/update/FoldersTask.h"
|
#include "minecraft/update/FoldersTask.h"
|
||||||
|
|
||||||
@ -1317,8 +1316,38 @@ QList<Mod*> MinecraftInstance::getJarMods() const
|
|||||||
void MinecraftInstance::applySettings()
|
void MinecraftInstance::applySettings()
|
||||||
{
|
{
|
||||||
// Shared directories
|
// Shared directories
|
||||||
m_update_shared_directories_task = std::make_shared<UpdateSharedDirectoriesTask>(this);
|
updateSharedDirectories();
|
||||||
m_update_shared_directories_task->start();
|
}
|
||||||
|
|
||||||
|
bool MinecraftInstance::updateSharedDirectories()
|
||||||
|
{
|
||||||
|
const std::vector<std::tuple<QString, QString, QString>> sharedDirectories = {
|
||||||
|
{ "UseSharedScreenshotsFolder", m_settings->get("SharedScreenshotsPath").toString(), screenshotsDir() },
|
||||||
|
{ "UseSharedSavesFolder", m_settings->get("SharedSavesPath").toString(), worldDir() },
|
||||||
|
{ "UseSharedResourcePacksFolder", m_settings->get("SharedResourcePacksPath").toString(), resourcePacksDir() },
|
||||||
|
{ "UseSharedResourcePacksFolder", m_settings->get("SharedResourcePacksPath").toString(), texturePacksDir() }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
for (const auto& [useSetting, source, destination] : sharedDirectories) {
|
||||||
|
// Create symlink if useSetting is true, remove symlink if false.
|
||||||
|
if (m_settings->get(useSetting).toBool())
|
||||||
|
{
|
||||||
|
// Try to create symlink, set setting to false if failed.
|
||||||
|
if(!FS::tryCreateSymlink(source, destination, tr("shared folder"))) {
|
||||||
|
m_settings->set(useSetting, false);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Safety check
|
||||||
|
if (FS::isSymLink(destination)) {
|
||||||
|
success = FS::deletePath(destination) && success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "MinecraftInstance.moc"
|
#include "MinecraftInstance.moc"
|
||||||
|
@ -52,7 +52,6 @@ class WorldList;
|
|||||||
class GameOptions;
|
class GameOptions;
|
||||||
class LaunchStep;
|
class LaunchStep;
|
||||||
class PackProfile;
|
class PackProfile;
|
||||||
class UpdateSharedDirectoriesTask;
|
|
||||||
|
|
||||||
class MinecraftInstance : public BaseInstance {
|
class MinecraftInstance : public BaseInstance {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -165,9 +164,10 @@ class MinecraftInstance : public BaseInstance {
|
|||||||
protected:
|
protected:
|
||||||
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
|
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
|
||||||
|
|
||||||
|
bool updateSharedDirectories();
|
||||||
|
|
||||||
protected: // data
|
protected: // data
|
||||||
std::shared_ptr<PackProfile> m_components;
|
std::shared_ptr<PackProfile> m_components;
|
||||||
std::shared_ptr<UpdateSharedDirectoriesTask> m_update_shared_directories_task;
|
|
||||||
mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
|
mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
|
||||||
mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
|
mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
|
||||||
mutable std::shared_ptr<ModFolderModel> m_nil_mod_list;
|
mutable std::shared_ptr<ModFolderModel> m_nil_mod_list;
|
||||||
|
@ -1,186 +0,0 @@
|
|||||||
#include "UpdateSharedDirectoriesTask.h"
|
|
||||||
|
|
||||||
#include <QDirIterator>
|
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "tasks/ConcurrentTask.h"
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
|
||||||
#include "ui/dialogs/FileConflictDialog.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Move a file or folder, and ask the user what to do in case of a conflict.
|
|
||||||
* @param source What to move.
|
|
||||||
* @param destination Where to move it to.
|
|
||||||
* @param recursive If true, all direct children will be moved 1 by 1.
|
|
||||||
* If false, the source will be directly moved to the destination.
|
|
||||||
* @param parent The parent of the dialog.
|
|
||||||
* @return True if everything could be moved.
|
|
||||||
*/
|
|
||||||
bool interactiveMove(const QString& source, const QString& destination, bool recursive = false, QWidget* parent = nullptr)
|
|
||||||
{
|
|
||||||
const QFileInfo sourceInfo(source);
|
|
||||||
|
|
||||||
// Make sure the source exists.
|
|
||||||
if (!sourceInfo.exists())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Recursive doesn't make sense if the source isn't a directory.
|
|
||||||
if (recursive && sourceInfo.isDir()) {
|
|
||||||
QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot);
|
|
||||||
|
|
||||||
while (sourceIt.hasNext()) {
|
|
||||||
if (!interactiveMove(sourceIt.next(), FS::PathCombine(destination, sourceIt.fileName()), false))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (QFile(destination).exists()) {
|
|
||||||
FileConflictDialog dialog(source, destination, true, parent);
|
|
||||||
FileConflictDialog::Result result = dialog.execWithResult();
|
|
||||||
|
|
||||||
switch(result) {
|
|
||||||
case FileConflictDialog::Cancel:
|
|
||||||
return false;
|
|
||||||
case FileConflictDialog::ChooseDestination:
|
|
||||||
return FS::deletePath(source);
|
|
||||||
case FileConflictDialog::ChooseSource:
|
|
||||||
FS::deletePath(destination);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FS::move(source, destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 shared folder.\nSymbolic links are not supported on the filesystem"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the destination already exists.
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
FS::deletePath(m_destination);
|
|
||||||
} else if (QFileInfo::exists(m_destination)) {
|
|
||||||
if (!FS::checkFolderPathEmpty(m_destination)) {
|
|
||||||
if (!interactiveMove(m_destination, m_source, true)) {
|
|
||||||
fail(tr("Failed to create shared folder.\nEnsure that \"%1\" is empty.").arg(m_destination));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FS::deletePath(m_destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the source folder exists
|
|
||||||
if (!FS::ensureFolderPathExists(m_source)) {
|
|
||||||
fail(tr("Failed to create shared folder.\nEnsure that \"%1\" exists.").arg(m_source));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FS::create_link folderLink(m_source, m_destination);
|
|
||||||
folderLink.linkRecursively(false);
|
|
||||||
|
|
||||||
if (folderLink()) {
|
|
||||||
emitSucceeded();
|
|
||||||
} else {
|
|
||||||
fail(tr("Failed to create shared folder. Error %1: %2")
|
|
||||||
.arg(folderLink.getOSError().value())
|
|
||||||
.arg(folderLink.getOSError().message().c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
UpdateSharedDirectoriesTask::UpdateSharedDirectoriesTask(MinecraftInstance* inst)
|
|
||||||
: Task(), m_inst(inst)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void UpdateSharedDirectoriesTask::executeTask()
|
|
||||||
{
|
|
||||||
auto tasks = makeShared<ConcurrentTask>("UpdateSharedDirectoriesTask");
|
|
||||||
|
|
||||||
auto screenshotsTask = makeShared<TryCreateSymlinkTask>(m_inst->settings()->get("SharedScreenshotsPath").toString(),
|
|
||||||
m_inst->screenshotsDir(), m_inst, "UseSharedScreenshotsFolder");
|
|
||||||
connect(screenshotsTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed);
|
|
||||||
tasks->addTask(screenshotsTask);
|
|
||||||
|
|
||||||
auto savesTask = makeShared<TryCreateSymlinkTask>(m_inst->settings()->get("SharedSavesPath").toString(), m_inst->worldDir(), m_inst,
|
|
||||||
"UseSharedSavesFolder");
|
|
||||||
connect(savesTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed);
|
|
||||||
tasks->addTask(savesTask);
|
|
||||||
|
|
||||||
auto resoucePacksTask = makeShared<TryCreateSymlinkTask>(m_inst->settings()->get("SharedResourcePacksPath").toString(),
|
|
||||||
m_inst->resourcePacksDir(), m_inst, "UseSharedResourcePacksFolder");
|
|
||||||
connect(resoucePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed);
|
|
||||||
tasks->addTask(resoucePacksTask);
|
|
||||||
|
|
||||||
auto texturePacksTask = makeShared<TryCreateSymlinkTask>(m_inst->settings()->get("SharedResourcePacksPath").toString(),
|
|
||||||
m_inst->texturePacksDir(), m_inst, "UseSharedResourcePacksFolder");
|
|
||||||
connect(texturePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed);
|
|
||||||
tasks->addTask(texturePacksTask);
|
|
||||||
|
|
||||||
m_tasks = tasks;
|
|
||||||
|
|
||||||
connect(m_tasks.get(), &Task::succeeded, this, &UpdateSharedDirectoriesTask::emitSucceeded);
|
|
||||||
|
|
||||||
m_tasks->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateSharedDirectoriesTask::notifyFailed(QString reason)
|
|
||||||
{
|
|
||||||
CustomMessageBox::selectable(nullptr, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec();
|
|
||||||
emit failed(reason);
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
|
|
||||||
class MinecraftInstance;
|
|
||||||
|
|
||||||
class UpdateSharedDirectoriesTask : public Task {
|
|
||||||
public:
|
|
||||||
explicit UpdateSharedDirectoriesTask(MinecraftInstance* inst);
|
|
||||||
virtual ~UpdateSharedDirectoriesTask = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void executeTask() override;
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void notifyFailed(QString reason);
|
|
||||||
|
|
||||||
private:
|
|
||||||
MinecraftInstance* m_inst;
|
|
||||||
Task::Ptr m_tasks;
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user