From f794e49bb6eadd70c52683e60a700a1d7e9cd17b Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Mon, 6 Feb 2023 23:05:06 -0800
Subject: we want to make links!
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/dialogs/CopyInstanceDialog.cpp | 19 +++
launcher/ui/dialogs/CopyInstanceDialog.h | 3 +
launcher/ui/dialogs/CopyInstanceDialog.ui | 200 +++++++++++++++++++----------
3 files changed, 155 insertions(+), 67 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 3f5122f6..981352ae 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -85,6 +85,10 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled());
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
+
+ ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
+ ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
+ ui->linkWorldsCheckbox->setChecked(m_selectedOptions.isLinkWorldsEnabled());
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -220,3 +224,18 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
m_selectedOptions.enableCopyScreenshots(state == Qt::Checked);
updateSelectAllCheckbox();
}
+
+void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
+{
+ m_selectedOptions.enableLinkFiles(checked);
+}
+
+void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+}
+
+void CopyInstanceDialog::on_linkWorldsCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableLinkWorlds(state == Qt::Checked);
+}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 884501d1..a80faab9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -55,6 +55,9 @@ slots:
void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
+ void on_linkFilesGroup_toggled(bool checked);
+ void on_hardLinksCheckbox_stateChanged(int state);
+ void on_linkWorldsCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index b7828fe3..e41ad526 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 341
- 399
+ 525
+ 581
@@ -136,70 +136,126 @@
-
-
-
-
-
-
- Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.
-
-
- Copy mods
-
-
-
- -
-
+
+
+ Instance copy options
+
+
+
-
+
+
+ Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.
+
+
+ Copy mods
+
+
+
+ -
+
+
+ Copy the in-game options like FOV, max framerate, etc.
+
+
+ Copy game options
+
+
+
+ -
+
+
+ Copy saves
+
+
+
+ -
+
+
+ Copy shader packs
+
+
+
+ -
+
+
+ Copy servers
+
+
+
+ -
+
+
+ true
+
+
+ Copy resource packs
+
+
+
+ -
+
+
+ Keep play time
+
+
+
+ -
+
+
+ Copy screenshots
+
+
+
+
+
+
+ -
+
+
-
+
- Copy the in-game options like FOV, max framerate, etc.
+ Use symbolic links instead of copying files.
-
- Copy game options
+
+ Link files instead of copying them
-
-
- -
-
-
- Copy saves
-
-
-
- -
-
-
- Copy shader packs
-
-
-
- -
-
-
- Copy servers
+
+ false
-
-
- -
-
-
+
true
-
- Copy resource packs
-
-
-
- -
-
-
- Keep play time
-
-
-
- -
-
-
- Copy screenshots
+
+ false
+
+
-
+
+
+ Use hard links instead of symbolic links
+
+
+ Use hard links
+
+
+
+ -
+
+
+ World save data will be linked and thus shared between instances.
+
+
+ Link worlds
+
+
+ true
+
+
+ false
+
+
+
+
@@ -210,7 +266,7 @@
Qt::Horizontal
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+ QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok
@@ -220,10 +276,20 @@
iconButton
instNameTextBox
groupBox
+ selectAllCheckbox
+ keepPlaytimeCheckbox
+ copyScreenshotsCheckbox
+ copySavesCheckbox
+ copyShaderPacksCheckbox
+ copyGameOptionsCheckbox
+ copyServersCheckbox
+ copyResPacksCheckbox
+ copyModsCheckbox
+ linkFilesGroup
+ hardLinksCheckbox
+ linkWorldsCheckbox
-
-
-
+
buttonBox
@@ -232,8 +298,8 @@
accept()
- 254
- 316
+ 263
+ 571
157
@@ -248,8 +314,8 @@
reject()
- 322
- 316
+ 331
+ 571
286
--
cgit
From 59788823785c186af78d8100fce3bdedbed85c80 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 14:30:45 -0800
Subject: feat(symlinks&hardlinks): linkup copy dialog
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 5 +-
launcher/FileSystem.h | 9 +++-
launcher/InstanceCopyPrefs.cpp | 8 ++--
launcher/InstanceCopyPrefs.h | 6 +--
launcher/InstanceCopyTask.cpp | 73 ++++++++++++++++++++++++++++--
launcher/InstanceCopyTask.h | 3 ++
launcher/ui/dialogs/CopyInstanceDialog.cpp | 7 +--
launcher/ui/dialogs/CopyInstanceDialog.h | 2 +-
launcher/ui/dialogs/CopyInstanceDialog.ui | 9 ++--
9 files changed, 96 insertions(+), 26 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index c48a3bba..c94770ee 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -420,7 +420,7 @@ void create_link::runPrivlaged(const QString& offset)
qint64 byteswritten = clientConnection->write(block);
bool bytesflushed = clientConnection->flush();
qDebug() << "block flushed" << byteswritten << bytesflushed;
- //clientConnection->disconnectFromServer();
+
});
qDebug() << "Listening on pipe" << serverName;
@@ -437,7 +437,6 @@ void create_link::runPrivlaged(const QString& offset)
}
-
void ExternalLinkFileProcess::runLinkFile() {
QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink");
QString params = "-s " + m_server;
@@ -463,7 +462,7 @@ void ExternalLinkFileProcess::runLinkFile() {
ShExecInfo.lpFile = programNameWin;
ShExecInfo.lpParameters = paramsWin;
ShExecInfo.lpDirectory = NULL;
- ShExecInfo.nShow = SW_NORMAL;
+ ShExecInfo.nShow = SW_HIDE;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 2e739298..d79096e6 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -211,16 +211,21 @@ class create_link : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
+ int totalLinked() { return m_linked; }
+
+
void runPrivlaged() { runPrivlaged(QString()); }
void runPrivlaged(const QString& offset);
- int totalLinked() { return m_linked; }
+ QList getResults() { return m_path_results; }
+
signals:
void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
- void finishedPrivlaged(bool gotResults);
void finished();
+ void finishedPrivlaged(bool gotResults);
+
private:
bool operator()(const QString& offset, bool dryRun = false);
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index 18a6d704..e363d4c6 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -103,9 +103,9 @@ bool InstanceCopyPrefs::isUseHardLinksEnabled() const
return useHardLinks;
}
-bool InstanceCopyPrefs::isLinkWorldsEnabled() const
+bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
{
- return linkWorlds;
+ return dontLinkSaves;
}
// ======= Setters =======
@@ -159,7 +159,7 @@ void InstanceCopyPrefs::enableUseHardLinks(bool b)
useHardLinks = b;
}
-void InstanceCopyPrefs::enableLinkWorlds(bool b)
+void InstanceCopyPrefs::enableDontLinkSaves(bool b)
{
- linkWorlds = b;
+ dontLinkSaves = b;
}
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 25c0f3fc..61719a06 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -21,7 +21,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
[[nodiscard]] bool isLinkFilesEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
- [[nodiscard]] bool isLinkWorldsEnabled() const;
+ [[nodiscard]] bool isDontLinkSavesEnabled() const;
// Setters
void enableCopySaves(bool b);
void enableKeepPlaytime(bool b);
@@ -33,7 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyScreenshots(bool b);
void enableLinkFiles(bool b);
void enableUseHardLinks(bool b);
- void enableLinkWorlds(bool b);
+ void enableDontLinkSaves(bool b);
protected: // data
bool copySaves = true;
@@ -46,5 +46,5 @@ struct InstanceCopyPrefs {
bool copyScreenshots = true;
bool linkFiles = false;
bool useHardLinks = false;
- bool linkWorlds = true;
+ bool dontLinkSaves = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 188d163b..31c6bdca 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -11,6 +11,11 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
QString filters = prefs.getSelectedFiltersAsRegex();
+
+ m_useLinks = prefs.isLinkFilesEnabled();
+ m_useHardLinks = prefs.isUseHardLinksEnabled();
+ m_copySaves = prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+
if (!filters.isEmpty())
{
// Set regex filter:
@@ -25,11 +30,71 @@ void InstanceCopyTask::executeTask()
{
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]{
- FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
- folderCopy.followSymlinks(false).matcher(m_matcher.get());
+ auto copySaves = [&](){
+ FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
+ savesCopy.followSymlinks(false);
+
+ return savesCopy();
+ };
+
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
+ if (m_useLinks) {
+ FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
+ folderLink.linkRecursively(true).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
+
+ bool there_were_errors = false;
+
+ if(!folderLink()){
+#if defined Q_OS_WIN32
+ if (!m_useHardLinks) {
+ qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
+
+ qDebug() << "atempting to run with privelage";
+
+ QEventLoop loop;
+ bool got_priv_results = false;
+
+ connect(&folderLink, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
+ if (!gotResults) {
+ qDebug() << "Privlaged run exited without results!";
+ }
+ got_priv_results = gotResults;
+ loop.quit();
+ });
+ folderLink.runPrivlaged();
+
+ loop.exec(); // wait for the finished signal
+
+ for (auto result : folderLink.getResults()) {
+ if (result.err_value != 0) {
+ there_were_errors = true;
+ }
+ }
+
+ if (m_copySaves) {
+ there_were_errors |= !copySaves();
+ }
+
+ 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();
+#endif return false;
+ }
+
+ if (m_copySaves) {
+ there_were_errors |= !copySaves();
+ }
+
+ return !there_were_errors;
+ } else {
+ FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
+ folderCopy.followSymlinks(false).matcher(m_matcher.get());
- return folderCopy();
+ return folderCopy();
+ }
});
connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &InstanceCopyTask::copyAborted);
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index 1f29b854..d9651b07 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -30,4 +30,7 @@ private:
QFutureWatcher m_copyFutureWatcher;
std::unique_ptr m_matcher;
bool m_keepPlaytime;
+ bool m_useLinks = false;
+ bool m_useHardLinks = false;
+ bool m_copySaves = true;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 981352ae..e477b4b3 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -88,7 +88,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
- ui->linkWorldsCheckbox->setChecked(m_selectedOptions.isLinkWorldsEnabled());
+ ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -179,6 +179,7 @@ void CopyInstanceDialog::on_selectAllCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableCopySaves(state == Qt::Checked);
+ ui->dontLinkSavesCheckbox->setChecked((state == Qt::Checked) && ui->dontLinkSavesCheckbox->isChecked());
updateSelectAllCheckbox();
}
@@ -235,7 +236,7 @@ void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
}
-void CopyInstanceDialog::on_linkWorldsCheckbox_stateChanged(int state)
+void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
- m_selectedOptions.enableLinkWorlds(state == Qt::Checked);
+ m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index a80faab9..57775925 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -57,7 +57,7 @@ slots:
void on_copyScreenshotsCheckbox_stateChanged(int state);
void on_linkFilesGroup_toggled(bool checked);
void on_hardLinksCheckbox_stateChanged(int state);
- void on_linkWorldsCheckbox_stateChanged(int state);
+ void on_dontLinkSavesCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index e41ad526..d8eb96eb 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -240,17 +240,14 @@
-
-
+
- World save data will be linked and thus shared between instances.
+ If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
- Link worlds
+ Don't link saves
- true
-
-
false
--
cgit
From 1bed7754e0bf3c009a38818963fe8d0832b36852 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 18:39:17 -0700
Subject: feat(symlinks): make recursive links explicit
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/InstanceCopyPrefs.cpp | 10 ++++++++++
launcher/InstanceCopyPrefs.h | 3 +++
launcher/InstanceCopyTask.cpp | 10 ++++++----
launcher/InstanceCopyTask.h | 3 ++-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 14 ++++++++++++++
launcher/ui/dialogs/CopyInstanceDialog.h | 1 +
launcher/ui/dialogs/CopyInstanceDialog.ui | 25 +++++++++++++++++++++++--
7 files changed, 59 insertions(+), 7 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index e363d4c6..59825ced 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -103,6 +103,11 @@ bool InstanceCopyPrefs::isUseHardLinksEnabled() const
return useHardLinks;
}
+bool InstanceCopyPrefs::isLinkRecursivelyEnabled() const
+{
+ return linkRecursively;
+}
+
bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
{
return dontLinkSaves;
@@ -154,6 +159,11 @@ void InstanceCopyPrefs::enableLinkFiles(bool b)
linkFiles = b;
}
+void InstanceCopyPrefs::enableLinkRecursively(bool b)
+{
+ linkRecursively = b;
+}
+
void InstanceCopyPrefs::enableUseHardLinks(bool b)
{
useHardLinks = b;
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 61719a06..9fc9dcab 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -20,6 +20,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyModsEnabled() const;
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
[[nodiscard]] bool isLinkFilesEnabled() const;
+ [[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
// Setters
@@ -32,6 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyMods(bool b);
void enableCopyScreenshots(bool b);
void enableLinkFiles(bool b);
+ void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
@@ -45,6 +47,7 @@ struct InstanceCopyPrefs {
bool copyMods = true;
bool copyScreenshots = true;
bool linkFiles = false;
+ bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 31c6bdca..81502d89 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -12,9 +12,11 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
QString filters = prefs.getSelectedFiltersAsRegex();
+
m_useLinks = prefs.isLinkFilesEnabled();
- m_useHardLinks = prefs.isUseHardLinksEnabled();
- m_copySaves = prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+ m_linkRecursively = prefs.isLinkRecursivelyEnabled();
+ m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
+ m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
if (!filters.isEmpty())
{
@@ -32,7 +34,7 @@ void InstanceCopyTask::executeTask()
auto copySaves = [&](){
FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
- savesCopy.followSymlinks(false);
+ savesCopy.followSymlinks(true);
return savesCopy();
};
@@ -40,7 +42,7 @@ void InstanceCopyTask::executeTask()
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
if (m_useLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
- folderLink.linkRecursively(true).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
+ folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
bool there_were_errors = false;
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index d9651b07..3dce1662 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -32,5 +32,6 @@ private:
bool m_keepPlaytime;
bool m_useLinks = false;
bool m_useHardLinks = false;
- bool m_copySaves = true;
+ bool m_copySaves = false;
+ bool m_linkRecursively = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index e477b4b3..55962c5a 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -87,6 +87,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
+ ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
}
@@ -231,9 +232,22 @@ void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
m_selectedOptions.enableLinkFiles(checked);
}
+void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
+ if (state != Qt::Checked) {
+ ui->hardLinksCheckbox->setChecked(false);
+ ui->dontLinkSavesCheckbox->setChecked(false);
+ }
+
+}
+
void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
{
m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+ if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
+ ui->recursiveLinkCheckbox->setChecked(true);
+ }
}
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 57775925..2fc6f38a 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -56,6 +56,7 @@ slots:
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
void on_linkFilesGroup_toggled(bool checked);
+ void on_recursiveLinkCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index d8eb96eb..8df0d3db 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -209,6 +209,16 @@
+ -
+
+
+ Advanced Copy Options
+
+
+ Qt::AlignCenter
+
+
+
-
-
@@ -229,8 +239,18 @@
false
+
-
+
+
+ Link files recursively
+
+
+
-
+
+ false
+
Use hard links instead of symbolic links
@@ -242,7 +262,7 @@
-
- If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
+ If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
Don't link saves
@@ -283,8 +303,9 @@
copyResPacksCheckbox
copyModsCheckbox
linkFilesGroup
+ recursiveLinkCheckbox
hardLinksCheckbox
- linkWorldsCheckbox
+ dontLinkSavesCheckbox
--
cgit
From c9105e525e175ee8181ab0a6998d0e21526f116d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 18:52:50 -0700
Subject: fix: follow symlinks when exporting
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.h | 2 +-
launcher/MMCZip.cpp | 9 ++++++---
launcher/MMCZip.h | 6 ++++--
launcher/ui/dialogs/ExportInstanceDialog.cpp | 5 ++++-
4 files changed, 15 insertions(+), 7 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index d79096e6..782a2f40 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -144,7 +144,7 @@ class ExternalLinkFileProcess : public QThread {
Q_OBJECT
public:
ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr)
- : QThread(parent), m_server(server), m_useHardLinks(useHardLinks)
+ : QThread(parent), m_useHardLinks(useHardLinks), m_server(server)
{}
void run() override
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index 1eda43fe..b4b663c1 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -94,20 +94,23 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet &containe
return true;
}
-bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files)
+bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks)
{
QDir directory(dir);
if (!directory.exists()) return false;
for (auto e : files) {
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
- if( !JlCompress::compressFile(zip, e.absoluteFilePath(), filePath)) return false;
+ auto srcPath = e.absoluteFilePath();
+ if (followSymlinks)
+ srcPath = e.canonicalFilePath();
+ if( !JlCompress::compressFile(zip, srcPath, filePath)) return false;
}
return true;
}
-bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files)
+bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
{
QuaZip zip(fileCompressed);
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h
index 81f9cb90..2a78f830 100644
--- a/launcher/MMCZip.h
+++ b/launcher/MMCZip.h
@@ -59,18 +59,20 @@ namespace MMCZip
* \param zip target archive
* \param dir directory that will be compressed (to compress with relative paths)
* \param files list of files to compress
+ * \param followSymlinks should follow symlinks when compressing file data
* \return true for success or false for failure
*/
- bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files);
+ bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false);
/**
* Compress directory, by providing a list of files to compress
* \param fileCompressed target archive file
* \param dir directory that will be compressed (to compress with relative paths)
* \param files list of files to compress
+ * \param followSymlinks should follow symlinks when compressing file data
* \return true for success or false for failure
*/
- bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files);
+ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
/**
* take a source jar, add mods to it, resulting in target jar
diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp
index f13e36e8..07ec3c70 100644
--- a/launcher/ui/dialogs/ExportInstanceDialog.cpp
+++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp
@@ -45,6 +45,8 @@
#include
#include
#include
+#include
+
#include "StringUtils.h"
#include "SeparatorPrefixTree.h"
#include "Application.h"
@@ -429,7 +431,8 @@ bool ExportInstanceDialog::doExport()
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
}
- if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files))
+
+ if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files, true))
{
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
--
cgit
From 397e7f036339b09569317300423261f2b37d6119 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 9 Feb 2023 02:02:40 -0700
Subject: feat(reflink): hook up relink / clone on the copy dialog
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 26 ++++++++++++++----
launcher/FileSystem.h | 3 +++
launcher/InstanceCopyPrefs.cpp | 10 +++++++
launcher/InstanceCopyPrefs.h | 3 +++
launcher/InstanceCopyTask.cpp | 11 ++++++--
launcher/InstanceCopyTask.h | 1 +
launcher/ui/dialogs/CopyInstanceDialog.cpp | 28 ++++++++++++++++++-
launcher/ui/dialogs/CopyInstanceDialog.h | 5 ++++
launcher/ui/dialogs/CopyInstanceDialog.ui | 43 +++++++++++++++++++++++++++---
9 files changed, 119 insertions(+), 11 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 7b7fc80b..fd09842f 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -103,6 +103,7 @@ namespace fs = ghc::filesystem;
#include /* Definition of FICLONE* constants */
#include
#include
+#include
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include
#include
@@ -880,6 +881,9 @@ FilesystemInfo statFS(QString path)
info.name = storage_info.name();
info.rootPath = storage_info.rootPath();
+ qDebug() << "Pulling filesystem info for" << info.rootPath;
+ qDebug() << "Filesystem: " << fsTypeName << "detected" << getFilesystemTypeName(info.fsType);
+
return info;
}
@@ -995,33 +999,45 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
// https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html
int src_fd = open(src_path.c_str(), O_RDONLY);
- if(!src_fd) {
+ if(src_fd == -1) {
qWarning() << "Failed to open file:" << src_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
return false;
}
- int dst_fd = open(dst_path.c_str(), O_WRONLY | O_TRUNC);
- if(!dst_fd) {
+ int dst_fd = open(dst_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if(dst_fd == -1) {
qWarning() << "Failed to open file:" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
+ close(src_fd);
return false;
}
// attempt to clone
- if(!ioctl(dst_fd, FICLONE, src_fd)){
+ if(ioctl(dst_fd, FICLONE, src_fd) == -1){
qWarning() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
+ close(src_fd);
+ close(dst_fd);
return false;
}
+ if(close(src_fd)) {
+ qWarning() << "Failed to close file:" << src_path.c_str();
+ qDebug() << "Error:" << strerror(errno);
+ }
+ if(close(dst_fd)) {
+ qWarning() << "Failed to close file:" << dst_path.c_str();
+ qDebug() << "Error:" << strerror(errno);
+ }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
// TODO: use clonefile
// clonefile(const char * src, const char * dst, int flags);
// https://www.manpagez.com/man/2/clonefile/
- if (!clonefile(src_path.c_str(), dst_path.c_str(), 0)) {
+ qDebug() << "attempting file clone via clonefile" << src << "to" << dst;
+ if (clonefile(src_path.c_str(), dst_path.c_str(), 0) == -1) {
qWarning() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 531036dd..aa28de93 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -329,6 +329,7 @@ enum class FilesystemType {
HFS,
HFSPLUS,
HFSX,
+ FUSEBLK,
UNKNOWN
};
@@ -346,6 +347,7 @@ static const QMap s_filesystem_type_names = {
{FilesystemType::HFS, QString("HFS")},
{FilesystemType::HFSPLUS, QString("HFSPLUS")},
{FilesystemType::HFSX, QString("HFSX")},
+ {FilesystemType::FUSEBLK, QString("FUSEBLK")},
{FilesystemType::UNKNOWN, QString("UNKNOWN")}
};
@@ -365,6 +367,7 @@ static const QMap s_filesystem_type_names_inverse = {
{QString("HFSPLUS"), FilesystemType::HFSPLUS},
{QString("HFSX"), FilesystemType::HFSX},
{QString("HFS"), FilesystemType::HFS},
+ {QString("FUSEBLK"), FilesystemType::FUSEBLK},
{QString("UNKNOWN"), FilesystemType::UNKNOWN}
};
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index 59825ced..f2aa5e69 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -113,6 +113,11 @@ bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
return dontLinkSaves;
}
+bool InstanceCopyPrefs::isUseCloneEnabled() const
+{
+ return useClone;
+}
+
// ======= Setters =======
void InstanceCopyPrefs::enableCopySaves(bool b)
{
@@ -173,3 +178,8 @@ void InstanceCopyPrefs::enableDontLinkSaves(bool b)
{
dontLinkSaves = b;
}
+
+void InstanceCopyPrefs::enableUseClone(bool b)
+{
+ useClone = b;
+}
\ No newline at end of file
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 9fc9dcab..583fdd4c 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -23,6 +23,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
+ [[nodiscard]] bool isUseCloneEnabled() const;
// Setters
void enableCopySaves(bool b);
void enableKeepPlaytime(bool b);
@@ -36,6 +37,7 @@ struct InstanceCopyPrefs {
void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
+ void enableUseClone(bool b);
protected: // data
bool copySaves = true;
@@ -50,4 +52,5 @@ struct InstanceCopyPrefs {
bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
+ bool useClone = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 81502d89..b3ea54b0 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -17,6 +17,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+ m_useClone = prefs.isUseCloneEnabled();
if (!filters.isEmpty())
{
@@ -40,7 +41,12 @@ void InstanceCopyTask::executeTask()
};
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
- if (m_useLinks) {
+ if (m_useClone) {
+ FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
+ folderClone.matcher(m_matcher.get());
+
+ return folderClone();
+ } else if (m_useLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
@@ -83,7 +89,8 @@ void InstanceCopyTask::executeTask()
}
#else
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
-#endif return false;
+#endif
+ return false;
}
if (m_copySaves) {
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index 3dce1662..aea9d99a 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -34,4 +34,5 @@ private:
bool m_useHardLinks = false;
bool m_copySaves = false;
bool m_linkRecursively = false;
+ bool m_useClone = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 55962c5a..c51bc067 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -46,6 +46,7 @@
#include "icons/IconList.h"
#include "BaseInstance.h"
#include "InstanceList.h"
+#include "FileSystem.h"
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
@@ -85,11 +86,22 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled());
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
-
+
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
+
+ auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+ m_cloneSupported = FS::canCloneOnFS(detectedOS);
+
+ if (m_cloneSupported) {
+ ui->cloneSupportedLabel->setText(tr("Clone / Reflink is supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ } else {
+ ui->cloneSupportedLabel->setText(tr("Clone / Reflink not supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ }
+
+ updateUseCloneCheckbox();
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -152,6 +164,12 @@ void CopyInstanceDialog::updateSelectAllCheckbox()
ui->selectAllCheckbox->blockSignals(false);
}
+void CopyInstanceDialog::updateUseCloneCheckbox()
+{
+ ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->linkFilesGroup->isChecked());
+ ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
+}
+
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
@@ -230,6 +248,7 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
{
m_selectedOptions.enableLinkFiles(checked);
+ updateUseCloneCheckbox();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
@@ -254,3 +273,10 @@ void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
}
+
+void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
+ ui->linkFilesGroup->setEnabled(!m_selectedOptions.isUseCloneEnabled());
+ updateUseCloneCheckbox();
+}
\ No newline at end of file
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 2fc6f38a..859c643c 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -16,6 +16,7 @@
#pragma once
#include
+#include "BaseInstance.h"
#include "BaseVersion.h"
#include "InstanceCopyPrefs.h"
@@ -59,13 +60,17 @@ slots:
void on_recursiveLinkCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
+ void on_useCloneCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
+ void updateUseCloneCheckbox();
+
/* data */
Ui::CopyInstanceDialog *ui;
QString InstIconKey;
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
+ bool m_cloneSupported = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 8df0d3db..ce8657f6 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 525
- 581
+ 531
+ 640
@@ -275,6 +275,44 @@
+ -
+
+
+ Clone / Reflink (Copy On Write) Options
+
+
+
-
+
+
+ false
+
+
+ Use Clone / Reflink
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ Clone / Reflink not supported on this filesystem
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 4
+
+
+
+
+
+
-
@@ -302,7 +340,6 @@
copyServersCheckbox
copyResPacksCheckbox
copyModsCheckbox
- linkFilesGroup
recursiveLinkCheckbox
hardLinksCheckbox
dontLinkSavesCheckbox
--
cgit
From bc8336a4b115fd190e068f57159d925683ba3930 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 9 Feb 2023 16:19:38 -0700
Subject: fix: cleanup UI, detect FAT and turn off links
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/DesktopServices.cpp | 1 -
launcher/FileSystem.cpp | 28 ++++++++
launcher/FileSystem.h | 24 ++++++-
launcher/InstanceCopyPrefs.cpp | 8 +--
launcher/InstanceCopyPrefs.h | 6 +-
launcher/InstanceCopyTask.cpp | 2 +-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 50 +++++++++----
launcher/ui/dialogs/CopyInstanceDialog.h | 6 +-
launcher/ui/dialogs/CopyInstanceDialog.ui | 111 ++++++++++++++++++++---------
9 files changed, 175 insertions(+), 61 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp
index 69770e99..2984a1b4 100644
--- a/launcher/DesktopServices.cpp
+++ b/launcher/DesktopServices.cpp
@@ -37,7 +37,6 @@
#include
#include
#include
-//#include "Application.h"
/**
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index fd09842f..c363f6ea 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -1053,4 +1053,32 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
return true;
}
+
+/**
+ * @brief if the Filesystem is symlink capable
+ *
+ */
+bool canLinkOnFS(const QString& path)
+{
+ FilesystemInfo info = statFS(path);
+ return canLinkOnFS(info);
+}
+bool canLinkOnFS(const FilesystemInfo& info)
+{
+ return canLinkOnFS(info.fsType);
+}
+bool canLinkOnFS(FilesystemType type)
+{
+ return !s_non_link_filesystems.contains(type);
+}
+/**
+ * @brief if the Filesystem is symlink capable on both ends
+ *
+ */
+bool canLink(const QString& src, const QString& dst)
+{
+ return canLinkOnFS(src) && canLinkOnFS(dst);
+}
+
+
}
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index aa28de93..83ff99a4 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -330,6 +330,7 @@ enum class FilesystemType {
HFSPLUS,
HFSX,
FUSEBLK,
+ F2FS,
UNKNOWN
};
@@ -348,6 +349,7 @@ static const QMap s_filesystem_type_names = {
{FilesystemType::HFSPLUS, QString("HFSPLUS")},
{FilesystemType::HFSX, QString("HFSX")},
{FilesystemType::FUSEBLK, QString("FUSEBLK")},
+ {FilesystemType::F2FS, QString("F2FS")},
{FilesystemType::UNKNOWN, QString("UNKNOWN")}
};
@@ -368,6 +370,7 @@ static const QMap s_filesystem_type_names_inverse = {
{QString("HFSX"), FilesystemType::HFSX},
{QString("HFS"), FilesystemType::HFS},
{QString("FUSEBLK"), FilesystemType::FUSEBLK},
+ {QString("F2FS"), FilesystemType::F2FS},
{QString("UNKNOWN"), FilesystemType::UNKNOWN}
};
@@ -405,7 +408,7 @@ bool canCloneOnFS(const FilesystemInfo& info);
bool canCloneOnFS(FilesystemType type);
/**
- * @brief if the Filesystem is reflink/clone capable and both are on the same device
+ * @brief if the Filesystems are reflink/clone capable and both are on the same device
*
*/
bool canClone(const QString& src, const QString& dst);
@@ -457,4 +460,23 @@ class clone : public QObject {
*/
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
+
+static const QList s_non_link_filesystems = {
+ FilesystemType::FAT,
+};
+
+/**
+ * @brief if the Filesystem is symlink capable
+ *
+ */
+bool canLinkOnFS(const QString& path);
+bool canLinkOnFS(const FilesystemInfo& info);
+bool canLinkOnFS(FilesystemType type);
+
+/**
+ * @brief if the Filesystem is symlink capable on both ends
+ *
+ */
+bool canLink(const QString& src, const QString& dst);
+
}
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index f2aa5e69..c03d0aae 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -93,9 +93,9 @@ bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
return copyScreenshots;
}
-bool InstanceCopyPrefs::isLinkFilesEnabled() const
+bool InstanceCopyPrefs::isUseSymLinksEnabled() const
{
- return linkFiles;
+ return useSymLinks;
}
bool InstanceCopyPrefs::isUseHardLinksEnabled() const
@@ -159,9 +159,9 @@ void InstanceCopyPrefs::enableCopyScreenshots(bool b)
copyScreenshots = b;
}
-void InstanceCopyPrefs::enableLinkFiles(bool b)
+void InstanceCopyPrefs::enableUseSymLinks(bool b)
{
- linkFiles = b;
+ useSymLinks = b;
}
void InstanceCopyPrefs::enableLinkRecursively(bool b)
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 583fdd4c..14ae9551 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -19,7 +19,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyServersEnabled() const;
[[nodiscard]] bool isCopyModsEnabled() const;
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
- [[nodiscard]] bool isLinkFilesEnabled() const;
+ [[nodiscard]] bool isUseSymLinksEnabled() const;
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
@@ -33,7 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyServers(bool b);
void enableCopyMods(bool b);
void enableCopyScreenshots(bool b);
- void enableLinkFiles(bool b);
+ void enableUseSymLinks(bool b);
void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
@@ -48,7 +48,7 @@ struct InstanceCopyPrefs {
bool copyServers = true;
bool copyMods = true;
bool copyScreenshots = true;
- bool linkFiles = false;
+ bool useSymLinks = false;
bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index b3ea54b0..ba0052fa 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -13,7 +13,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
QString filters = prefs.getSelectedFiltersAsRegex();
- m_useLinks = prefs.isLinkFilesEnabled();
+ m_useLinks = prefs.isUseSymLinksEnabled();
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index c51bc067..c6cbefcf 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -87,21 +87,26 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
- ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
- ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
+ ui->symbolicLinksCheckbox->setChecked(m_selectedOptions.isUseSymLinksEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
+
+ ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+
m_cloneSupported = FS::canCloneOnFS(detectedOS);
+ m_linkSupported = FS::canLinkOnFS(detectedOS);
if (m_cloneSupported) {
- ui->cloneSupportedLabel->setText(tr("Clone / Reflink is supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
} else {
- ui->cloneSupportedLabel->setText(tr("Clone / Reflink not supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
}
+ updateLinkOptions();
updateUseCloneCheckbox();
+
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -170,6 +175,21 @@ void CopyInstanceDialog::updateUseCloneCheckbox()
ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
}
+void CopyInstanceDialog::updateLinkOptions()
+{
+ ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked());
+ ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked());
+
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled());
+ ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled());
+
+ bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
+ ui->recursiveLinkCheckbox->setEnabled(m_linkSupported && linksInUse && !ui->hardLinksCheckbox->isChecked());
+ ui->dontLinkSavesCheckbox->setEnabled(m_linkSupported && linksInUse);
+ ui->recursiveLinkCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isLinkRecursivelyEnabled());
+ ui->dontLinkSavesCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isDontLinkSavesEnabled());
+}
+
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
@@ -245,10 +265,20 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
updateSelectAllCheckbox();
}
-void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
+void CopyInstanceDialog::on_symbolicLinksCheckbox_stateChanged(int state)
{
- m_selectedOptions.enableLinkFiles(checked);
+ m_selectedOptions.enableUseSymLinks(state == Qt::Checked);
updateUseCloneCheckbox();
+ updateLinkOptions();
+}
+
+void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+ if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
+ ui->recursiveLinkCheckbox->setChecked(true);
+ }
+ updateLinkOptions();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
@@ -261,14 +291,6 @@ void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
}
-void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
-{
- m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
- if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
- ui->recursiveLinkCheckbox->setChecked(true);
- }
-}
-
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 859c643c..2dea3795 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -56,9 +56,9 @@ slots:
void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
- void on_linkFilesGroup_toggled(bool checked);
- void on_recursiveLinkCheckbox_stateChanged(int state);
+ void on_symbolicLinksCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
+ void on_recursiveLinkCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
void on_useCloneCheckbox_stateChanged(int state);
@@ -66,6 +66,7 @@ private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
void updateUseCloneCheckbox();
+ void updateLinkOptions();
/* data */
Ui::CopyInstanceDialog *ui;
@@ -73,4 +74,5 @@ private:
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
bool m_cloneSupported = false;
+ bool m_linkSupported = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index ce8657f6..7bf75c2d 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 531
- 640
+ 527
+ 699
@@ -138,7 +138,7 @@
-
- Instance copy options
+ Instance Copy Options
-
@@ -224,53 +224,92 @@
-
- Use symbolic links instead of copying files.
+ Use symbolic or hard links instead of copying files.
- Link files instead of copying them
+ Symbolic and Hard Link Options
false
- true
+ false
false
-
-
+
- Link files recursively
-
-
-
- -
-
-
- false
+ Links are supported on most filesystems except FAT
-
- Use hard links instead of symbolic links
-
-
- Use hard links
+
+ Qt::AlignCenter
-
-
-
- If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
+
+
+ 6
-
- Don't link saves
+
+ 6
-
- false
+
+ 6
-
+
+ 6
+
+
-
+
+
+ true
+
+
+ Use hard links instead of symbolic links
+
+
+ Use hard links
+
+
+
+ -
+
+
+ false
+
+
+ Link files recursively
+
+
+
+ -
+
+
+ false
+
+
+ If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
+
+
+ Don't link saves
+
+
+ false
+
+
+
+ -
+
+
+ Use symbloic links
+
+
+
+
@@ -278,7 +317,7 @@
-
- Clone / Reflink (Copy On Write) Options
+ CoW (Copy-on-Write) Options
-
@@ -287,7 +326,7 @@
false
- Use Clone / Reflink
+ Clone instead of copying
@@ -300,7 +339,7 @@
- Clone / Reflink not supported on this filesystem
+ Your filesystem and/or OS doesn't support reflinks
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
@@ -340,9 +379,11 @@
copyServersCheckbox
copyResPacksCheckbox
copyModsCheckbox
+ symbolicLinksCheckbox
recursiveLinkCheckbox
hardLinksCheckbox
dontLinkSavesCheckbox
+ useCloneCheckbox
@@ -353,8 +394,8 @@
accept()
- 263
- 571
+ 269
+ 692
157
@@ -369,8 +410,8 @@
reject()
- 331
- 571
+ 337
+ 692
286
--
cgit
From 2837236d81b882f041a1cefadc86ca9d5f09ceeb Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 9 Feb 2023 19:48:40 -0700
Subject: fix: intelegent recursive links & symlink follow on export
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 66 ++++++++++++-
launcher/FileSystem.h | 28 +++++-
launcher/InstanceCopyPrefs.cpp | 9 ++
launcher/InstanceCopyPrefs.h | 1 +
launcher/InstanceCopyTask.cpp | 16 +++-
launcher/MMCZip.cpp | 11 ++-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 21 ++--
tests/FileSystem_test.cpp | 148 +++++++++++++++++++++++++++++
8 files changed, 278 insertions(+), 22 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index c363f6ea..4ee3899b 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -242,6 +242,14 @@ bool copy::operator()(const QString& offset, bool dryRun)
return err.value() == 0;
}
+/// qDebug print support for the LinkPair struct
+QDebug operator<<(QDebug debug, const LinkPair& lp)
+{
+ QDebugStateSaver saver(debug);
+
+ debug.nospace() << "LinkPair{ src: " << lp.src << " , dst: " << lp.dst << " }";
+ return debug;
+}
bool create_link::operator()(const QString& offset, bool dryRun)
{
@@ -265,7 +273,7 @@ bool create_link::operator()(const QString& offset, bool dryRun)
* @param offset subdirectory form src to link to dest
* @return if there was an error during the attempt to link
*/
-void create_link::make_link_list( const QString& offset)
+void create_link::make_link_list(const QString& offset)
{
for (auto pair : m_path_pairs) {
const QString& srcPath = pair.src;
@@ -297,14 +305,26 @@ void create_link::make_link_list( const QString& offset)
link_file(src, "");
} else {
if (m_debug)
- qDebug() << "linking recursivly:" << src << "to" << dst;
+ qDebug() << "linking recursivly:" << src << "to" << dst << "max_depth:" << m_max_depth;
QDir src_dir(src);
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
+ QStringList linkedPaths;
+
while (source_it.hasNext()) {
auto src_path = source_it.next();
auto relative_path = src_dir.relativeFilePath(src_path);
+ if (m_max_depth >= 0 && PathDepth(relative_path) > m_max_depth){
+ relative_path = PathTruncate(relative_path, m_max_depth);
+ src_path = src_dir.filePath(relative_path);
+ if (linkedPaths.contains(src_path)) {
+ continue;
+ }
+ }
+
+ linkedPaths.append(src_path);
+
link_file(src_path, relative_path);
}
}
@@ -312,7 +332,7 @@ void create_link::make_link_list( const QString& offset)
}
bool create_link::make_links()
-{
+{
for (auto link : m_links_to_make) {
QString src_path = link.src;
@@ -556,11 +576,49 @@ QString PathCombine(const QString& path1, const QString& path2, const QString& p
return PathCombine(PathCombine(path1, path2, path3), path4);
}
-QString AbsolutePath(QString path)
+QString AbsolutePath(const QString& path)
{
return QFileInfo(path).absolutePath();
}
+int PathDepth(const QString& path)
+{
+ if (path.isEmpty()) return 0;
+
+ QFileInfo info(path);
+
+ auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts);
+
+ int numParts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts).length();
+ numParts -= parts.count(".");
+ numParts -= parts.count("..") * 2;
+
+ return numParts;
+}
+
+QString PathTruncate(const QString& path, int depth)
+{
+ if (path.isEmpty() || (depth < 0) ) return "";
+
+ QString trunc = QFileInfo(path).path();
+
+ if (PathDepth(trunc) > depth ) {
+ return PathTruncate(trunc, depth);
+ }
+
+ auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), Qt::SkipEmptyParts);
+ if (parts.startsWith(".") && !path.startsWith(".")) {
+ parts.removeFirst();
+ }
+ if (path.startsWith(QDir::separator())) {
+ parts.prepend("");
+ }
+
+ trunc = parts.join(QDir::separator());
+
+ return trunc;
+}
+
QString ResolveExecutable(QString path)
{
if (path.isEmpty()) {
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 83ff99a4..7485206a 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -200,6 +200,11 @@ class create_link : public QObject {
m_recursive = recursive;
return *this;
}
+ create_link& setMaxDepth(int depth)
+ {
+ m_max_depth = depth;
+ return *this;
+ }
create_link& debug(bool d)
{
m_debug = d;
@@ -239,6 +244,9 @@ class create_link : public QObject {
bool m_whitelist = false;
bool m_recursive = true;
+ /// @brief >= -1 = infinite, 0 = link files at src/* to dest/*, 1 = link files at src/*/* to dest/*/*, etc.
+ int m_max_depth = -1;
+
QList m_path_pairs;
QList m_path_results;
QList m_links_to_make;
@@ -272,7 +280,25 @@ QString PathCombine(const QString& path1, const QString& path2);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
-QString AbsolutePath(QString path);
+QString AbsolutePath(const QString& path);
+
+/**
+ * @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
+ *
+ * @param path path to measure
+ * @return int number of componants before base path
+ */
+int PathDepth(const QString& path);
+
+
+/**
+ * @brief cut off segments of path untill it is a max of length depth
+ *
+ * @param path path to truncate
+ * @param depth max depth of new path
+ * @return QString truncated path
+ */
+QString PathTruncate(const QString& path, int depth);
/**
* Resolve an executable
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index c03d0aae..0650002b 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -16,8 +16,13 @@ bool InstanceCopyPrefs::allTrue() const
copyScreenshots;
}
+
// Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat")
QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
+{
+ return getSelectedFiltersAsRegex({});
+}
+QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& additionalFilters) const
{
QStringList filters;
@@ -42,6 +47,10 @@ QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
if(!copyScreenshots)
filters << "screenshots";
+ for (auto filter : additionalFilters) {
+ filters << filter;
+ }
+
// If we have any filters to add, join them as a single regex string to return:
if (!filters.isEmpty()) {
const QString MC_ROOT = "[.]?minecraft/";
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 14ae9551..c7bde068 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -10,6 +10,7 @@ struct InstanceCopyPrefs {
public:
[[nodiscard]] bool allTrue() const;
[[nodiscard]] QString getSelectedFiltersAsRegex() const;
+ [[nodiscard]] QString getSelectedFiltersAsRegex(const QStringList& additionalFilters) const;
// Getters
[[nodiscard]] bool isCopySavesEnabled() const;
[[nodiscard]] bool isKeepPlaytimeEnabled() const;
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index ba0052fa..40babd0f 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -4,13 +4,14 @@
#include "NullInstance.h"
#include "pathmatcher/RegexpMatcher.h"
#include
+#include
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
{
m_origInstance = origInstance;
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
- QString filters = prefs.getSelectedFiltersAsRegex();
+
m_useLinks = prefs.isUseSymLinksEnabled();
@@ -18,6 +19,14 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
m_useClone = prefs.isUseCloneEnabled();
+
+ QString filters = prefs.getSelectedFiltersAsRegex();
+ if (m_useLinks || m_useHardLinks) {
+ if (!filters.isEmpty()) filters += "|";
+ filters += "instance.cfg";
+ }
+
+ qDebug() << "CopyFilters:" << filters;
if (!filters.isEmpty())
{
@@ -46,9 +55,10 @@ void InstanceCopyTask::executeTask()
folderClone.matcher(m_matcher.get());
return folderClone();
- } else if (m_useLinks) {
+ } else if (m_useLinks || m_useHardLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
- folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
+ 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());
bool there_were_errors = false;
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index b4b663c1..1a336375 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -102,8 +102,13 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo
for (auto e : files) {
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
auto srcPath = e.absoluteFilePath();
- if (followSymlinks)
- srcPath = e.canonicalFilePath();
+ if (followSymlinks) {
+ if (e.isSymLink()) {
+ srcPath = e.symLinkTarget();
+ } else {
+ srcPath = e.canonicalFilePath();
+ }
+ }
if( !JlCompress::compressFile(zip, srcPath, filePath)) return false;
}
@@ -119,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
return false;
}
- auto result = compressDirFiles(&zip, dir, files);
+ auto result = compressDirFiles(&zip, dir, files, followSymlinks);
zip.close();
if(zip.getZipError()!=0) {
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index c6cbefcf..9fe129f1 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -171,17 +171,18 @@ void CopyInstanceDialog::updateSelectAllCheckbox()
void CopyInstanceDialog::updateUseCloneCheckbox()
{
- ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->linkFilesGroup->isChecked());
- ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
+ ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->hardLinksCheckbox->isChecked());
+ ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled() && !ui->symbolicLinksCheckbox->isChecked() &&
+ !ui->hardLinksCheckbox->isChecked());
}
void CopyInstanceDialog::updateLinkOptions()
{
- ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked());
- ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked());
+ ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
+ ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
- ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled());
- ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled());
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() && !ui->useCloneCheckbox->isChecked());
+ ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled() && !ui->useCloneCheckbox->isChecked());
bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
ui->recursiveLinkCheckbox->setEnabled(m_linkSupported && linksInUse && !ui->hardLinksCheckbox->isChecked());
@@ -278,16 +279,14 @@ void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
ui->recursiveLinkCheckbox->setChecked(true);
}
+ updateUseCloneCheckbox();
updateLinkOptions();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
{
m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
- if (state != Qt::Checked) {
- ui->hardLinksCheckbox->setChecked(false);
- ui->dontLinkSavesCheckbox->setChecked(false);
- }
+ updateLinkOptions();
}
@@ -299,6 +298,6 @@ void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
{
m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
- ui->linkFilesGroup->setEnabled(!m_selectedOptions.isUseCloneEnabled());
updateUseCloneCheckbox();
+ updateLinkOptions();
}
\ No newline at end of file
diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp
index 4ccc4003..4418dd62 100644
--- a/tests/FileSystem_test.cpp
+++ b/tests/FileSystem_test.cpp
@@ -57,6 +57,11 @@ class LinkTask : public Task {
m_lnk->whitelist(b);
}
+ void setMaxDepth(int depth)
+ {
+ m_lnk->setMaxDepth(depth);
+ }
+
private:
void executeTask() override
{
@@ -630,6 +635,149 @@ slots:
QVERIFY(target_dir.entryList(filter).contains("pack.mcmeta"));
}
}
+
+ void test_link_with_max_depth()
+ {
+ QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
+ auto f = [&folder, this]()
+ {
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(true);
+ qDebug() << "From:" << folder << "To:" << tempDir.path();
+
+ QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
+ qDebug() << tempDir.path();
+ qDebug() << target_dir.path();
+
+ LinkTask lnk_tsk(folder, target_dir.path());
+ lnk_tsk.linkRecursively(true);
+ lnk_tsk.setMaxDepth(0);
+ QObject::connect(&lnk_tsk, &Task::finished, [&]{
+ QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ });
+ lnk_tsk.start();
+
+ QVERIFY2(QTest::qWaitFor([&]() {
+ return lnk_tsk.isFinished();
+ }, 100000), "Task didn't finish as it should.");
+
+ QVERIFY(!QFileInfo(target_dir.path()).isSymLink());
+
+ auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
+ for(auto entry: target_dir.entryList(filter))
+ {
+ qDebug() << entry;
+ if (entry == "." || entry == "..") continue;
+ QFileInfo entry_lnk_info(target_dir.filePath(entry));
+ QVERIFY(entry_lnk_info.isSymLink());
+ }
+
+ QFileInfo lnk_info(target_dir.path());
+ QVERIFY(lnk_info.exists());
+ QVERIFY(!lnk_info.isSymLink());
+
+ QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+
+
+ };
+
+ // first try variant without trailing /
+ QVERIFY(!folder.endsWith('/'));
+ f();
+
+ // then variant with trailing /
+ folder.append('/');
+ QVERIFY(folder.endsWith('/'));
+ f();
+ }
+
+ void test_link_with_no_max_depth()
+ {
+ QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
+ auto f = [&folder]()
+ {
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(true);
+ qDebug() << "From:" << folder << "To:" << tempDir.path();
+
+ QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
+ qDebug() << tempDir.path();
+ qDebug() << target_dir.path();
+
+ LinkTask lnk_tsk(folder, target_dir.path());
+ lnk_tsk.linkRecursively(true);
+ lnk_tsk.setMaxDepth(-1);
+ QObject::connect(&lnk_tsk, &Task::finished, [&]{
+ QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ });
+ lnk_tsk.start();
+
+ QVERIFY2(QTest::qWaitFor([&]() {
+ return lnk_tsk.isFinished();
+ }, 100000), "Task didn't finish as it should.");
+
+
+ std::function verify_check = [&](QString check_path) {
+ QDir check_dir(check_path);
+ auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
+ for(auto entry: check_dir.entryList(filter))
+ {
+ QFileInfo entry_lnk_info(check_dir.filePath(entry));
+ qDebug() << entry << check_dir.filePath(entry);
+ if (!entry_lnk_info.isDir()){
+ QVERIFY(entry_lnk_info.isSymLink());
+ } else if (entry != "." && entry != "..") {
+ qDebug() << "Decending tree to verify symlinks:" << check_dir.filePath(entry);
+ verify_check(entry_lnk_info.filePath());
+ }
+ }
+ };
+
+ verify_check(target_dir.path());
+
+
+ QFileInfo lnk_info(target_dir.path());
+ QVERIFY(lnk_info.exists());
+
+ QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+ };
+
+ // first try variant without trailing /
+ QVERIFY(!folder.endsWith('/'));
+ f();
+
+ // then variant with trailing /
+ folder.append('/');
+ QVERIFY(folder.endsWith('/'));
+ f();
+ }
+
+ void test_path_depth() {
+ QCOMPARE_EQ(FS::PathDepth(""), 0);
+ QCOMPARE_EQ(FS::PathDepth("."), 0);
+ QCOMPARE_EQ(FS::PathDepth("foo.txt"), 0);
+ QCOMPARE_EQ(FS::PathDepth("./foo.txt"), 0);
+ QCOMPARE_EQ(FS::PathDepth("./bar/foo.txt"), 1);
+ QCOMPARE_EQ(FS::PathDepth("../bar/foo.txt"), 0);
+ QCOMPARE_EQ(FS::PathDepth("/bar/foo.txt"), 1);
+ QCOMPARE_EQ(FS::PathDepth("baz/bar/foo.txt"), 2);
+ QCOMPARE_EQ(FS::PathDepth("/baz/bar/foo.txt"), 2);
+ QCOMPARE_EQ(FS::PathDepth("./baz/bar/foo.txt"), 2);
+ QCOMPARE_EQ(FS::PathDepth("/baz/../bar/foo.txt"), 1);
+ }
+
+ void test_path_trunc() {
+ QCOMPARE_EQ(FS::PathTruncate("", 0), "");
+ QCOMPARE_EQ(FS::PathTruncate("foo.txt", 0), "");
+ QCOMPARE_EQ(FS::PathTruncate("foo.txt", 1), "");
+ QCOMPARE_EQ(FS::PathTruncate("./bar/foo.txt", 0), "./bar");
+ QCOMPARE_EQ(FS::PathTruncate("./bar/foo.txt", 1), "./bar");
+ QCOMPARE_EQ(FS::PathTruncate("/bar/foo.txt", 1), "/bar");
+ QCOMPARE_EQ(FS::PathTruncate("bar/foo.txt", 1), "bar");
+ QCOMPARE_EQ(FS::PathTruncate("baz/bar/foo.txt", 2), "baz/bar");
+ }
};
QTEST_GUILESS_MAIN(FileSystemTest)
--
cgit
From 9f441a9678f56c5fb5efbc415b3faff176609b9c Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sat, 11 Feb 2023 22:51:53 -0800
Subject: feat: Add UAC icon when symlinking on windows.
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 8 --------
launcher/ui/dialogs/CopyInstanceDialog.cpp | 23 ++++++++++++++++++-----
launcher/ui/dialogs/CopyInstanceDialog.ui | 5 ++++-
3 files changed, 22 insertions(+), 14 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index dd62893c..b47f5746 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -1149,14 +1149,6 @@ if(WIN32)
SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
endif()
- # may be unnessacery with manifest
- if(CMAKE_GENERATOR MATCHES "Visual Studio")
- SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/level='requireAdministrator' /uiAccess='false' /SUBSYSTEM:CONSOLE")
- # else() # link arg /MANIFESTUAC only works with MSVC
- # SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" /SUBSYSTEM:CONSOLE")
- endif()
-
-
install(TARGETS "${Launcher_Name}_filelink"
BUNDLE DESTINATION "." COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 9fe129f1..495e98e9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -93,17 +93,25 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
- auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+ auto detectedFS = FS::statFS(m_original->instanceRoot()).fsType;
- m_cloneSupported = FS::canCloneOnFS(detectedOS);
- m_linkSupported = FS::canLinkOnFS(detectedOS);
+ m_cloneSupported = FS::canCloneOnFS(detectedFS);
+ m_linkSupported = FS::canLinkOnFS(detectedFS);
if (m_cloneSupported) {
- ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedFS)));
} else {
- ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedFS)));
}
+#if defined(Q_OS_WIN)
+ ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
+ ui->symbolicLinksCheckbox->setToolTip(
+ tr("Use symbolic links instead of copying files.") +
+ tr("\nOn windows symbolic links may require admin permision to create.")
+ );
+#endif
+
updateLinkOptions();
updateUseCloneCheckbox();
@@ -189,6 +197,11 @@ void CopyInstanceDialog::updateLinkOptions()
ui->dontLinkSavesCheckbox->setEnabled(m_linkSupported && linksInUse);
ui->recursiveLinkCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isDontLinkSavesEnabled());
+
+#if defined(Q_OS_WIN)
+ auto OkButton = ui->buttonBox->button(QDialogButtonBox::Ok);
+ OkButton->setIcon(m_selectedOptions.isUseSymLinksEnabled() ? style()->standardIcon(QStyle::SP_VistaShield) : QIcon());
+#endif
}
void CopyInstanceDialog::on_iconButton_clicked()
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 7bf75c2d..58442f73 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -269,7 +269,7 @@
true
- Use hard links instead of symbolic links
+ Use hard links instead of symbolic links.
Use hard links
@@ -307,6 +307,9 @@
Use symbloic links
+
+ Use symbolic links instead of copying files.
+
--
cgit
From a1053a4c5ac8651be3b57814918e28179eb2a1f9 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sun, 12 Feb 2023 02:44:39 -0700
Subject: feat: warnings when instance resources are linked
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 4 +++
launcher/FileSystem.h | 2 ++
launcher/minecraft/World.cpp | 24 +++++++++++++++++
launcher/minecraft/World.h | 15 +++++++++++
launcher/minecraft/WorldList.cpp | 30 ++++++++++++++++++++--
launcher/minecraft/WorldList.h | 5 +++-
launcher/minecraft/mod/ModFolderModel.cpp | 20 +++++++++++++++
launcher/minecraft/mod/Resource.cpp | 20 +++++++++++++++
launcher/minecraft/mod/Resource.h | 13 ++++++++++
launcher/minecraft/mod/ResourceFolderModel.cpp | 26 +++++++++++++++++++
launcher/minecraft/mod/ResourceFolderModel.h | 2 ++
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 20 +++++++++++++++
launcher/ui/pages/instance/WorldListPage.cpp | 1 +
13 files changed, 179 insertions(+), 3 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index a9461bb0..af2ba299 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -1641,5 +1641,9 @@ bool canLink(const QString& src, const QString& dst)
return canLinkOnFS(src) && canLinkOnFS(dst);
}
+uintmax_t hardLinkCount(const QString& path)
+{
+ return fs::hard_link_count(StringUtils::toStdString(path));
+}
}
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index cafbd2a8..361993eb 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -528,4 +528,6 @@ bool canLinkOnFS(FilesystemType type);
*/
bool canLink(const QString& src, const QString& dst);
+uintmax_t hardLinkCount(const QString& path);
+
}
diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp
index d310f8b9..54fb9434 100644
--- a/launcher/minecraft/World.cpp
+++ b/launcher/minecraft/World.cpp
@@ -56,6 +56,8 @@
#include
+#include "FileSystem.h"
+
using std::optional;
using std::nullopt;
@@ -567,3 +569,25 @@ bool World::operator==(const World &other) const
{
return is_valid == other.is_valid && folderName() == other.folderName();
}
+
+bool World::isSymLinkUnder(const QString& instPath) const
+{
+ if (isSymLink())
+ return true;
+
+ auto instDir = QDir(instPath);
+
+ auto relAbsPath = instDir.relativeFilePath(m_containerFile.absoluteFilePath());
+ auto relCanonPath = instDir.relativeFilePath(m_containerFile.canonicalFilePath());
+
+ return relAbsPath != relCanonPath;
+}
+
+bool World::isMoreThanOneHardLink() const
+{
+ if (m_containerFile.isDir())
+ {
+ return FS::hardLinkCount(QDir(m_containerFile.absoluteFilePath()).filePath("level.dat")) > 1;
+ }
+ return FS::hardLinkCount(m_containerFile.absoluteFilePath()) > 1;
+}
diff --git a/launcher/minecraft/World.h b/launcher/minecraft/World.h
index 8327253a..10328cce 100644
--- a/launcher/minecraft/World.h
+++ b/launcher/minecraft/World.h
@@ -95,6 +95,21 @@ public:
// WEAK compare operator - used for replacing worlds
bool operator==(const World &other) const;
+ [[nodiscard]] auto isSymLink() const -> bool{ return m_containerFile.isSymLink(); }
+
+ /**
+ * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance
+ *
+ * @param instPath path to an instance directory
+ * @return true
+ * @return false
+ */
+ [[nodiscard]] bool isSymLinkUnder(const QString& instPath) const;
+
+ [[nodiscard]] bool isMoreThanOneHardLink() const;
+
+ QString canonicalFilePath() const { return m_containerFile.canonicalFilePath(); }
+
private:
void readFromZip(const QFileInfo &file);
void readFromFS(const QFileInfo &file);
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index ae29a972..1262fa1d 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -128,6 +128,10 @@ bool WorldList::isValid()
return m_dir.exists() && m_dir.isReadable();
}
+QString WorldList::instDirPath() const {
+ return QFileInfo(m_dir.filePath("../..")).absoluteFilePath();
+}
+
bool WorldList::deleteWorld(int index)
{
if (index >= worlds.size() || index < 0)
@@ -173,7 +177,7 @@ bool WorldList::resetIcon(int row)
int WorldList::columnCount(const QModelIndex &parent) const
{
- return parent.isValid()? 0 : 4;
+ return parent.isValid()? 0 : 5;
}
QVariant WorldList::data(const QModelIndex &index, int role) const
@@ -207,6 +211,14 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
case SizeColumn:
return locale.formattedDataSize(world.bytes());
+ case InfoColumn:
+ if (world.isSymLinkUnder(instDirPath())) {
+ return tr("This world is symbolicly linked from elsewhere.");
+ }
+ if (world.isMoreThanOneHardLink()) {
+ return tr("\nThis world is hard linked elsewhere.");
+ }
+ return "";
default:
return QVariant();
}
@@ -222,7 +234,16 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
}
case Qt::ToolTipRole:
- {
+ {
+ if (column == InfoColumn) {
+ if (world.isSymLinkUnder(instDirPath())) {
+ return tr("Warning: This world is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nCanonical Path: %1").arg(world.canonicalFilePath());
+ }
+ if (world.isMoreThanOneHardLink()) {
+ return tr("Warning: This world is hard linked elsewhere. Editing it will also change the origonal");
+ }
+ }
return world.folderName();
}
case ObjectRole:
@@ -274,6 +295,9 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
case SizeColumn:
//: World size on disk
return tr("Size");
+ case InfoColumn:
+ //: special warnings?
+ return tr("Info");
default:
return QVariant();
}
@@ -289,6 +313,8 @@ QVariant WorldList::headerData(int section, Qt::Orientation orientation, int rol
return tr("Date and time the world was last played.");
case SizeColumn:
return tr("Size of the world on disk.");
+ case InfoColumn:
+ return tr("Information and warnings about the world.");
default:
return QVariant();
}
diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h
index 08294755..bd32dd4e 100644
--- a/launcher/minecraft/WorldList.h
+++ b/launcher/minecraft/WorldList.h
@@ -33,7 +33,8 @@ public:
NameColumn,
GameModeColumn,
LastPlayedColumn,
- SizeColumn
+ SizeColumn,
+ InfoColumn
};
enum Roles
@@ -112,6 +113,8 @@ public:
return m_dir;
}
+ QString instDirPath() const;
+
const QList &allWorlds() const
{
return worlds;
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index 3f31b93c..e2053f92 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -39,13 +39,17 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
#include
+#include "Application.h"
+
#include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
@@ -97,8 +101,24 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
}
case Qt::ToolTipRole:
+ if (column == NAME_COLUMN) {
+ if (at(row)->isSymLinkUnder(instDirPath())) {
+ return m_resources[row]->internal_id() +
+ tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());
+ }
+ if (at(row)->isMoreThanOneHardLink()) {
+ return m_resources[row]->internal_id() +
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ }
+ }
return m_resources[row]->internal_id();
+ case Qt::DecorationRole: {
+ if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
+ return APPLICATION->getThemedIcon("status-yellow");
+ return {};
+ }
case Qt::CheckStateRole:
switch (column)
{
diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp
index 0d35d755..a0b8a4bb 100644
--- a/launcher/minecraft/mod/Resource.cpp
+++ b/launcher/minecraft/mod/Resource.cpp
@@ -1,6 +1,8 @@
#include "Resource.h"
+
#include
+#include
#include "FileSystem.h"
@@ -152,3 +154,21 @@ bool Resource::destroy()
return FS::deletePath(m_file_info.filePath());
}
+
+bool Resource::isSymLinkUnder(const QString& instPath) const
+{
+ if (isSymLink())
+ return true;
+
+ auto instDir = QDir(instPath);
+
+ auto relAbsPath = instDir.relativeFilePath(m_file_info.absoluteFilePath());
+ auto relCanonPath = instDir.relativeFilePath(m_file_info.canonicalFilePath());
+
+ return relAbsPath != relCanonPath;
+}
+
+bool Resource::isMoreThanOneHardLink() const
+{
+ return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1;
+}
diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h
index 0c37f3a3..a5e9ae91 100644
--- a/launcher/minecraft/mod/Resource.h
+++ b/launcher/minecraft/mod/Resource.h
@@ -94,6 +94,19 @@ class Resource : public QObject {
// Delete all files of this resource.
bool destroy();
+ [[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
+
+ /**
+ * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance
+ *
+ * @param instPath path to an instance directory
+ * @return true
+ * @return false
+ */
+ [[nodiscard]] bool isSymLinkUnder(const QString& instPath) const;
+
+ [[nodiscard]] bool isMoreThanOneHardLink() const;
+
protected:
/* The file corresponding to this resource. */
QFileInfo m_file_info;
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index f2a77c12..d1748c1c 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -2,10 +2,14 @@
#include
#include
+#include
+#include
#include
+#include
#include
#include
+#include "Application.h"
#include "FileSystem.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
@@ -417,7 +421,25 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
return {};
}
case Qt::ToolTipRole:
+ if (column == NAME_COLUMN) {
+ if (at(row).isSymLinkUnder(instDirPath())) {
+ return m_resources[row]->internal_id() +
+ tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nCanonical Path: %1").arg(at(row).fileinfo().canonicalFilePath());;
+ }
+ if (at(row).isMoreThanOneHardLink()) {
+ return m_resources[row]->internal_id() +
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ }
+ }
+
return m_resources[row]->internal_id();
+ case Qt::DecorationRole: {
+ if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
+ return APPLICATION->getThemedIcon("status-yellow");
+
+ return {};
+ }
case Qt::CheckStateRole:
switch (column) {
case ACTIVE_COLUMN:
@@ -531,3 +553,7 @@ void ResourceFolderModel::enableInteraction(bool enabled)
return (compare_result.first < 0);
return (compare_result.first > 0);
}
+
+QString ResourceFolderModel::instDirPath() const {
+ return QFileInfo(m_dir.filePath("../..")).absoluteFilePath();
+}
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index 3bd78870..f840b2de 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -125,6 +125,8 @@ class ResourceFolderModel : public QAbstractListModel {
[[nodiscard]] bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override;
};
+ QString instDirPath() const;
+
public slots:
void enableInteraction(bool enabled);
void disableInteraction(bool disabled) { enableInteraction(!disabled); }
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index da4bd091..56584a34 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -36,6 +36,10 @@
#include "ResourcePackFolderModel.h"
+#include
+#include
+
+#include "Application.h"
#include "Version.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
@@ -78,12 +82,28 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
default:
return {};
}
+ case Qt::DecorationRole: {
+ if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
+ return APPLICATION->getThemedIcon("status-yellow");
+ return {};
+ }
case Qt::ToolTipRole: {
if (column == PackFormatColumn) {
//: The string being explained by this is in the format: ID (Lower version - Upper version)
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
}
+ if (column == NAME_COLUMN) {
+ if (at(row)->isSymLinkUnder(instDirPath())) {
+ return m_resources[row]->internal_id() +
+ tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());;
+ }
+ if (at(row)->isMoreThanOneHardLink()) {
+ return m_resources[row]->internal_id() +
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ }
+ }
return m_resources[row]->internal_id();
}
case Qt::CheckStateRole:
diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp
index d4a395d9..b6ad159e 100644
--- a/launcher/ui/pages/instance/WorldListPage.cpp
+++ b/launcher/ui/pages/instance/WorldListPage.cpp
@@ -107,6 +107,7 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr worl
auto head = ui->worldTreeView->header();
head->setSectionResizeMode(0, QHeaderView::Stretch);
head->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(4, QHeaderView::ResizeToContents);
connect(ui->worldTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &WorldListPage::worldChanged);
worldChanged(QModelIndex(), QModelIndex());
--
cgit
From a0e03c41c034ddbc330789c7639f02a4a8ac1a10 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sun, 12 Feb 2023 02:59:52 -0700
Subject: fix: typos
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/minecraft/WorldList.cpp | 6 +++---
launcher/minecraft/mod/ModFolderModel.cpp | 4 ++--
launcher/minecraft/mod/ResourceFolderModel.cpp | 4 ++--
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 4 ++--
launcher/ui/dialogs/CopyInstanceDialog.ui | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 1262fa1d..43733110 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -213,7 +213,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
case InfoColumn:
if (world.isSymLinkUnder(instDirPath())) {
- return tr("This world is symbolicly linked from elsewhere.");
+ return tr("This world is symbolically linked from elsewhere.");
}
if (world.isMoreThanOneHardLink()) {
return tr("\nThis world is hard linked elsewhere.");
@@ -237,11 +237,11 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
{
if (column == InfoColumn) {
if (world.isSymLinkUnder(instDirPath())) {
- return tr("Warning: This world is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(world.canonicalFilePath());
}
if (world.isMoreThanOneHardLink()) {
- return tr("Warning: This world is hard linked elsewhere. Editing it will also change the origonal");
+ return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original");
}
}
return world.folderName();
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index e2053f92..943f30ad 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -104,12 +104,12 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index d1748c1c..95b7651f 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -424,12 +424,12 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row).isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(at(row).fileinfo().canonicalFilePath());;
}
if (at(row).isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
}
}
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 56584a34..1fcfa909 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -96,12 +96,12 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());;
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 58442f73..009f5b88 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -305,7 +305,7 @@
-
- Use symbloic links
+ Use symbolic links
Use symbolic links instead of copying files.
--
cgit
From 1ca2c59f2ed7739b4b7d50c7212e292a4432da93 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 15 Feb 2023 22:01:27 -0700
Subject: feat: track instance copies that use links confirm deleations when
other instances link to it
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/BaseInstance.cpp | 36 +++++++++++++++++++++++++++++++
launcher/BaseInstance.h | 6 ++++++
launcher/InstanceCopyTask.cpp | 2 ++
launcher/InstanceList.cpp | 10 +++++++++
launcher/InstanceList.h | 2 ++
launcher/settings/INIFile.cpp | 18 ++++++++++++++++
launcher/settings/INIFile.h | 35 ++++++++++++++++++++++++++++++
launcher/settings/SettingsObject.cpp | 13 ++++++++++++
launcher/settings/SettingsObject.h | 41 ++++++++++++++++++++++++++++++++++++
launcher/ui/MainWindow.cpp | 14 ++++++++++++
tests/INIFile_test.cpp | 38 +++++++++++++++++++++++++++++++--
11 files changed, 213 insertions(+), 2 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp
index 8680361c..6428be43 100644
--- a/launcher/BaseInstance.cpp
+++ b/launcher/BaseInstance.cpp
@@ -40,6 +40,8 @@
#include
#include
#include
+#include
+#include
#include "settings/INISettingsObject.h"
#include "settings/Setting.h"
@@ -64,6 +66,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("lastTimePlayed", 0);
+ m_settings->registerSetting("linkedInstancesList", "[]");
+
// Game time override
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
@@ -182,6 +186,38 @@ bool BaseInstance::shouldStopOnConsoleOverflow() const
return m_settings->get("ConsoleOverflowStop").toBool();
}
+QStringList BaseInstance::getLinkedInstances() const
+{
+ return m_settings->getList("linkedInstancesList");
+}
+
+void BaseInstance::setLinkedInstances(const QStringList& list)
+{
+ auto linkedInstancesList = m_settings->getList("linkedInstancesList");
+ m_settings->setList("linkedInstancesList", list);
+}
+
+void BaseInstance::addLinkedInstanceId(const QString& id)
+{
+ auto linkedInstancesList = m_settings->getList("linkedInstancesList");
+ linkedInstancesList.append(id);
+ setLinkedInstances(linkedInstancesList);
+}
+
+bool BaseInstance::removeLinkedInstanceId(const QString& id)
+{
+ auto linkedInstancesList = m_settings->getList("linkedInstancesList");
+ int numRemoved = linkedInstancesList.removeAll(id);
+ setLinkedInstances(linkedInstancesList);
+ return numRemoved > 0;
+}
+
+bool BaseInstance::isLinkedToInstanceId(const QString& id) const
+{
+ auto linkedInstancesList = m_settings->getList("linkedInstancesList");
+ return linkedInstancesList.contains(id);
+}
+
void BaseInstance::iconUpdated(QString key)
{
if(iconKey() == key)
diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h
index a2a4f824..83a8064f 100644
--- a/launcher/BaseInstance.h
+++ b/launcher/BaseInstance.h
@@ -282,6 +282,12 @@ public:
int getConsoleMaxLines() const;
bool shouldStopOnConsoleOverflow() const;
+ QStringList getLinkedInstances() const;
+ void setLinkedInstances(const QStringList& list);
+ void addLinkedInstanceId(const QString& id);
+ bool removeLinkedInstanceId(const QString& id);
+ bool isLinkedToInstanceId(const QString& id) const;
+
protected:
void changeStatus(Status newStatus);
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 40babd0f..e0a4de0b 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -137,6 +137,8 @@ void InstanceCopyTask::copyFinished()
if(!m_keepPlaytime) {
inst->resetTimePlayed();
}
+ if (m_useLinks)
+ inst->addLinkedInstanceId(m_origInstance->id());
emitSucceeded();
}
diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp
index 1ca16ae9..179bfb9a 100644
--- a/launcher/InstanceList.cpp
+++ b/launcher/InstanceList.cpp
@@ -129,6 +129,16 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
return mimeData;
}
+QStringList InstanceList::getLinkedInstancesById(const QString &id) const
+{
+ QStringList linkedInstances;
+ for (auto inst : m_instances) {
+ if (inst->isLinkedToInstanceId(id))
+ linkedInstances.append(inst->id());
+ }
+ return linkedInstances;
+}
+
int InstanceList::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h
index edacba3c..48bede07 100644
--- a/launcher/InstanceList.h
+++ b/launcher/InstanceList.h
@@ -154,6 +154,8 @@ public:
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
+ QStringList getLinkedInstancesById(const QString &id) const;
+
signals:
void dataIsInvalid();
void instancesChanged();
diff --git a/launcher/settings/INIFile.cpp b/launcher/settings/INIFile.cpp
index 733cd444..e48e6f47 100644
--- a/launcher/settings/INIFile.cpp
+++ b/launcher/settings/INIFile.cpp
@@ -183,3 +183,21 @@ void INIFile::set(QString key, QVariant val)
{
this->operator[](key) = val;
}
+
+void INIFile::setList(QString key, QVariantList val)
+{
+ QString stringList = QJsonDocument(QVariant(val).toJsonArray()).toJson(QJsonDocument::Compact);
+
+ this->operator[](key) = stringList;
+}
+
+QVariantList INIFile::getList(QString key, QVariantList def) const
+{
+ if (this->contains(key)) {
+ auto src = this->operator[](key);
+
+ return QJsonDocument::fromJson(src.toByteArray()).toVariant().toList();
+ }
+
+ return def;
+}
diff --git a/launcher/settings/INIFile.h b/launcher/settings/INIFile.h
index 4313e829..86bf0898 100644
--- a/launcher/settings/INIFile.h
+++ b/launcher/settings/INIFile.h
@@ -19,6 +19,9 @@
#include
#include
+#include
+#include
+
// Sectionless INI parser (for instance config files)
class INIFile : public QMap
{
@@ -33,4 +36,36 @@ public:
void set(QString key, QVariant val);
static QString unescape(QString orig);
static QString escape(QString orig);
+
+ void setList(QString key, QVariantList val);
+ template void setList(QString key, QList val)
+ {
+ QVariantList variantList;
+ variantList.reserve(val.size());
+ for (const T& v : val)
+ {
+ variantList.append(v);
+ }
+
+ this->setList(key, variantList);
+ }
+
+ QVariantList getList(QString key, QVariantList def) const;
+ template QList getList(QString key, QList def) const
+ {
+ if (this->contains(key)) {
+ QVariant src = this->operator[](key);
+ QVariantList variantList = QJsonDocument::fromJson(src.toByteArray()).toVariant().toList();
+
+ QListTList;
+ TList.reserve(variantList.size());
+ for (const QVariant& v : variantList)
+ {
+ TList.append(v.value());
+ }
+ return TList;
+ }
+
+ return def;
+ }
};
diff --git a/launcher/settings/SettingsObject.cpp b/launcher/settings/SettingsObject.cpp
index 8a0bc045..4c51d6e9 100644
--- a/launcher/settings/SettingsObject.cpp
+++ b/launcher/settings/SettingsObject.cpp
@@ -121,6 +121,19 @@ bool SettingsObject::contains(const QString &id)
return m_settings.contains(id);
}
+bool SettingsObject::setList(const QString &id, QVariantList value)
+{
+ QString stringList = QJsonDocument(QVariant(value).toJsonArray()).toJson(QJsonDocument::Compact);
+
+ return set(id, stringList);
+}
+
+QVariantList SettingsObject::getList(const QString &id)
+{
+ QVariant value = this->get(id);
+ return QJsonDocument::fromJson(value.toByteArray()).toVariant().toList();
+}
+
bool SettingsObject::reload()
{
for (auto setting : m_settings.values())
diff --git a/launcher/settings/SettingsObject.h b/launcher/settings/SettingsObject.h
index 6200bc3a..ff430172 100644
--- a/launcher/settings/SettingsObject.h
+++ b/launcher/settings/SettingsObject.h
@@ -19,6 +19,8 @@
#include
#include
#include
+#include
+#include
#include
class Setting;
@@ -142,6 +144,45 @@ public:
*/
bool contains(const QString &id);
+ /*!
+ * \brief Sets the value of the setting with the given ID with a json list.
+ * If no setting with the given ID exists, returns false
+ * \param id The ID of the setting to change.
+ * \param value The new value of the setting.
+ */
+ bool setList(const QString &id, QVariantList value);
+ template bool setList(const QString &id, QList val)
+ {
+ QVariantList variantList;
+ variantList.reserve(val.size());
+ for (const T& v : val)
+ {
+ variantList.append(v);
+ }
+
+ return setList(id, variantList);
+ }
+
+ /**
+ * \brief Gets the value of the setting with the given ID as if it were a json list.
+ * \param id The ID of the setting to change.
+ * \return The setting's value as a QVariantList.
+ * If no setting with the given ID exists, returns an empty QVariantList.
+ */
+ QVariantList getList(const QString &id);
+ template QList getList(const QString &id)
+ {
+ QVariantList variantList = this->getList(id);
+
+ QListTList;
+ TList.reserve(variantList.size());
+ for (const QVariant& v : variantList)
+ {
+ TList.append(v.value());
+ }
+ return TList;
+ }
+
/*!
* \brief Reloads the settings and emit signals for changed settings
* \return True if reloading was successful
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index 8490b292..a6aa8320 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -1337,6 +1337,20 @@ void MainWindow::on_actionDeleteInstance_triggered()
if (response != QMessageBox::Yes)
return;
+ auto linkedInstances = APPLICATION->instances()->getLinkedInstancesById(id);
+ if (!linkedInstances.empty()) {
+ response = CustomMessageBox::selectable(
+ this, tr("There are linked instances"),
+ tr("The folowing Instance(s) might reference files in this instance:\n\n"
+ "%1\n\n"
+ "Deleting it could break the other instance(s), \n\n"
+ "Are you sure?").arg(linkedInstances.join("\n")),
+ QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No
+ )->exec();
+ if (response != QMessageBox::Yes)
+ return;
+ }
+
if (APPLICATION->instances()->trashInstance(id)) {
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
return;
diff --git a/tests/INIFile_test.cpp b/tests/INIFile_test.cpp
index b64b031b..d13937c0 100644
--- a/tests/INIFile_test.cpp
+++ b/tests/INIFile_test.cpp
@@ -1,7 +1,10 @@
#include
+#include
+#include
#include
+
class IniFileTest : public QObject
{
Q_OBJECT
@@ -52,8 +55,39 @@ slots:
// load
INIFile f2;
f2.loadFile(filename);
- QCOMPARE(a, f2.get("a","NOT SET").toString());
- QCOMPARE(b, f2.get("b","NOT SET").toString());
+ QCOMPARE(f2.get("a","NOT SET").toString(), a);
+ QCOMPARE(f2.get("b","NOT SET").toString(), b);
+ }
+
+ void test_SaveLoadLists()
+ {
+ QString slist_strings = "[\"a\",\"b\",\"c\"]";
+ QStringList list_strings = {"a", "b", "c"};
+
+ QString slist_numbers = "[1,2,3,10]";
+ QList list_numbers = {1, 2, 3, 10};
+
+ QString filename = "test_SaveLoadLists.ini";
+
+ INIFile f;
+ f.setList("list_strings", list_strings);
+ f.setList("list_numbers", list_numbers);
+ f.saveFile(filename);
+
+ // load
+ INIFile f2;
+ f2.loadFile(filename);
+
+ QStringList out_list_strings = f2.getList("list_strings", QStringList());
+ qDebug() << "OutStringList" << out_list_strings;
+
+ QList out_list_numbers = f2.getList("list_numbers", QList());
+ qDebug() << "OutNumbersList" << out_list_numbers;
+
+ QCOMPARE(f2.get("list_strings","NOT SET").toString(), slist_strings);
+ QCOMPARE(out_list_strings, list_strings);
+ QCOMPARE(f2.get("list_numbers","NOT SET").toString(), slist_numbers);
+ QCOMPARE(out_list_numbers, list_numbers);
}
};
--
cgit
From e0ef86340f72ce508034815f1c5f8c695d31140d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 16 Feb 2023 03:31:04 -0700
Subject: feat: connect new help button help-pages/instance-copy
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/dialogs/CopyInstanceDialog.cpp | 11 +++++++++++
launcher/ui/dialogs/CopyInstanceDialog.h | 3 +++
2 files changed, 14 insertions(+)
(limited to 'launcher/ui')
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 495e98e9..62c0bb39 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -37,6 +37,7 @@
#include
#include "Application.h"
+#include "BuildConfig.h"
#include "CopyInstanceDialog.h"
#include "ui_CopyInstanceDialog.h"
@@ -47,6 +48,7 @@
#include "BaseInstance.h"
#include "InstanceList.h"
#include "FileSystem.h"
+#include "DesktopServices.h"
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
@@ -114,6 +116,9 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
updateLinkOptions();
updateUseCloneCheckbox();
+
+ auto HelpButton = ui->buttonBox->button(QDialogButtonBox::Help);
+ connect(HelpButton, &QPushButton::clicked, this, &CopyInstanceDialog::help);
}
@@ -157,6 +162,12 @@ const InstanceCopyPrefs& CopyInstanceDialog::getChosenOptions() const
return m_selectedOptions;
}
+
+void CopyInstanceDialog::help()
+{
+ DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("instance-copy")));
+}
+
void CopyInstanceDialog::checkAllCheckboxes(const bool& b)
{
ui->keepPlaytimeCheckbox->setChecked(b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 2dea3795..c447bee9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -42,6 +42,9 @@ public:
QString iconKey() const;
const InstanceCopyPrefs& getChosenOptions() const;
+public slots:
+ void help();
+
private
slots:
void on_iconButton_clicked();
--
cgit
From ae289c923c4f896dca7e6696eef7ca35b10be9bf Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 22 Feb 2023 17:40:07 -0700
Subject: fix: clean up initial review comments (flowin)
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 15 ++--
launcher/FileSystem.h | 6 +-
launcher/InstanceCopyTask.cpp | 6 +-
launcher/ui/dialogs/CopyInstanceDialog.ui | 129 ++++++++++++++++--------------
tests/FileSystem_test.cpp | 6 +-
5 files changed, 82 insertions(+), 80 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 45c73d24..2bd0cf52 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -36,10 +36,6 @@
*/
#include "FileSystem.h"
-#include
-#include
-#include
-#include
#include "BuildConfig.h"
@@ -48,6 +44,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -435,7 +432,7 @@ bool create_link::make_links()
return true;
}
-void create_link::runPrivlaged(const QString& offset)
+void create_link::runPrivileged(const QString& offset)
{
m_linked = 0; // reset counter
m_path_results.clear();
@@ -506,10 +503,10 @@ void create_link::runPrivlaged(const QString& offset)
in >> err_value;
result.err_value = err_value;
if (result.err_value) {
- qDebug() << "privlaged link fail" << result.src << "to" << result.dst << "code" << result.err_value << result.err_msg;
+ qDebug() << "privileged link fail" << result.src << "to" << result.dst << "code" << result.err_value << result.err_msg;
emit linkFailed(result.src, result.dst, result.err_msg, result.err_value);
} else {
- qDebug() << "privlaged link success" << result.src << "to" << result.dst;
+ qDebug() << "privileged link success" << result.src << "to" << result.dst;
m_linked++;
emit fileLinked(result.src, result.dst);
}
@@ -533,7 +530,7 @@ void create_link::runPrivlaged(const QString& offset)
}
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
- connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivlaged(gotResults); });
+ connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
linkFileProcess->start();
@@ -1041,7 +1038,7 @@ FilesystemInfo statFS(const QString& path)
QStorageInfo storage_info(NearestExistentAncestor(path));
- info.fsTypeName = QString::fromStdString(storage_info.fileSystemType().toStdString());
+ info.fsTypeName = storage_info.fileSystemType();
info.fsType = getFilesystemTypeFuzzy(info.fsTypeName);
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 84526c11..71175bb4 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -220,8 +220,8 @@ class create_link : public QObject {
int totalLinked() { return m_linked; }
- void runPrivlaged() { runPrivlaged(QString()); }
- void runPrivlaged(const QString& offset);
+ void runPrivileged() { runPrivileged(QString()); }
+ void runPrivileged(const QString& offset);
QList getResults() { return m_path_results; }
@@ -230,7 +230,7 @@ class create_link : public QObject {
void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
void finished();
- void finishedPrivlaged(bool gotResults);
+ void finishedPrivileged(bool gotResults);
private:
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index e0a4de0b..5ef7a7fd 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -72,14 +72,14 @@ void InstanceCopyTask::executeTask()
QEventLoop loop;
bool got_priv_results = false;
- connect(&folderLink, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
+ connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){
if (!gotResults) {
- qDebug() << "Privlaged run exited without results!";
+ qDebug() << "Privileged run exited without results!";
}
got_priv_results = gotResults;
loop.quit();
});
- folderLink.runPrivlaged();
+ folderLink.runPrivileged();
loop.exec(); // wait for the finished signal
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 009f5b88..3101acec 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 527
- 699
+ 531
+ 653
@@ -112,35 +112,19 @@
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::LeftToRight
-
-
- Select all
-
-
- false
-
-
-
-
-
-
Instance Copy Options
+
-
+
+
+ Keep play time
+
+
+
-
@@ -151,6 +135,16 @@
+ -
+
+
+ true
+
+
+ Copy resource packs
+
+
+
-
@@ -161,13 +155,6 @@
- -
-
-
- Copy saves
-
-
-
-
@@ -182,33 +169,49 @@
- -
-
-
- true
-
+
-
+
- Copy resource packs
+ Copy saves
- -
-
+
-
+
- Keep play time
+ Copy screenshots
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Qt::LeftToRight
+
- Copy screenshots
+ Select all
+
+
+ false
+ -
+
+
+ Qt::Horizontal
+
+
+
-
@@ -250,7 +253,7 @@
-
-
+
6
@@ -263,24 +266,14 @@
6
-
-
-
-
- true
-
-
- Use hard links instead of symbolic links.
-
-
- Use hard links
-
-
-
-
false
+
+ Link each resource individually instead of linking whole folders at once
+
Link files recursively
@@ -302,14 +295,27 @@
- -
-
+
-
+
+
+ true
+
+
+ Use hard links instead of copying files.
+
- Use symbolic links
+ Use hard links
+
+
+ -
+
Use symbolic links instead of copying files.
+
+ Use symbolic links
+
@@ -373,7 +379,6 @@
iconButton
instNameTextBox
groupBox
- selectAllCheckbox
keepPlaytimeCheckbox
copyScreenshotsCheckbox
copySavesCheckbox
diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp
index 169f0669..19565a99 100644
--- a/tests/FileSystem_test.cpp
+++ b/tests/FileSystem_test.cpp
@@ -72,15 +72,15 @@ class LinkTask : public Task {
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
qDebug() << "atempting to run with privelage";
- connect(m_lnk, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
+ connect(m_lnk, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){
if (gotResults) {
emitSucceeded();
} else {
- qDebug() << "Privlaged run exited without results!";
+ qDebug() << "Privileged run exited without results!";
emitFailed();
}
});
- m_lnk->runPrivlaged();
+ m_lnk->runPrivileged();
} else {
qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str();
}
--
cgit
From 458c2f38bc8e560832ca09b846edaa9ddc64f58d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Fri, 3 Mar 2023 06:33:37 -0700
Subject: cleanup: code review sugestions
clean up translation strings
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/BaseInstance.cpp | 24 +++++++++++-----------
launcher/InstanceCopyTask.cpp | 4 ----
launcher/filelink/filelink.exe.manifest | 2 +-
launcher/filelink/main.cpp | 2 +-
launcher/minecraft/WorldList.cpp | 6 +++---
launcher/minecraft/mod/ModFolderModel.cpp | 7 ++++---
launcher/minecraft/mod/ResourceFolderModel.cpp | 7 ++++---
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 7 ++++---
launcher/ui/dialogs/CopyInstanceDialog.cpp | 2 +-
9 files changed, 30 insertions(+), 31 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp
index 6428be43..ad45aa2d 100644
--- a/launcher/BaseInstance.cpp
+++ b/launcher/BaseInstance.cpp
@@ -66,7 +66,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("lastTimePlayed", 0);
- m_settings->registerSetting("linkedInstancesList", "[]");
+ m_settings->registerSetting("linkedInstances", "[]");
// Game time override
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
@@ -188,34 +188,34 @@ bool BaseInstance::shouldStopOnConsoleOverflow() const
QStringList BaseInstance::getLinkedInstances() const
{
- return m_settings->getList("linkedInstancesList");
+ return m_settings->getList("linkedInstances");
}
void BaseInstance::setLinkedInstances(const QStringList& list)
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- m_settings->setList("linkedInstancesList", list);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ m_settings->setList("linkedInstances", list);
}
void BaseInstance::addLinkedInstanceId(const QString& id)
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- linkedInstancesList.append(id);
- setLinkedInstances(linkedInstancesList);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ linkedInstances.append(id);
+ setLinkedInstances(linkedInstances);
}
bool BaseInstance::removeLinkedInstanceId(const QString& id)
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- int numRemoved = linkedInstancesList.removeAll(id);
- setLinkedInstances(linkedInstancesList);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ int numRemoved = linkedInstances.removeAll(id);
+ setLinkedInstances(linkedInstances);
return numRemoved > 0;
}
bool BaseInstance::isLinkedToInstanceId(const QString& id) const
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- return linkedInstancesList.contains(id);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ return linkedInstances.contains(id);
}
void BaseInstance::iconUpdated(QString key)
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 5ef7a7fd..6bd56de3 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -10,10 +10,6 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
{
m_origInstance = origInstance;
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
-
-
-
-
m_useLinks = prefs.isUseSymLinksEnabled();
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
diff --git a/launcher/filelink/filelink.exe.manifest b/launcher/filelink/filelink.exe.manifest
index a4e16264..239aa978 100644
--- a/launcher/filelink/filelink.exe.manifest
+++ b/launcher/filelink/filelink.exe.manifest
@@ -25,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp
index 7f06795e..4a22ff18 100644
--- a/launcher/filelink/main.cpp
+++ b/launcher/filelink/main.cpp
@@ -28,4 +28,4 @@ int main(int argc, char *argv[])
FileLinkApp ldh(argc, argv);
return ldh.exec();
-}
\ No newline at end of file
+}
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 43733110..3681bcda 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -237,11 +237,11 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
{
if (column == InfoColumn) {
if (world.isSymLinkUnder(instDirPath())) {
- return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original") +
- tr("\nCanonical Path: %1").arg(world.canonicalFilePath());
+ return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1").arg(world.canonicalFilePath());
}
if (world.isMoreThanOneHardLink()) {
- return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original");
+ return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original.");
}
}
return world.folderName();
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index 943f30ad..91d16175 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -104,12 +104,13 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
if (column == NAME_COLUMN) {
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") +
- tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row)->fileinfo().canonicalFilePath());
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index 95b7651f..29a0c736 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -424,12 +424,13 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
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") +
- tr("\nCanonical Path: %1").arg(at(row).fileinfo().canonicalFilePath());;
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row).fileinfo().canonicalFilePath());;
}
if (at(row).isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 1fcfa909..0480d8ba 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -96,12 +96,13 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
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") +
- tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());;
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row)->fileinfo().canonicalFilePath());;
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 62c0bb39..ced57ae0 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -324,4 +324,4 @@ void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
updateUseCloneCheckbox();
updateLinkOptions();
-}
\ No newline at end of file
+}
--
cgit
From a28193430c3830af294cbba92d68e9bcccd7dfd2 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Tue, 14 Mar 2023 16:17:43 -0700
Subject: fix: adjust geometry and add missing tooltip
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/dialogs/CopyInstanceDialog.ui | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 3101acec..5060debc 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 531
- 653
+ 575
+ 695
@@ -334,11 +334,27 @@
false
+
+ Files cloned with reflinks take up no extra space until they are modified.
+
Clone instead of copying
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
-
--
cgit
From 0c986ba4d006740947603afb2e18dd9d2ffdfd2f Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Mon, 20 Mar 2023 16:38:40 -0700
Subject: spelling and formatting
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 14 +--
launcher/FileSystem.h | 185 +++++++++++++----------------
launcher/InstanceCopyTask.cpp | 45 ++++---
launcher/filelink/FileLink.cpp | 110 +++++++----------
launcher/filelink/FileLink.h | 20 ++--
launcher/filelink/main.cpp | 5 +-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 40 +++----
launcher/ui/dialogs/CopyInstanceDialog.h | 21 ++--
8 files changed, 192 insertions(+), 248 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index c913b43e..640cf9be 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -328,7 +328,7 @@ bool create_link::operator()(const QString& offset, bool dryRun)
}
/**
- * @brief make a list off all the links ot make
+ * @brief make a list off all the links to
* @param offset subdirectory form src to link to dest
* @return if there was an error during the attempt to link
*/
@@ -363,7 +363,7 @@ void create_link::make_link_list(const QString& offset)
link_file(src, "");
} else {
if (m_debug)
- qDebug() << "linking recursivly:" << src << "to" << dst << "max_depth:" << m_max_depth;
+ qDebug() << "linking recursively:" << src << "to" << dst << "max_depth:" << m_max_depth;
QDir src_dir(src);
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
@@ -414,7 +414,7 @@ bool create_link::make_links()
qWarning() << "Failed to link files:" << QString::fromStdString(m_os_err.message());
qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path;
- qDebug() << "Error catagory:" << m_os_err.category().name();
+ qDebug() << "Error category:" << m_os_err.category().name();
qDebug() << "Error code:" << m_os_err.value();
emit linkFailed(src_path, dst_path, QString::fromStdString(m_os_err.message()), m_os_err.value());
} else {
@@ -469,7 +469,7 @@ void create_link::runPrivileged(const QString& offset)
in.setDevice(clientConnection);
in.setVersion(QDataStream::Qt_5_0);
qDebug() << "Reading path results from client";
- qDebug() << "bytes avalible" << clientConnection->bytesAvailable();
+ qDebug() << "bytes available" << clientConnection->bytesAvailable();
// Relies on the fact that QDataStream serializes a quint32 into
// sizeof(quint32) bytes
@@ -479,7 +479,7 @@ void create_link::runPrivileged(const QString& offset)
in >> blockSize;
qDebug() << "blocksize is" << blockSize;
- qDebug() << "bytes avalible" << clientConnection->bytesAvailable();
+ qDebug() << "bytes available" << clientConnection->bytesAvailable();
if (clientConnection->bytesAvailable() < blockSize || in.atEnd())
return;
@@ -506,7 +506,7 @@ void create_link::runPrivileged(const QString& offset)
m_path_results.append(result);
}
gotResults = true;
- qDebug() << "results recieved, closing connection";
+ qDebug() << "results received, closing connection";
clientConnection->close();
});
@@ -981,7 +981,7 @@ FilesystemType getFilesystemType(const QString& name)
}
/**
- * @brief path to the near ancestor that exsists
+ * @brief path to the near ancestor that exists
*
*/
QString NearestExistentAncestor(const QString& path)
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 71175bb4..673c3563 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -44,9 +44,9 @@
#include
#include
+#include
#include
#include
-#include
namespace FS {
@@ -84,7 +84,7 @@ bool ensureFolderPathExists(QString filenamepath);
/**
* @brief Copies a directory and it's contents from src to dest
- */
+ */
class copy : public QObject {
Q_OBJECT
public:
@@ -167,17 +167,14 @@ class ExternalLinkFileProcess : public QThread {
/**
* @brief links (a file / a directory and it's contents) from src to dest
- */
+ */
class create_link : public QObject {
Q_OBJECT
public:
- create_link(const QList path_pairs, QObject* parent = nullptr) : QObject(parent)
- {
- m_path_pairs.append(path_pairs);
- }
+ create_link(const QList path_pairs, QObject* parent = nullptr) : QObject(parent) { m_path_pairs.append(path_pairs); }
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
{
- LinkPair pair = {src, dst};
+ LinkPair pair = { src, dst };
m_path_pairs.append(pair);
}
create_link& useHardLinks(const bool useHard)
@@ -211,28 +208,23 @@ class create_link : public QObject {
return *this;
}
- std::error_code getOSError() {
- return m_os_err;
- }
+ std::error_code getOSError() { return m_os_err; }
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalLinked() { return m_linked; }
-
void runPrivileged() { runPrivileged(QString()); }
void runPrivileged(const QString& offset);
QList getResults() { return m_path_results; }
-
signals:
void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
void finished();
void finishedPrivileged(bool gotResults);
-
private:
bool operator()(const QString& offset, bool dryRun = false);
void make_link_list(const QString& offset);
@@ -262,9 +254,9 @@ class create_link : public QObject {
* @brief moves a file by renaming it
* @param source source file path
* @param dest destination filepath
- *
+ *
*/
-bool move(const QString& source, const QString& dest);
+bool move(const QString& source, const QString& dest);
/**
* Delete a folder recursively
@@ -274,7 +266,7 @@ bool deletePath(QString path);
/**
* Trash a folder / file
*/
-bool trash(QString path, QString *pathInTrash = nullptr);
+bool trash(QString path, QString* pathInTrash = nullptr);
QString PathCombine(const QString& path1, const QString& path2);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
@@ -284,16 +276,15 @@ QString AbsolutePath(const QString& path);
/**
* @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
- *
+ *
* @param path path to measure
- * @return int number of componants before base path
+ * @return int number of components before base path
*/
int PathDepth(const QString& path);
-
/**
- * @brief cut off segments of path untill it is a max of length depth
- *
+ * @brief cut off segments of path until it is a max of length depth
+ *
* @param path path to truncate
* @param depth max depth of new path
* @return QString truncated path
@@ -363,124 +354,118 @@ enum class FilesystemType {
/**
* @brief Ordered Mapping of enum types to reported filesystem names
- * this maping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
+ * this mapping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
* all string values are in uppercase, use `QString.toUpper()` or equivalent during lookup.
- *
+ *
* QMap is ordered
- *
+ *
*/
-static const QMap s_filesystem_type_names = {
- {FilesystemType::FAT, QString("FAT")},
- {FilesystemType::NTFS, QString("NTFS")},
- {FilesystemType::REFS, QString("REFS")},
- {FilesystemType::EXT, QString("EXT")},
- {FilesystemType::EXT_2_OLD, QString("EXT_2_OLD")},
- {FilesystemType::EXT_2_3_4, QString("EXT2/3/4")},
- {FilesystemType::XFS, QString("XFS")},
- {FilesystemType::BTRFS, QString("BTRFS")},
- {FilesystemType::NFS, QString("NFS")},
- {FilesystemType::ZFS, QString("ZFS")},
- {FilesystemType::APFS, QString("APFS")},
- {FilesystemType::HFS, QString("HFS")},
- {FilesystemType::HFSPLUS, QString("HFSPLUS")},
- {FilesystemType::HFSX, QString("HFSX")},
- {FilesystemType::FUSEBLK, QString("FUSEBLK")},
- {FilesystemType::F2FS, QString("F2FS")},
- {FilesystemType::UNKNOWN, QString("UNKNOWN")}
-};
-
+static const QMap s_filesystem_type_names = { { FilesystemType::FAT, QString("FAT") },
+ { FilesystemType::NTFS, QString("NTFS") },
+ { FilesystemType::REFS, QString("REFS") },
+ { FilesystemType::EXT, QString("EXT") },
+ { FilesystemType::EXT_2_OLD, QString("EXT_2_OLD") },
+ { FilesystemType::EXT_2_3_4, QString("EXT2/3/4") },
+ { FilesystemType::XFS, QString("XFS") },
+ { FilesystemType::BTRFS, QString("BTRFS") },
+ { FilesystemType::NFS, QString("NFS") },
+ { FilesystemType::ZFS, QString("ZFS") },
+ { FilesystemType::APFS, QString("APFS") },
+ { FilesystemType::HFS, QString("HFS") },
+ { FilesystemType::HFSPLUS, QString("HFSPLUS") },
+ { FilesystemType::HFSX, QString("HFSX") },
+ { FilesystemType::FUSEBLK, QString("FUSEBLK") },
+ { FilesystemType::F2FS, QString("F2FS") },
+ { FilesystemType::UNKNOWN, QString("UNKNOWN") } };
/**
* @brief Ordered Mapping of reported filesystem names to enum types
- * this maping is non exsaustive, it just attempts to capture the many way these filesystems could be reported.
+ * this mapping is non exsaustive, it just attempts to capture the many way these filesystems could be reported.
* all keys are in uppercase, use `QString.toUpper()` or equivalent during lookup.
- *
+ *
* QMap is ordered
- *
+ *
*/
static const QMap s_filesystem_type_names_inverse = {
- {QString("FAT"), FilesystemType::FAT},
- {QString("NTFS"), FilesystemType::NTFS},
- {QString("REFS"), FilesystemType::REFS},
- {QString("EXT2_OLD"), FilesystemType::EXT_2_OLD},
- {QString("EXT_2_OLD"), FilesystemType::EXT_2_OLD},
- {QString("EXT2"), FilesystemType::EXT_2_3_4},
- {QString("EXT3"), FilesystemType::EXT_2_3_4},
- {QString("EXT4"), FilesystemType::EXT_2_3_4},
- {QString("EXT2/3/4"), FilesystemType::EXT_2_3_4},
- {QString("EXT_2_3_4"), FilesystemType::EXT_2_3_4},
- {QString("EXT"), FilesystemType::EXT}, // must come after all other EXT variants to prevent greedy detection
- {QString("XFS"), FilesystemType::XFS},
- {QString("BTRFS"), FilesystemType::BTRFS},
- {QString("NFS"), FilesystemType::NFS},
- {QString("ZFS"), FilesystemType::ZFS},
- {QString("APFS"), FilesystemType::APFS},
- {QString("HFSPLUS"), FilesystemType::HFSPLUS},
- {QString("HFSX"), FilesystemType::HFSX},
- {QString("HFS"), FilesystemType::HFS},
- {QString("FUSEBLK"), FilesystemType::FUSEBLK},
- {QString("F2FS"), FilesystemType::F2FS},
- {QString("UNKNOWN"), FilesystemType::UNKNOWN}
+ { QString("FAT"), FilesystemType::FAT },
+ { QString("NTFS"), FilesystemType::NTFS },
+ { QString("REFS"), FilesystemType::REFS },
+ { QString("EXT2_OLD"), FilesystemType::EXT_2_OLD },
+ { QString("EXT_2_OLD"), FilesystemType::EXT_2_OLD },
+ { QString("EXT2"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT3"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT4"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT2/3/4"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT_2_3_4"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT"), FilesystemType::EXT }, // must come after all other EXT variants to prevent greedy detection
+ { QString("XFS"), FilesystemType::XFS },
+ { QString("BTRFS"), FilesystemType::BTRFS },
+ { QString("NFS"), FilesystemType::NFS },
+ { QString("ZFS"), FilesystemType::ZFS },
+ { QString("APFS"), FilesystemType::APFS },
+ { QString("HFSPLUS"), FilesystemType::HFSPLUS },
+ { QString("HFSX"), FilesystemType::HFSX },
+ { QString("HFS"), FilesystemType::HFS },
+ { QString("FUSEBLK"), FilesystemType::FUSEBLK },
+ { QString("F2FS"), FilesystemType::F2FS },
+ { QString("UNKNOWN"), FilesystemType::UNKNOWN }
};
/**
* @brief Get the string name of Filesystem enum object
- *
- * @param type
- * @return QString
+ *
+ * @param type
+ * @return QString
*/
QString getFilesystemTypeName(FilesystemType type);
/**
* @brief Get the Filesystem enum object from a name
- * Does a lookup of the type name and returns an exact match
+ * Does a lookup of the type name and returns an exact match
*
- * @param name
- * @return FilesystemType
+ * @param name
+ * @return FilesystemType
*/
FilesystemType getFilesystemType(const QString& name);
/**
* @brief Get the Filesystem enum object from a name
* Does a fuzzy lookup of the type name and returns an apropreate match
- *
- * @param name
- * @return FilesystemType
+ *
+ * @param name
+ * @return FilesystemType
*/
FilesystemType getFilesystemTypeFuzzy(const QString& name);
-
struct FilesystemInfo {
FilesystemType fsType = FilesystemType::UNKNOWN;
QString fsTypeName;
- int blockSize;
- qint64 bytesAvailable;
- qint64 bytesFree;
- qint64 bytesTotal;
+ int blockSize;
+ qint64 bytesAvailable;
+ qint64 bytesFree;
+ qint64 bytesTotal;
QString name;
QString rootPath;
};
/**
- * @brief path to the near ancestor that exsists
- *
+ * @brief path to the near ancestor that exists
+ *
*/
QString NearestExistentAncestor(const QString& path);
/**
* @brief colect information about the filesystem under a file
- *
+ *
*/
FilesystemInfo statFS(const QString& path);
-static const QList s_clone_filesystems = {
- FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
- FilesystemType::XFS, FilesystemType::REFS
-};
+static const QList s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
+ FilesystemType::XFS, FilesystemType::REFS };
/**
- * @brief if the Filesystem is reflink/clone capable
- *
+ * @brief if the Filesystem is reflink/clone capable
+ *
*/
bool canCloneOnFS(const QString& path);
bool canCloneOnFS(const FilesystemInfo& info);
@@ -488,13 +473,13 @@ bool canCloneOnFS(FilesystemType type);
/**
* @brief if the Filesystems are reflink/clone capable and both are on the same device
- *
+ *
*/
bool canClone(const QString& src, const QString& dst);
/**
* @brief Copies a directory and it's contents from src to dest
- */
+ */
class clone : public QObject {
Q_OBJECT
public:
@@ -535,7 +520,7 @@ class clone : public QObject {
/**
* @brief clone/reflink file from src to dst
- *
+ *
*/
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
@@ -552,8 +537,8 @@ static const QList s_non_link_filesystems = {
};
/**
- * @brief if the Filesystem is symlink capable
- *
+ * @brief if the Filesystem is symlink capable
+ *
*/
bool canLinkOnFS(const QString& path);
bool canLinkOnFS(const FilesystemInfo& info);
@@ -561,10 +546,10 @@ bool canLinkOnFS(FilesystemType type);
/**
* @brief if the Filesystem is symlink capable on both ends
- *
+ *
*/
bool canLink(const QString& src, const QString& dst);
uintmax_t hardLinkCount(const QString& path);
-}
+} // namespace FS
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 6bd56de3..4ac3b51a 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -1,10 +1,10 @@
#include "InstanceCopyTask.h"
-#include "settings/INISettingsObject.h"
+#include
+#include
#include "FileSystem.h"
#include "NullInstance.h"
#include "pathmatcher/RegexpMatcher.h"
-#include
-#include
+#include "settings/INISettingsObject.h"
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
{
@@ -15,17 +15,17 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
m_useClone = prefs.isUseCloneEnabled();
-
+
QString filters = prefs.getSelectedFiltersAsRegex();
if (m_useLinks || m_useHardLinks) {
- if (!filters.isEmpty()) filters += "|";
+ if (!filters.isEmpty())
+ filters += "|";
filters += "instance.cfg";
- }
+ }
qDebug() << "CopyFilters:" << filters;
- if (!filters.isEmpty())
- {
+ if (!filters.isEmpty()) {
// Set regex filter:
// FIXME: get this from the original instance type...
auto matcherReal = new RegexpMatcher(filters);
@@ -38,14 +38,14 @@ void InstanceCopyTask::executeTask()
{
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
- auto copySaves = [&](){
- FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
+ auto copySaves = [&]() {
+ FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves"), FS::PathCombine(m_stagingPath, "saves"));
savesCopy.followSymlinks(true);
return savesCopy();
};
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] {
if (m_useClone) {
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
folderClone.matcher(m_matcher.get());
@@ -53,22 +53,22 @@ void InstanceCopyTask::executeTask()
return folderClone();
} else if (m_useLinks || m_useHardLinks) {
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
+ 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());
bool there_were_errors = false;
- if(!folderLink()){
+ if (!folderLink()) {
#if defined Q_OS_WIN32
if (!m_useHardLinks) {
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
- qDebug() << "atempting to run with privelage";
+ qDebug() << "attempting to run with privelage";
QEventLoop loop;
bool got_priv_results = false;
- connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){
+ connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
if (!gotResults) {
qDebug() << "Privileged run exited without results!";
}
@@ -77,7 +77,7 @@ void InstanceCopyTask::executeTask()
});
folderLink.runPrivileged();
- loop.exec(); // wait for the finished signal
+ loop.exec(); // wait for the finished signal
for (auto result : folderLink.getResults()) {
if (result.err_value != 0) {
@@ -88,17 +88,17 @@ void InstanceCopyTask::executeTask()
if (m_copySaves) {
there_were_errors |= !copySaves();
}
-
+
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();
-#endif
+#endif
return false;
}
-
+
if (m_copySaves) {
there_were_errors |= !copySaves();
}
@@ -119,8 +119,7 @@ void InstanceCopyTask::executeTask()
void InstanceCopyTask::copyFinished()
{
auto successful = m_copyFuture.result();
- if(!successful)
- {
+ if (!successful) {
emitFailed(tr("Instance folder copy failed."));
return;
}
@@ -130,7 +129,7 @@ void InstanceCopyTask::copyFinished()
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(name());
inst->setIconKey(m_instIcon);
- if(!m_keepPlaytime) {
+ if (!m_keepPlaytime) {
inst->resetTimePlayed();
}
if (m_useLinks)
diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp
index a731ecdb..ded46061 100644
--- a/launcher/filelink/FileLink.cpp
+++ b/launcher/filelink/FileLink.cpp
@@ -25,7 +25,6 @@
#include "StringUtils.h"
-
#include
#include
@@ -41,53 +40,47 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
-#include
#include
+#include
#endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
-#include // for deployment target to support pre-catalina targets without std::fs
-#endif // __APPLE__
+#include // for deployment target to support pre-catalina targets without std::fs
+#endif // __APPLE__
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include
namespace fs = std::filesystem;
-#endif // MacOS min version check
-#endif // Other OSes version check
+#endif // MacOS min version check
+#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include
namespace fs = ghc::filesystem;
#endif
-
-
-FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
+FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
{
#if defined Q_OS_WIN32
// attach the parent console
- if(AttachConsole(ATTACH_PARENT_PROCESS))
- {
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
// if attach succeeds, reopen and sync all the i/o
- if(freopen("CON", "w", stdout))
- {
+ if (freopen("CON", "w", stdout)) {
std::cout.sync_with_stdio();
}
- if(freopen("CON", "w", stderr))
- {
+ if (freopen("CON", "w", stderr)) {
std::cerr.sync_with_stdio();
}
- if(freopen("CON", "r", stdin))
- {
+ if (freopen("CON", "r", stdin)) {
std::cin.sync_with_stdio();
}
- auto out = GetStdHandle (STD_OUTPUT_HANDLE);
+ auto out = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD written;
- const char * endline = "\n";
+ const char* endline = "\n";
WriteConsole(out, endline, strlen(endline), &written, NULL);
consoleAttached = true;
}
@@ -101,10 +94,8 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv),
QCommandLineParser parser;
parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be used with prismlauncher"));
- parser.addOptions({
- {{"s", "server"}, "Join the specified server on launch", "pipe name"},
- {{"H", "hard"}, "use hard links insted of symbolic", "true/false"}
- });
+ parser.addOptions({ { { "s", "server" }, "Join the specified server on launch", "pipe name" },
+ { { "H", "hard" }, "use hard links instead of symbolic", "true/false" } });
parser.addHelpOption();
parser.addVersionOption();
@@ -122,63 +113,57 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv),
qDebug() << "no server to join";
exit();
}
-
}
void FileLinkApp::joinServer(QString server)
{
-
- blockSize = 0;
+ blockSize = 0;
in.setDevice(&socket);
in.setVersion(QDataStream::Qt_5_0);
- connect(&socket, &QLocalSocket::connected, this, [&](){
- qDebug() << "connected to server";
- });
+ connect(&socket, &QLocalSocket::connected, this, [&]() { qDebug() << "connected to server"; });
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
- connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError){
+ connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
switch (socketError) {
- case QLocalSocket::ServerNotFoundError:
- qDebug() << ("The host was not found. Please make sure "
+ case QLocalSocket::ServerNotFoundError:
+ qDebug()
+ << ("The host was not found. Please make sure "
"that the server is running and that the "
"server name is correct.");
- break;
- case QLocalSocket::ConnectionRefusedError:
- qDebug() << ("The connection was refused by the peer. "
+ break;
+ case QLocalSocket::ConnectionRefusedError:
+ qDebug()
+ << ("The connection was refused by the peer. "
"Make sure the server is running, "
"and check that the server name "
"is correct.");
- break;
- case QLocalSocket::PeerClosedError:
- qDebug() << ("The connection was closed by the peer. ");
- break;
- default:
- qDebug() << "The following error occurred: " << socket.errorString();
+ break;
+ case QLocalSocket::PeerClosedError:
+ qDebug() << ("The connection was closed by the peer. ");
+ break;
+ default:
+ qDebug() << "The following error occurred: " << socket.errorString();
}
});
- connect(&socket, &QLocalSocket::disconnected, this, [&](){
- qDebug() << "dissconnected from server, should exit";
+ connect(&socket, &QLocalSocket::disconnected, this, [&]() {
+ qDebug() << "disconnected from server, should exit";
exit();
});
socket.connectToServer(server);
-
-
}
void FileLinkApp::runLink()
-{
-
+{
std::error_code os_err;
qDebug() << "creating links";
for (auto link : m_links_to_make) {
-
QString src_path = link.src;
QString dst_path = link.dst;
@@ -192,26 +177,25 @@ void FileLinkApp::runLink()
} else {
qDebug() << "making symlink:" << src_path << "to" << dst_path;
fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err);
- }
+ }
if (os_err) {
qWarning() << "Failed to link files:" << QString::fromStdString(os_err.message());
qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path;
- qDebug() << "Error catagory:" << os_err.category().name();
+ qDebug() << "Error category:" << os_err.category().name();
qDebug() << "Error code:" << os_err.value();
- FS::LinkResult result = {src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value()};
+ FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() };
m_path_results.append(result);
} else {
- FS::LinkResult result = {src_path, dst_path};
+ FS::LinkResult result = { src_path, dst_path };
m_path_results.append(result);
}
}
sendResults();
qDebug() << "done, should exit soon";
-
}
void FileLinkApp::sendResults()
@@ -245,10 +229,10 @@ void FileLinkApp::sendResults()
}
void FileLinkApp::readPathPairs()
-{
+{
m_links_to_make.clear();
qDebug() << "Reading path pairs from server";
- qDebug() << "bytes avalible" << socket.bytesAvailable();
+ qDebug() << "bytes available" << socket.bytesAvailable();
if (blockSize == 0) {
// Relies on the fact that QDataStream serializes a quint32 into
// sizeof(quint32) bytes
@@ -258,15 +242,15 @@ void FileLinkApp::readPathPairs()
in >> blockSize;
}
qDebug() << "blocksize is" << blockSize;
- qDebug() << "bytes avalible" << socket.bytesAvailable();
+ qDebug() << "bytes available" << socket.bytesAvailable();
if (socket.bytesAvailable() < blockSize || in.atEnd())
return;
-
+
quint32 numLinks;
in >> numLinks;
qDebug() << "numLinks" << numLinks;
- for(int i = 0; i < numLinks; i++) {
+ for (int i = 0; i < numLinks; i++) {
FS::LinkPair pair;
in >> pair.src;
in >> pair.dst;
@@ -277,17 +261,15 @@ void FileLinkApp::readPathPairs()
runLink();
}
-
FileLinkApp::~FileLinkApp()
-{
+{
qDebug() << "link program shutting down";
// Shut down logger by setting the logger function to nothing
qInstallMessageHandler(nullptr);
#if defined Q_OS_WIN32
// Detach from Windows console
- if(consoleAttached)
- {
+ if (consoleAttached) {
fclose(stdout);
fclose(stdin);
fclose(stderr);
@@ -295,7 +277,3 @@ FileLinkApp::~FileLinkApp()
}
#endif
}
-
-
-
-
diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h
index d146b8d9..4c47d9bb 100644
--- a/launcher/filelink/FileLink.h
+++ b/launcher/filelink/FileLink.h
@@ -25,30 +25,26 @@
#include
#include
-#include
+#include
+#include
#include
#include
#include
-#include
-#include
-#include
-#include
#include
+#include
+#include
#define PRISM_EXTERNAL_EXE
#include "FileSystem.h"
-class FileLinkApp : public QCoreApplication
-{
+class FileLinkApp : public QCoreApplication {
// friends for the purpose of limiting access to deprecated stuff
Q_OBJECT
-public:
-
- FileLinkApp(int &argc, char **argv);
+ public:
+ FileLinkApp(int& argc, char** argv);
virtual ~FileLinkApp();
-private:
-
+ private:
void joinServer(QString server);
void readPathPairs();
void runLink();
diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp
index 4a22ff18..83566a3c 100644
--- a/launcher/filelink/main.cpp
+++ b/launcher/filelink/main.cpp
@@ -22,10 +22,9 @@
#include "FileLink.h"
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
{
-
FileLinkApp ldh(argc, argv);
-
+
return ldh.exec();
}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index ced57ae0..347bd39f 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -43,15 +43,15 @@
#include "ui/dialogs/IconPickerDialog.h"
-#include "BaseVersion.h"
-#include "icons/IconList.h"
#include "BaseInstance.h"
-#include "InstanceList.h"
-#include "FileSystem.h"
+#include "BaseVersion.h"
#include "DesktopServices.h"
+#include "FileSystem.h"
+#include "InstanceList.h"
+#include "icons/IconList.h"
-CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
- :QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
+CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent)
+ : QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
{
ui->setupUi(this);
resize(minimumSizeHint());
@@ -74,8 +74,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
groupList.push_front("");
ui->groupBox->addItems(groupList);
int index = groupList.indexOf(APPLICATION->instances()->getInstanceGroup(m_original->id()));
- if(index == -1)
- {
+ if (index == -1) {
index = 0;
}
ui->groupBox->setCurrentIndex(index);
@@ -108,10 +107,8 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
#if defined(Q_OS_WIN)
ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
- ui->symbolicLinksCheckbox->setToolTip(
- tr("Use symbolic links instead of copying files.") +
- tr("\nOn windows symbolic links may require admin permision to create.")
- );
+ ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") +
+ tr("\nOn windows symbolic links may require admin permission to create."));
#endif
updateLinkOptions();
@@ -119,7 +116,6 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
auto HelpButton = ui->buttonBox->button(QDialogButtonBox::Help);
connect(HelpButton, &QPushButton::clicked, this, &CopyInstanceDialog::help);
-
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -131,8 +127,7 @@ void CopyInstanceDialog::updateDialogState()
{
auto allowOK = !instName().isEmpty();
auto OkButton = ui->buttonBox->button(QDialogButtonBox::Ok);
- if(OkButton->isEnabled() != allowOK)
- {
+ if (OkButton->isEnabled() != allowOK) {
OkButton->setEnabled(allowOK);
}
}
@@ -140,8 +135,7 @@ void CopyInstanceDialog::updateDialogState()
QString CopyInstanceDialog::instName() const
{
auto result = ui->instNameTextBox->text().trimmed();
- if(result.size())
- {
+ if (result.size()) {
return result;
}
return QString();
@@ -162,7 +156,6 @@ const InstanceCopyPrefs& CopyInstanceDialog::getChosenOptions() const
return m_selectedOptions;
}
-
void CopyInstanceDialog::help()
{
DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("instance-copy")));
@@ -200,7 +193,8 @@ void CopyInstanceDialog::updateLinkOptions()
ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
- ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() && !ui->useCloneCheckbox->isChecked());
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() &&
+ !ui->useCloneCheckbox->isChecked());
ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled() && !ui->useCloneCheckbox->isChecked());
bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
@@ -220,15 +214,13 @@ void CopyInstanceDialog::on_iconButton_clicked()
IconPickerDialog dlg(this);
dlg.execWithSelection(InstIconKey);
- if (dlg.result() == QDialog::Accepted)
- {
+ if (dlg.result() == QDialog::Accepted) {
InstIconKey = dlg.selectedIconKey;
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
}
}
-
-void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
+void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString& arg1)
{
updateDialogState();
}
@@ -247,7 +239,6 @@ void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
updateSelectAllCheckbox();
}
-
void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)
{
m_selectedOptions.enableKeepPlaytime(state == Qt::Checked);
@@ -311,7 +302,6 @@ void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
{
m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
updateLinkOptions();
-
}
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index c447bee9..698c6e93 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -22,17 +22,15 @@
class BaseInstance;
-namespace Ui
-{
+namespace Ui {
class CopyInstanceDialog;
}
-class CopyInstanceDialog : public QDialog
-{
+class CopyInstanceDialog : public QDialog {
Q_OBJECT
-public:
- explicit CopyInstanceDialog(InstancePtr original, QWidget *parent = 0);
+ public:
+ explicit CopyInstanceDialog(InstancePtr original, QWidget* parent = 0);
~CopyInstanceDialog();
void updateDialogState();
@@ -42,13 +40,12 @@ public:
QString iconKey() const;
const InstanceCopyPrefs& getChosenOptions() const;
-public slots:
+ public slots:
void help();
-private
-slots:
+ private slots:
void on_iconButton_clicked();
- void on_instNameTextBox_textChanged(const QString &arg1);
+ void on_instNameTextBox_textChanged(const QString& arg1);
// Checkboxes
void on_selectAllCheckbox_stateChanged(int state);
void on_copySavesCheckbox_stateChanged(int state);
@@ -65,14 +62,14 @@ slots:
void on_dontLinkSavesCheckbox_stateChanged(int state);
void on_useCloneCheckbox_stateChanged(int state);
-private:
+ private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
void updateUseCloneCheckbox();
void updateLinkOptions();
/* data */
- Ui::CopyInstanceDialog *ui;
+ Ui::CopyInstanceDialog* ui;
QString InstIconKey;
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
--
cgit
From 4c013e59f0e9a481bc63281c0d9e349827419d37 Mon Sep 17 00:00:00 2001
From: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
Date: Sat, 25 Mar 2023 10:45:34 +0100
Subject: divide minecraftpage into tabs
this way small screen users can use the launcher settings without having window a bigger than their actual screens
Signed-off-by: DioEgizio <83089242+DioEgizio@users.noreply.github.com>
---
launcher/ui/pages/global/MinecraftPage.cpp | 1 -
launcher/ui/pages/global/MinecraftPage.ui | 133 ++++++++++++++++-------------
2 files changed, 74 insertions(+), 60 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp
index cc597fe0..eca3e865 100644
--- a/launcher/ui/pages/global/MinecraftPage.cpp
+++ b/launcher/ui/pages/global/MinecraftPage.cpp
@@ -46,7 +46,6 @@
MinecraftPage::MinecraftPage(QWidget *parent) : QWidget(parent), ui(new Ui::MinecraftPage)
{
ui->setupUi(this);
- ui->tabWidget->tabBar()->hide();
loadSettings();
updateCheckboxStuff();
}
diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui
index 640f436d..cff071bf 100644
--- a/launcher/ui/pages/global/MinecraftPage.ui
+++ b/launcher/ui/pages/global/MinecraftPage.ui
@@ -7,7 +7,7 @@
0
0
936
- 1134
+ 541
@@ -39,7 +39,7 @@
- Minecraft
+ General
-
@@ -112,22 +112,29 @@
-
-
+
- Native library workarounds
+ Game time
-
+
-
-
+
- Use system installation of &GLFW
+ Show time spent &playing instances
-
-
+
- Use system installation of &OpenAL
+ Show time spent playing across &all instances
+
+
+
+ -
+
+
+ &Record time spent playing instances
@@ -135,38 +142,28 @@
-
-
+
- Performance
+ Miscellaneous
-
-
-
-
-
- <html><head/><body><p>Enable Feral Interactive's GameMode, to potentially improve gaming performance.</p></body></html>
-
-
- Enable Feral GameMode
-
-
-
+
-
-
+
- <html><head/><body><p>Enable MangoHud's advanced performance overlay.</p></body></html>
+ <html><head/><body><p>The launcher will automatically reopen when the game crashes or exits.</p></body></html>
- Enable MangoHud
+ &Close the launcher after game window opens
-
-
+
- <html><head/><body><p>Use the discrete GPU instead of the primary GPU.</p></body></html>
+ <html><head/><body><p>The launcher will automatically quit after the game exits or crashes.</p></body></html>
- Use discrete GPU
+ &Quit the launcher after game window closes
@@ -174,29 +171,42 @@
-
-
+
+
+ Qt::Vertical
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+ System-related tweaks
+
+
+ -
+
- Game time
+ Native library workarounds
-
+
-
-
-
- Show time spent &playing instances
-
-
-
- -
-
+
- Show time spent playing across &all instances
+ Use system installation of &GLFW
-
-
+
- &Record time spent playing instances
+ Use system installation of &OpenAL
@@ -204,28 +214,38 @@
-
-
+
- Miscellaneous
+ Performance
-
+
-
-
+
- <html><head/><body><p>The launcher will automatically reopen when the game crashes or exits.</p></body></html>
+ <html><head/><body><p>Enable Feral Interactive's GameMode, to potentially improve gaming performance.</p></body></html>
- &Close the launcher after game window opens
+ Enable Feral GameMode
-
-
+
- <html><head/><body><p>The launcher will automatically quit after the game exits or crashes.</p></body></html>
+ <html><head/><body><p>Enable MangoHud's advanced performance overlay.</p></body></html>
- &Quit the launcher after game window closes
+ Enable MangoHud
+
+
+
+ -
+
+
+ <html><head/><body><p>Use the discrete GPU instead of the primary GPU.</p></body></html>
+
+
+ Use discrete GPU
@@ -233,14 +253,14 @@
-
-
+
Qt::Vertical
- 0
- 0
+ 20
+ 40
@@ -255,11 +275,6 @@
maximizedCheckBox
windowWidthSpinBox
windowHeightSpinBox
- useNativeGLFWCheck
- useNativeOpenALCheck
- enableFeralGamemodeCheck
- enableMangoHud
- useDiscreteGpuCheck
--
cgit
From 4df4b4390086171b9ee78a8cd7efb32412292485 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Fri, 31 Mar 2023 18:25:29 -0700
Subject: fix: Apply suggestions from code review (string changes)
Co-authored-by: flow
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/MainWindow.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index a6aa8320..e20a7613 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -1341,10 +1341,10 @@ void MainWindow::on_actionDeleteInstance_triggered()
if (!linkedInstances.empty()) {
response = CustomMessageBox::selectable(
this, tr("There are linked instances"),
- tr("The folowing Instance(s) might reference files in this instance:\n\n"
+ tr("The folowing instance(s) might reference files in this instance:\n\n"
"%1\n\n"
"Deleting it could break the other instance(s), \n\n"
- "Are you sure?").arg(linkedInstances.join("\n")),
+ "Do you wish to proceed?").arg(linkedInstances.join("\n")),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No
)->exec();
if (response != QMessageBox::Yes)
--
cgit
From ff07714e8c955003bb3deec84ec6cce6fc3ef5e6 Mon Sep 17 00:00:00 2001
From: Sefa Eyeoglu
Date: Thu, 27 Apr 2023 16:01:40 +0200
Subject: chore: remove FTB modpack support
We have been contacted by Feed the Beast to drop support for the FTB
modpack browser from Prism Launcher.
Signed-off-by: Sefa Eyeoglu
---
launcher/CMakeLists.txt | 16 -
.../modplatform/modpacksch/FTBPackInstallTask.cpp | 387 ---------------------
.../modplatform/modpacksch/FTBPackInstallTask.h | 101 ------
.../modplatform/modpacksch/FTBPackManifest.cpp | 195 -----------
launcher/modplatform/modpacksch/FTBPackManifest.h | 168 ---------
launcher/ui/dialogs/NewInstanceDialog.cpp | 2 -
.../ui/pages/modplatform/ftb/FtbFilterModel.cpp | 93 -----
launcher/ui/pages/modplatform/ftb/FtbFilterModel.h | 51 ---
launcher/ui/pages/modplatform/ftb/FtbListModel.cpp | 304 ----------------
launcher/ui/pages/modplatform/ftb/FtbListModel.h | 83 -----
launcher/ui/pages/modplatform/ftb/FtbPage.cpp | 199 -----------
launcher/ui/pages/modplatform/ftb/FtbPage.h | 105 ------
launcher/ui/pages/modplatform/ftb/FtbPage.ui | 86 -----
13 files changed, 1790 deletions(-)
delete mode 100644 launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
delete mode 100644 launcher/modplatform/modpacksch/FTBPackInstallTask.h
delete mode 100644 launcher/modplatform/modpacksch/FTBPackManifest.cpp
delete mode 100644 launcher/modplatform/modpacksch/FTBPackManifest.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbListModel.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbPage.cpp
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbPage.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbPage.ui
(limited to 'launcher/ui')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 074570e3..ee36175f 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -524,13 +524,6 @@ set(MODRINTH_SOURCES
modplatform/modrinth/ModrinthInstanceCreationTask.h
)
-set(MODPACKSCH_SOURCES
- modplatform/modpacksch/FTBPackInstallTask.h
- modplatform/modpacksch/FTBPackInstallTask.cpp
- modplatform/modpacksch/FTBPackManifest.h
- modplatform/modpacksch/FTBPackManifest.cpp
-)
-
set(PACKWIZ_SOURCES
modplatform/packwiz/Packwiz.h
modplatform/packwiz/Packwiz.cpp
@@ -599,7 +592,6 @@ set(LOGIC_SOURCES
${FTB_SOURCES}
${FLAME_SOURCES}
${MODRINTH_SOURCES}
- ${MODPACKSCH_SOURCES}
${PACKWIZ_SOURCES}
${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES}
@@ -797,13 +789,6 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
- ui/pages/modplatform/ftb/FtbFilterModel.cpp
- ui/pages/modplatform/ftb/FtbFilterModel.h
- ui/pages/modplatform/ftb/FtbListModel.cpp
- ui/pages/modplatform/ftb/FtbListModel.h
- ui/pages/modplatform/ftb/FtbPage.cpp
- ui/pages/modplatform/ftb/FtbPage.h
-
ui/pages/modplatform/legacy_ftb/Page.cpp
ui/pages/modplatform/legacy_ftb/Page.h
ui/pages/modplatform/legacy_ftb/ListModel.h
@@ -978,7 +963,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/ImportPage.ui
- ui/pages/modplatform/ftb/FtbPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/InstanceCardWidget.ui
diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
deleted file mode 100644
index 68d4751c..00000000
--- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 flowln
- * Copyright (c) 2022 Jamie Mansfield
- * 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 2020-2021 Jamie Mansfield
- * Copyright 2020-2021 Petr Mrazek
- *
- * 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 "FTBPackInstallTask.h"
-
-#include "FileSystem.h"
-#include "Json.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "modplatform/flame/PackManifest.h"
-#include "net/ChecksumValidator.h"
-#include "settings/INISettingsObject.h"
-
-#include "Application.h"
-#include "BuildConfig.h"
-#include "ui/dialogs/BlockedModsDialog.h"
-
-namespace ModpacksCH {
-
-PackInstallTask::PackInstallTask(Modpack pack, QString version, QWidget* parent)
- : m_pack(std::move(pack)), m_version_name(std::move(version)), m_parent(parent)
-{}
-
-bool PackInstallTask::abort()
-{
- if (!canAbort())
- return false;
-
- bool aborted = true;
-
- if (m_net_job)
- aborted &= m_net_job->abort();
- if (m_mod_id_resolver_task)
- aborted &= m_mod_id_resolver_task->abort();
-
- return aborted ? InstanceTask::abort() : false;
-}
-
-void PackInstallTask::executeTask()
-{
- setStatus(tr("Getting the manifest..."));
- setAbortable(false);
-
- // Find pack version
- auto version_it = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(),
- [this](ModpacksCH::VersionInfo const& a) { return a.name == m_version_name; });
-
- if (version_it == m_pack.versions.constEnd()) {
- emitFailed(tr("Failed to find pack version %1").arg(m_version_name));
- return;
- }
-
- auto version = *version_it;
-
- auto netJob = makeShared("ModpacksCH::VersionFetch", APPLICATION->network());
-
- auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2").arg(m_pack.id).arg(version.id);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &m_response));
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded);
- QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed);
- QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::abort);
- QObject::connect(netJob.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
-
- m_net_job = netJob;
-
- setAbortable(true);
- netJob->start();
-}
-
-void PackInstallTask::onManifestDownloadSucceeded()
-{
- m_net_job.reset();
-
- QJsonParseError parse_error{};
- QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
- if (parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset
- << " reason: " << parse_error.errorString();
- qWarning() << m_response;
- return;
- }
-
- ModpacksCH::Version version;
- try {
- auto obj = Json::requireObject(doc);
- ModpacksCH::loadVersion(version, obj);
- } catch (const JSONValidationError& e) {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
-
- m_version = version;
-
- resolveMods();
-}
-
-void PackInstallTask::resolveMods()
-{
- setStatus(tr("Resolving mods..."));
- setAbortable(false);
- setProgress(0, 100);
-
- m_file_id_map.clear();
-
- Flame::Manifest manifest;
- int index = 0;
-
- for (auto const& file : m_version.files) {
- if (!file.serverOnly && file.url.isEmpty()) {
- if (file.curseforge.file_id <= 0) {
- emitFailed(tr("Invalid manifest: There's no information available to download the file '%1'!").arg(file.name));
- return;
- }
-
- Flame::File flame_file;
- flame_file.projectId = file.curseforge.project_id;
- flame_file.fileId = file.curseforge.file_id;
- flame_file.hash = file.sha1;
-
- manifest.files.insert(flame_file.fileId, flame_file);
- m_file_id_map.append(flame_file.fileId);
- } else {
- m_file_id_map.append(-1);
- }
-
- index++;
- }
-
- m_mod_id_resolver_task.reset(new Flame::FileResolvingTask(APPLICATION->network(), manifest));
-
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::succeeded, this, &PackInstallTask::onResolveModsSucceeded);
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::failed, this, &PackInstallTask::onResolveModsFailed);
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::aborted, this, &PackInstallTask::abort);
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::progress, this, &PackInstallTask::setProgress);
-
- setAbortable(true);
-
- m_mod_id_resolver_task->start();
-}
-
-void PackInstallTask::onResolveModsSucceeded()
-{
- auto anyBlocked = false;
-
- Flame::Manifest results = m_mod_id_resolver_task->getResults();
- for (int index = 0; index < m_file_id_map.size(); index++) {
- auto const file_id = m_file_id_map.at(index);
- if (file_id < 0)
- continue;
-
- Flame::File results_file = results.files[file_id];
- VersionFile& local_file = m_version.files[index];
-
- // First check for blocked mods
- if (!results_file.resolved || results_file.url.isEmpty()) {
- BlockedMod blocked_mod;
- blocked_mod.name = local_file.name;
- blocked_mod.websiteUrl = results_file.websiteUrl;
- blocked_mod.hash = results_file.hash;
- blocked_mod.matched = false;
- blocked_mod.localPath = "";
- blocked_mod.targetFolder = results_file.targetFolder;
-
- m_blocked_mods.append(blocked_mod);
-
- anyBlocked = true;
- } else {
- local_file.url = results_file.url.toString();
- }
- }
-
- m_mod_id_resolver_task.reset();
-
- if (anyBlocked) {
- qDebug() << "Blocked files found, displaying file list";
-
- BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"),
- tr("The following files are not available for download in third party launchers.
"
- "You will need to manually download them and add them to the instance."),
- m_blocked_mods);
-
- message_dialog.setModal(true);
-
- if (message_dialog.exec() == QDialog::Accepted) {
- qDebug() << "Post dialog blocked mods list: " << m_blocked_mods;
- createInstance();
- } else {
- abort();
- }
-
- } else {
- createInstance();
- }
-}
-
-void PackInstallTask::createInstance()
-{
- setAbortable(false);
-
- setStatus(tr("Creating the instance..."));
- QCoreApplication::processEvents();
-
- auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared(instanceConfigPath);
-
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
-
- for (auto target : m_version.targets) {
- if (target.type == "game" && target.name == "minecraft") {
- components->setComponentVersion("net.minecraft", target.version, true);
- break;
- }
- }
-
- for (auto target : m_version.targets) {
- if (target.type != "modloader")
- continue;
-
- if (target.name == "forge") {
- components->setComponentVersion("net.minecraftforge", target.version);
- } else if (target.name == "fabric") {
- components->setComponentVersion("net.fabricmc.fabric-loader", target.version);
- }
- }
-
- // install any jar mods
- QDir jarModsDir(FS::PathCombine(m_stagingPath, "minecraft", "jarmods"));
- if (jarModsDir.exists()) {
- QStringList jarMods;
-
- for (const auto& info : jarModsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
- jarMods.push_back(info.absoluteFilePath());
- }
-
- components->installJarMods(jarMods);
- }
-
- components->saveNow();
-
- instance.setName(name());
- instance.setIconKey(m_instIcon);
- instance.setManagedPack("modpacksch", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name);
-
- instance.saveNow();
-
- onCreateInstanceSucceeded();
-}
-
-void PackInstallTask::onCreateInstanceSucceeded()
-{
- downloadPack();
-}
-
-void PackInstallTask::downloadPack()
-{
- setStatus(tr("Downloading mods..."));
- setAbortable(false);
-
- auto jobPtr = makeShared(tr("Mod download"), APPLICATION->network());
- for (auto const& file : m_version.files) {
- if (file.serverOnly || file.url.isEmpty())
- continue;
-
- auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path, file.name);
- qDebug() << "Will try to download" << file.url << "to" << path;
-
- QFileInfo file_info(file.name);
-
- auto dl = Net::Download::makeFile(file.url, path);
- if (!file.sha1.isEmpty()) {
- auto rawSha1 = QByteArray::fromHex(file.sha1.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- }
-
- jobPtr->addNetAction(dl);
- }
-
- connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded);
- connect(jobPtr.get(), &NetJob::failed, this, &PackInstallTask::onModDownloadFailed);
- connect(jobPtr.get(), &NetJob::aborted, this, &PackInstallTask::abort);
- connect(jobPtr.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
-
- m_net_job = jobPtr;
-
- setAbortable(true);
- jobPtr->start();
-}
-
-void PackInstallTask::onModDownloadSucceeded()
-{
- m_net_job.reset();
- if (!m_blocked_mods.isEmpty()) {
- copyBlockedMods();
- }
- emitSucceeded();
-}
-
-void PackInstallTask::onManifestDownloadFailed(QString reason)
-{
- m_net_job.reset();
- emitFailed(reason);
-}
-void PackInstallTask::onResolveModsFailed(QString reason)
-{
- m_net_job.reset();
- emitFailed(reason);
-}
-void PackInstallTask::onCreateInstanceFailed(QString reason)
-{
- emitFailed(reason);
-}
-void PackInstallTask::onModDownloadFailed(QString reason)
-{
- m_net_job.reset();
- emitFailed(reason);
-}
-
-/// @brief copy the matched blocked mods to the instance staging area
-void PackInstallTask::copyBlockedMods()
-{
- setStatus(tr("Copying Blocked Mods..."));
- setAbortable(false);
- int i = 0;
- int total = m_blocked_mods.length();
- setProgress(i, total);
- for (auto const& mod : m_blocked_mods) {
- if (!mod.matched) {
- qDebug() << mod.name << "was not matched to a local file, skipping copy";
- continue;
- }
-
- auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name);
-
- setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
-
- qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
-
- if (!FS::copy(mod.localPath, dest_path)()) {
- qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
- }
-
- i++;
- setProgress(i, total);
- }
-
- setAbortable(true);
-}
-
-} // namespace ModpacksCH
diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.h b/launcher/modplatform/modpacksch/FTBPackInstallTask.h
deleted file mode 100644
index 97b1eb0b..00000000
--- a/launcher/modplatform/modpacksch/FTBPackInstallTask.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 flowln
- * 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 2020-2021 Jamie Mansfield
- * Copyright 2020-2021 Petr Mrazek
- *
- * 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 "FTBPackManifest.h"
-
-#include "InstanceTask.h"
-#include "QObjectPtr.h"
-#include "modplatform/flame/FileResolvingTask.h"
-#include "net/NetJob.h"
-#include "ui/dialogs/BlockedModsDialog.h"
-
-#include
-
-namespace ModpacksCH {
-
-class PackInstallTask final : public InstanceTask
-{
- Q_OBJECT
-
-public:
- explicit PackInstallTask(Modpack pack, QString version, QWidget* parent = nullptr);
- ~PackInstallTask() override = default;
-
- bool abort() override;
-
-protected:
- void executeTask() override;
-
-private slots:
- void onManifestDownloadSucceeded();
- void onResolveModsSucceeded();
- void onCreateInstanceSucceeded();
- void onModDownloadSucceeded();
-
- void onManifestDownloadFailed(QString reason);
- void onResolveModsFailed(QString reason);
- void onCreateInstanceFailed(QString reason);
- void onModDownloadFailed(QString reason);
-
-private:
- void resolveMods();
- void createInstance();
- void downloadPack();
- void copyBlockedMods();
-
-private:
- NetJob::Ptr m_net_job = nullptr;
- shared_qobject_ptr m_mod_id_resolver_task = nullptr;
-
- QList m_file_id_map;
-
- QByteArray m_response;
-
- Modpack m_pack;
- QString m_version_name;
- Version m_version;
-
- QMap m_files_to_copy;
- QList m_blocked_mods;
-
- //FIXME: nuke
- QWidget* m_parent;
-};
-
-}
diff --git a/launcher/modplatform/modpacksch/FTBPackManifest.cpp b/launcher/modplatform/modpacksch/FTBPackManifest.cpp
deleted file mode 100644
index 421527ae..00000000
--- a/launcher/modplatform/modpacksch/FTBPackManifest.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - 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 2020 Jamie Mansfield
- * Copyright 2020-2021 Petr Mrazek
- *
- * 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 "FTBPackManifest.h"
-
-#include "Json.h"
-
-static void loadSpecs(ModpacksCH::Specs & s, QJsonObject & obj)
-{
- s.id = Json::requireInteger(obj, "id");
- s.minimum = Json::requireInteger(obj, "minimum");
- s.recommended = Json::requireInteger(obj, "recommended");
-}
-
-static void loadTag(ModpacksCH::Tag & t, QJsonObject & obj)
-{
- t.id = Json::requireInteger(obj, "id");
- t.name = Json::requireString(obj, "name");
-}
-
-static void loadArt(ModpacksCH::Art & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.url = Json::requireString(obj, "url");
- a.type = Json::requireString(obj, "type");
- a.width = Json::requireInteger(obj, "width");
- a.height = Json::requireInteger(obj, "height");
- a.compressed = Json::requireBoolean(obj, "compressed");
- a.sha1 = Json::requireString(obj, "sha1");
- a.size = Json::requireInteger(obj, "size");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadAuthor(ModpacksCH::Author & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.name = Json::requireString(obj, "name");
- a.type = Json::requireString(obj, "type");
- a.website = Json::requireString(obj, "website");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionInfo(ModpacksCH::VersionInfo & v, QJsonObject & obj)
-{
- v.id = Json::requireInteger(obj, "id");
- v.name = Json::requireString(obj, "name");
- v.type = Json::requireString(obj, "type");
- v.updated = Json::requireInteger(obj, "updated");
- auto specs = Json::requireObject(obj, "specs");
- loadSpecs(v.specs, specs);
-}
-
-void ModpacksCH::loadModpack(ModpacksCH::Modpack & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.name = Json::requireString(obj, "name");
- m.synopsis = Json::requireString(obj, "synopsis");
- m.description = Json::requireString(obj, "description");
- m.type = Json::requireString(obj, "type");
- m.featured = Json::requireBoolean(obj, "featured");
- m.installs = Json::requireInteger(obj, "installs");
- m.plays = Json::requireInteger(obj, "plays");
- m.updated = Json::requireInteger(obj, "updated");
- m.refreshed = Json::requireInteger(obj, "refreshed");
- auto artArr = Json::requireArray(obj, "art");
- for (QJsonValueRef artRaw : artArr)
- {
- auto artObj = Json::requireObject(artRaw);
- ModpacksCH::Art art;
- loadArt(art, artObj);
- m.art.append(art);
- }
- auto authorArr = Json::requireArray(obj, "authors");
- for (QJsonValueRef authorRaw : authorArr)
- {
- auto authorObj = Json::requireObject(authorRaw);
- ModpacksCH::Author author;
- loadAuthor(author, authorObj);
- m.authors.append(author);
- }
- auto versionArr = Json::requireArray(obj, "versions");
- for (QJsonValueRef versionRaw : versionArr)
- {
- auto versionObj = Json::requireObject(versionRaw);
- ModpacksCH::VersionInfo version;
- loadVersionInfo(version, versionObj);
- m.versions.append(version);
- }
- auto tagArr = Json::requireArray(obj, "tags");
- for (QJsonValueRef tagRaw : tagArr)
- {
- auto tagObj = Json::requireObject(tagRaw);
- ModpacksCH::Tag tag;
- loadTag(tag, tagObj);
- m.tags.append(tag);
- }
- m.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionTarget(ModpacksCH::VersionTarget & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.name = Json::requireString(obj, "name");
- a.type = Json::requireString(obj, "type");
- a.version = Json::requireString(obj, "version");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionFile(ModpacksCH::VersionFile & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.type = Json::requireString(obj, "type");
- a.path = Json::requireString(obj, "path");
- a.name = Json::requireString(obj, "name");
- a.version = Json::requireString(obj, "version");
- a.url = Json::ensureString(obj, "url"); // optional
- a.sha1 = Json::requireString(obj, "sha1");
- a.size = Json::requireInteger(obj, "size");
- a.clientOnly = Json::requireBoolean(obj, "clientonly");
- a.serverOnly = Json::requireBoolean(obj, "serveronly");
- a.optional = Json::requireBoolean(obj, "optional");
- a.updated = Json::requireInteger(obj, "updated");
- auto curseforgeObj = Json::ensureObject(obj, "curseforge"); // optional
- a.curseforge.project_id = Json::ensureInteger(curseforgeObj, "project");
- a.curseforge.file_id = Json::ensureInteger(curseforgeObj, "file");
-}
-
-void ModpacksCH::loadVersion(ModpacksCH::Version & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.parent = Json::requireInteger(obj, "parent");
- m.name = Json::requireString(obj, "name");
- m.type = Json::requireString(obj, "type");
- m.installs = Json::requireInteger(obj, "installs");
- m.plays = Json::requireInteger(obj, "plays");
- m.updated = Json::requireInteger(obj, "updated");
- m.refreshed = Json::requireInteger(obj, "refreshed");
- auto specs = Json::requireObject(obj, "specs");
- loadSpecs(m.specs, specs);
- auto targetArr = Json::requireArray(obj, "targets");
- for (QJsonValueRef targetRaw : targetArr)
- {
- auto versionObj = Json::requireObject(targetRaw);
- ModpacksCH::VersionTarget target;
- loadVersionTarget(target, versionObj);
- m.targets.append(target);
- }
- auto fileArr = Json::requireArray(obj, "files");
- for (QJsonValueRef fileRaw : fileArr)
- {
- auto fileObj = Json::requireObject(fileRaw);
- ModpacksCH::VersionFile file;
- loadVersionFile(file, fileObj);
- m.files.append(file);
- }
-}
-
-//static void loadVersionChangelog(ModpacksCH::VersionChangelog & m, QJsonObject & obj)
-//{
-// m.content = Json::requireString(obj, "content");
-// m.updated = Json::requireInteger(obj, "updated");
-//}
diff --git a/launcher/modplatform/modpacksch/FTBPackManifest.h b/launcher/modplatform/modpacksch/FTBPackManifest.h
deleted file mode 100644
index a8b6f35e..00000000
--- a/launcher/modplatform/modpacksch/FTBPackManifest.h
+++ /dev/null
@@ -1,168 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - 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 2020-2021 Jamie Mansfield
- * Copyright 2020 Petr Mrazek
- *
- * 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
-#include
-#include
-#include
-#include
-
-namespace ModpacksCH
-{
-
-struct Specs
-{
- int id;
- int minimum;
- int recommended;
-};
-
-struct Tag
-{
- int id;
- QString name;
-};
-
-struct Art
-{
- int id;
- QString url;
- QString type;
- int width;
- int height;
- bool compressed;
- QString sha1;
- int size;
- int64_t updated;
-};
-
-struct Author
-{
- int id;
- QString name;
- QString type;
- QString website;
- int64_t updated;
-};
-
-struct VersionInfo
-{
- int id;
- QString name;
- QString type;
- int64_t updated;
- Specs specs;
-};
-
-struct Modpack
-{
- int id;
- QString name;
- QString synopsis;
- QString description;
- QString type;
- bool featured;
- int installs;
- int plays;
- int64_t updated;
- int64_t refreshed;
- QVector art;
- QVector authors;
- QVector versions;
- QVector tags;
-};
-
-struct VersionTarget
-{
- int id;
- QString type;
- QString name;
- QString version;
- int64_t updated;
-};
-
-struct VersionFileCurseForge
-{
- int project_id;
- int file_id;
-};
-
-struct VersionFile
-{
- int id;
- QString type;
- QString path;
- QString name;
- QString version;
- QString url;
- QString sha1;
- int size;
- bool clientOnly;
- bool serverOnly;
- bool optional;
- int64_t updated;
- VersionFileCurseForge curseforge;
-};
-
-struct Version
-{
- int id;
- int parent;
- QString name;
- QString type;
- int installs;
- int plays;
- int64_t updated;
- int64_t refreshed;
- Specs specs;
- QVector targets;
- QVector files;
-};
-
-struct VersionChangelog
-{
- QString content;
- int64_t updated;
-};
-
-void loadModpack(Modpack & m, QJsonObject & obj);
-
-void loadVersion(Version & m, QJsonObject & obj);
-}
-
-Q_DECLARE_METATYPE(ModpacksCH::Modpack)
diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp
index df182f09..64ed7673 100644
--- a/launcher/ui/dialogs/NewInstanceDialog.cpp
+++ b/launcher/ui/dialogs/NewInstanceDialog.cpp
@@ -56,7 +56,6 @@
#include "ui/widgets/PageContainer.h"
#include "ui/pages/modplatform/VanillaPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
-#include "ui/pages/modplatform/ftb/FtbPage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/flame/FlamePage.h"
#include "ui/pages/modplatform/ImportPage.h"
@@ -168,7 +167,6 @@ QList NewInstanceDialog::getPages()
pages.append(new AtlPage(this));
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(new FlamePage(this));
- pages.append(new FtbPage(this));
pages.append(new LegacyFTB::Page(this));
pages.append(new ModrinthPage(this));
pages.append(new TechnicPage(this));
diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
deleted file mode 100644
index e2b548f2..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * 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 "FtbFilterModel.h"
-
-#include
-
-#include "modplatform/modpacksch/FTBPackManifest.h"
-
-#include "StringUtils.h"
-
-namespace Ftb {
-
-FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
-{
- currentSorting = Sorting::ByPlays;
- sortings.insert(tr("Sort by Plays"), Sorting::ByPlays);
- sortings.insert(tr("Sort by Installs"), Sorting::ByInstalls);
- sortings.insert(tr("Sort by Name"), Sorting::ByName);
-}
-
-const QMap FilterModel::getAvailableSortings()
-{
- return sortings;
-}
-
-QString FilterModel::translateCurrentSorting()
-{
- return sortings.key(currentSorting);
-}
-
-void FilterModel::setSorting(Sorting sorting)
-{
- currentSorting = sorting;
- invalidate();
-}
-
-FilterModel::Sorting FilterModel::getCurrentSorting()
-{
- return currentSorting;
-}
-
-void FilterModel::setSearchTerm(const QString& term)
-{
- searchTerm = term.trimmed();
- invalidate();
-}
-
-bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
-{
- if (searchTerm.isEmpty()) {
- return true;
- }
-
- auto index = sourceModel()->index(sourceRow, 0, sourceParent);
- auto pack = sourceModel()->data(index, Qt::UserRole).value();
- return pack.name.contains(searchTerm, Qt::CaseInsensitive);
-}
-
-bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
-{
- ModpacksCH::Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value();
- ModpacksCH::Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value();
-
- if (currentSorting == ByPlays) {
- return leftPack.plays < rightPack.plays;
- }
- else if (currentSorting == ByInstalls) {
- return leftPack.installs < rightPack.installs;
- }
- else if (currentSorting == ByName) {
- return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
- }
-
- // Invalid sorting set, somehow...
- qWarning() << "Invalid sorting set!";
- return true;
-}
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
deleted file mode 100644
index 1be28e99..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * 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 Ftb {
-
-class FilterModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
-public:
- FilterModel(QObject* parent = Q_NULLPTR);
- enum Sorting {
- ByPlays,
- ByInstalls,
- ByName,
- };
- const QMap getAvailableSortings();
- QString translateCurrentSorting();
- void setSorting(Sorting sorting);
- Sorting getCurrentSorting();
- void setSearchTerm(const QString& term);
-
-protected:
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
- bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
-
-private:
- QMap sortings;
- Sorting currentSorting;
- QString searchTerm { "" };
-
-};
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
deleted file mode 100644
index e8065415..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * 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 "FtbListModel.h"
-
-#include "BuildConfig.h"
-#include "Application.h"
-#include "Json.h"
-
-#include
-
-namespace Ftb {
-
-ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
-{
-}
-
-ListModel::~ListModel()
-{
-}
-
-int ListModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : modpacks.size();
-}
-
-int ListModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 1;
-}
-
-QVariant ListModel::data(const QModelIndex &index, int role) const
-{
- int pos = index.row();
- if(pos >= modpacks.size() || pos < 0 || !index.isValid())
- {
- return QString("INVALID INDEX %1").arg(pos);
- }
-
- ModpacksCH::Modpack pack = modpacks.at(pos);
- if(role == Qt::DisplayRole)
- {
- return pack.name;
- }
- else if (role == Qt::ToolTipRole)
- {
- return pack.synopsis;
- }
- else if(role == Qt::DecorationRole)
- {
- QIcon placeholder = APPLICATION->getThemedIcon("screenshot-placeholder");
-
- auto iter = m_logoMap.find(pack.name);
- if (iter != m_logoMap.end()) {
- auto & logo = *iter;
- if(!logo.result.isNull()) {
- return logo.result;
- }
- return placeholder;
- }
-
- for(auto art : pack.art) {
- if(art.type == "square") {
- ((ListModel *)this)->requestLogo(pack.name, art.url);
- }
- }
- return placeholder;
- }
- else if(role == Qt::UserRole)
- {
- QVariant v;
- v.setValue(pack);
- return v;
- }
-
- return QVariant();
-}
-
-void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
-{
- if(m_logoMap.contains(logo))
- {
- callback(APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
- }
- else
- {
- requestLogo(logo, logoUrl);
- }
-}
-
-void ListModel::request()
-{
- m_aborted = false;
-
- beginResetModel();
- modpacks.clear();
- endResetModel();
-
- auto netJob = makeShared("Ftb::Request", APPLICATION->network());
- auto url = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all");
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::requestFinished);
- QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::requestFailed);
-}
-
-void ListModel::abortRequest()
-{
- m_aborted = jobPtr->abort();
- jobPtr.reset();
-}
-
-void ListModel::requestFinished()
-{
- jobPtr.reset();
- remainingPacks.clear();
-
- QJsonParseError parse_error {};
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto packs = doc.object().value("packs").toArray();
- for(auto pack : packs) {
- auto packId = pack.toInt();
- remainingPacks.append(packId);
- }
-
- if(!remainingPacks.isEmpty()) {
- currentPack = remainingPacks.at(0);
- requestPack();
- }
-}
-
-void ListModel::requestFailed(QString reason)
-{
- jobPtr.reset();
- remainingPacks.clear();
-}
-
-void ListModel::requestPack()
-{
- auto netJob = makeShared("Ftb::Search", APPLICATION->network());
- auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1").arg(currentPack);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::packRequestFinished);
- QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::packRequestFailed);
-}
-
-void ListModel::packRequestFinished()
-{
- if (!jobPtr || m_aborted)
- return;
-
- jobPtr.reset();
- remainingPacks.removeOne(currentPack);
-
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
-
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto obj = doc.object();
-
- ModpacksCH::Modpack pack;
- try
- {
- ModpacksCH::loadModpack(pack, obj);
- }
- catch (const JSONValidationError &e)
- {
- qDebug() << QString::fromUtf8(response);
- qWarning() << "Error while reading pack manifest from ModpacksCH: " << e.cause();
- return;
- }
-
- // Since there is no guarantee that packs have a version, this will just
- // ignore those "dud" packs.
- if (pack.versions.empty())
- {
- qWarning() << "ModpacksCH Pack " << pack.id << " ignored. reason: lacking any versions";
- }
- else
- {
- beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
- modpacks.append(pack);
- endInsertRows();
- }
-
- if(!remainingPacks.isEmpty()) {
- currentPack = remainingPacks.at(0);
- requestPack();
- }
-}
-
-void ListModel::packRequestFailed(QString reason)
-{
- jobPtr.reset();
- remainingPacks.removeOne(currentPack);
-}
-
-void ListModel::logoLoaded(QString logo, bool stale)
-{
- auto & logoObj = m_logoMap[logo];
- logoObj.downloadJob.reset();
- QString smallPath = logoObj.fullpath + ".small";
-
- QFileInfo smallInfo(smallPath);
-
- if(stale || !smallInfo.exists()) {
- QImage image(logoObj.fullpath);
- if (image.isNull())
- {
- logoObj.failed = true;
- return;
- }
- QImage small;
- if (image.width() > image.height()) {
- small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
- }
- else {
- small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
- }
- QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
- QImage square(QSize(256, 256), QImage::Format_ARGB32);
- square.fill(Qt::transparent);
-
- QPainter painter(&square);
- painter.drawImage(offset, small);
- painter.end();
-
- square.save(logoObj.fullpath + ".small", "PNG");
- }
-
- logoObj.result = QIcon(logoObj.fullpath + ".small");
- for(int i = 0; i < modpacks.size(); i++) {
- if(modpacks[i].name == logo) {
- emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
- }
- }
-}
-
-void ListModel::logoFailed(QString logo)
-{
- m_logoMap[logo].failed = true;
- m_logoMap[logo].downloadJob.reset();
-}
-
-void ListModel::requestLogo(QString logo, QString url)
-{
- if(m_logoMap.contains(logo)) {
- return;
- }
-
- MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
-
- bool stale = entry->isStale();
-
- auto job = makeShared(QString("ModpacksCH Icon Download %1").arg(logo), APPLICATION->network());
- job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
-
- auto fullPath = entry->getFullPath();
- QObject::connect(job.get(), &NetJob::finished, this, [this, logo, fullPath, stale]
- {
- logoLoaded(logo, stale);
- });
-
- QObject::connect(job.get(), &NetJob::failed, this, [this, logo]
- {
- logoFailed(logo);
- });
-
- auto &newLogoEntry = m_logoMap[logo];
- newLogoEntry.downloadJob = job;
- newLogoEntry.fullpath = fullPath;
- job->start();
-}
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.h b/launcher/ui/pages/modplatform/ftb/FtbListModel.h
deleted file mode 100644
index d7a120f0..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * 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
-
-#include "modplatform/modpacksch/FTBPackManifest.h"
-#include "net/NetJob.h"
-#include
-
-namespace Ftb {
-
-struct Logo {
- QString fullpath;
- NetJob::Ptr downloadJob;
- QIcon result;
- bool failed = false;
-};
-
-typedef QMap LogoMap;
-typedef std::function LogoCallback;
-
-class ListModel : public QAbstractListModel
-{
- Q_OBJECT
-
-public:
- ListModel(QObject *parent);
- virtual ~ListModel();
-
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
-
- void request();
- void abortRequest();
-
- void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
-
- [[nodiscard]] bool isMakingRequest() const { return jobPtr.get(); }
- [[nodiscard]] bool wasAborted() const { return m_aborted; }
-
-private slots:
- void requestFinished();
- void requestFailed(QString reason);
-
- void requestPack();
- void packRequestFinished();
- void packRequestFailed(QString reason);
-
- void logoFailed(QString logo);
- void logoLoaded(QString logo, bool stale);
-
-private:
- void requestLogo(QString file, QString url);
-
-private:
- bool m_aborted = false;
-
- QList modpacks;
- LogoMap m_logoMap;
-
- NetJob::Ptr jobPtr;
- int currentPack;
- QList remainingPacks;
- QByteArray response;
-};
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
deleted file mode 100644
index 7d59a6ae..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield
- * 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 2020-2021 Jamie Mansfield
- * Copyright 2021 Philip T
- *
- * 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 "FtbPage.h"
-#include "ui_FtbPage.h"
-
-#include
-
-#include "ui/dialogs/NewInstanceDialog.h"
-#include "modplatform/modpacksch/FTBPackInstallTask.h"
-
-#include "Markdown.h"
-
-FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), ui(new Ui::FtbPage), dialog(dialog)
-{
- ui->setupUi(this);
-
- filterModel = new Ftb::FilterModel(this);
- listModel = new Ftb::ListModel(this);
- filterModel->setSourceModel(listModel);
- ui->packView->setModel(filterModel);
- ui->packView->setSortingEnabled(true);
- ui->packView->header()->hide();
- ui->packView->setIndentation(0);
-
- ui->searchEdit->installEventFilter(this);
-
- ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
-
- for(int i = 0; i < filterModel->getAvailableSortings().size(); i++)
- {
- ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i));
- }
- ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting());
-
- connect(ui->searchEdit, &QLineEdit::textChanged, this, &FtbPage::triggerSearch);
- connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged);
- connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
- connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
-
- ui->packDescription->setMetaEntry("FTBPacks");
-}
-
-FtbPage::~FtbPage()
-{
- delete ui;
-}
-
-bool FtbPage::eventFilter(QObject* watched, QEvent* event)
-{
- if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast(event);
- if (keyEvent->key() == Qt::Key_Return) {
- triggerSearch();
- keyEvent->accept();
- return true;
- }
- }
- return QWidget::eventFilter(watched, event);
-}
-
-bool FtbPage::shouldDisplay() const
-{
- return true;
-}
-
-void FtbPage::retranslate()
-{
- ui->retranslateUi(this);
-}
-
-void FtbPage::openedImpl()
-{
- if(!initialised || listModel->wasAborted())
- {
- listModel->request();
- initialised = true;
- }
-
- suggestCurrent();
-}
-
-void FtbPage::closedImpl()
-{
- if (listModel->isMakingRequest())
- listModel->abortRequest();
-}
-
-void FtbPage::suggestCurrent()
-{
- if(!isOpened)
- {
- return;
- }
-
- if (selectedVersion.isEmpty())
- {
- dialog->setSuggestedPack();
- return;
- }
-
- dialog->setSuggestedPack(selected.name, selectedVersion, new ModpacksCH::PackInstallTask(selected, selectedVersion, this));
- for(auto art : selected.art) {
- if(art.type == "square") {
- QString editedLogoName;
- editedLogoName = selected.name;
-
- listModel->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
- {
- dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName);
- });
- }
- }
-}
-
-void FtbPage::triggerSearch()
-{
- filterModel->setSearchTerm(ui->searchEdit->text());
-}
-
-void FtbPage::onSortingSelectionChanged(QString data)
-{
- auto toSet = filterModel->getAvailableSortings().value(data);
- filterModel->setSorting(toSet);
-}
-
-void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
-{
- ui->versionSelectionBox->clear();
-
- if(!first.isValid())
- {
- if(isOpened)
- {
- dialog->setSuggestedPack();
- }
- return;
- }
-
- selected = filterModel->data(first, Qt::UserRole).value();
-
- QString output = markdownToHTML(selected.description.toUtf8());
- ui->packDescription->setHtml(output);
-
- // reverse foreach, so that the newest versions are first
- for (auto i = selected.versions.size(); i--;) {
- ui->versionSelectionBox->addItem(selected.versions.at(i).name);
- }
-
- suggestCurrent();
-}
-
-void FtbPage::onVersionSelectionChanged(QString data)
-{
- if(data.isNull() || data.isEmpty())
- {
- selectedVersion = "";
- return;
- }
-
- selectedVersion = data;
- suggestCurrent();
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.h b/launcher/ui/pages/modplatform/ftb/FtbPage.h
deleted file mode 100644
index 631ae7f5..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield
- *
- * 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 "FtbFilterModel.h"
-#include "FtbListModel.h"
-
-#include
-
-#include "Application.h"
-#include "ui/pages/BasePage.h"
-#include "tasks/Task.h"
-
-namespace Ui
-{
- class FtbPage;
-}
-
-class NewInstanceDialog;
-
-class FtbPage : public QWidget, public BasePage
-{
-Q_OBJECT
-
-public:
- explicit FtbPage(NewInstanceDialog* dialog, QWidget *parent = 0);
- virtual ~FtbPage();
- virtual QString displayName() const override
- {
- return "FTB";
- }
- virtual QIcon icon() const override
- {
- return APPLICATION->getThemedIcon("ftb_logo");
- }
- virtual QString id() const override
- {
- return "ftb";
- }
- virtual QString helpPage() const override
- {
- return "FTB-platform";
- }
- virtual bool shouldDisplay() const override;
- void retranslate() override;
-
- void openedImpl() override;
- void closedImpl() override;
-
- bool eventFilter(QObject * watched, QEvent * event) override;
-
-private:
- void suggestCurrent();
-
-private slots:
- void triggerSearch();
-
- void onSortingSelectionChanged(QString data);
- void onSelectionChanged(QModelIndex first, QModelIndex second);
- void onVersionSelectionChanged(QString data);
-
-private:
- Ui::FtbPage *ui = nullptr;
- NewInstanceDialog* dialog = nullptr;
- Ftb::ListModel* listModel = nullptr;
- Ftb::FilterModel* filterModel = nullptr;
-
- ModpacksCH::Modpack selected;
- QString selectedVersion;
-
- bool initialised { false };
-};
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.ui b/launcher/ui/pages/modplatform/ftb/FtbPage.ui
deleted file mode 100644
index 8de0f4e6..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.ui
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
- FtbPage
-
-
-
- 0
- 0
- 875
- 745
-
-
-
- -
-
-
-
-
-
- -
-
-
- Version selected:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
-
- -
-
-
- Search and filter...
-
-
- true
-
-
-
- -
-
-
-
-
-
- true
-
-
-
- 48
- 48
-
-
-
-
- -
-
-
- true
-
-
- true
-
-
-
-
-
-
-
-
-
- ProjectDescriptionPage
- QTextBrowser
- ui/widgets/ProjectDescriptionPage.h
-
-
-
- searchEdit
- versionSelectionBox
-
-
-
-
--
cgit
From d80dee2a54a172fa19c0bc21486ee43ef2e3514d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sat, 29 Apr 2023 19:38:51 -0700
Subject: refactor: pass instance ptr to resource models. use it to find
instance root.
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/minecraft/MinecraftInstance.cpp | 14 +++++++-------
launcher/minecraft/WorldList.cpp | 6 +++---
launcher/minecraft/WorldList.h | 4 +++-
launcher/minecraft/mod/ModFolderModel.cpp | 3 ++-
launcher/minecraft/mod/ModFolderModel.h | 2 +-
launcher/minecraft/mod/ResourceFolderModel.cpp | 7 ++++---
launcher/minecraft/mod/ResourceFolderModel.h | 5 ++++-
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 3 ++-
launcher/minecraft/mod/ResourcePackFolderModel.h | 2 +-
launcher/minecraft/mod/ShaderPackFolderModel.h | 4 +++-
launcher/minecraft/mod/TexturePackFolderModel.cpp | 4 +++-
launcher/minecraft/mod/TexturePackFolderModel.h | 2 +-
launcher/ui/MainWindow.cpp | 4 ++--
launcher/ui/dialogs/CopyInstanceDialog.cpp | 2 +-
tests/ResourceFolderModel_test.cpp | 18 ++++++++++++------
15 files changed, 49 insertions(+), 31 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index af4da5d0..6b7ca84f 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -1111,7 +1111,7 @@ std::shared_ptr MinecraftInstance::loaderModList() const
if (!m_loader_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_loader_mod_list.reset(new ModFolderModel(modsRoot(), is_indexed));
+ m_loader_mod_list.reset(new ModFolderModel(modsRoot(), shared_from_this(), is_indexed));
m_loader_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
}
@@ -1123,7 +1123,7 @@ std::shared_ptr MinecraftInstance::coreModList() const
if (!m_core_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_core_mod_list.reset(new ModFolderModel(coreModsDir(), is_indexed));
+ m_core_mod_list.reset(new ModFolderModel(coreModsDir(), shared_from_this(), is_indexed));
m_core_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
}
@@ -1135,7 +1135,7 @@ std::shared_ptr MinecraftInstance::nilModList() const
if (!m_nil_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), is_indexed, false));
+ m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), shared_from_this(), is_indexed, false));
m_nil_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction);
}
@@ -1146,7 +1146,7 @@ std::shared_ptr MinecraftInstance::resourcePackList() c
{
if (!m_resource_pack_list)
{
- m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir()));
+ m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), shared_from_this()));
}
return m_resource_pack_list;
}
@@ -1155,7 +1155,7 @@ std::shared_ptr MinecraftInstance::texturePackList() con
{
if (!m_texture_pack_list)
{
- m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir()));
+ m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), shared_from_this()));
}
return m_texture_pack_list;
}
@@ -1164,7 +1164,7 @@ std::shared_ptr MinecraftInstance::shaderPackList() const
{
if (!m_shader_pack_list)
{
- m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir()));
+ m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), shared_from_this()));
}
return m_shader_pack_list;
}
@@ -1173,7 +1173,7 @@ std::shared_ptr MinecraftInstance::worldList() const
{
if (!m_world_list)
{
- m_world_list.reset(new WorldList(worldDir()));
+ m_world_list.reset(new WorldList(worldDir(), shared_from_this()));
}
return m_world_list;
}
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 3681bcda..62e55cd4 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -45,8 +45,8 @@
#include
#include
-WorldList::WorldList(const QString &dir)
- : QAbstractListModel(), m_dir(dir)
+WorldList::WorldList(const QString &dir, std::shared_ptr instance)
+ : QAbstractListModel(), m_instance(instance), m_dir(dir)
{
FS::ensureFolderPathExists(m_dir.absolutePath());
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
@@ -129,7 +129,7 @@ bool WorldList::isValid()
}
QString WorldList::instDirPath() const {
- return QFileInfo(m_dir.filePath("../..")).absoluteFilePath();
+ return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
}
bool WorldList::deleteWorld(int index)
diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h
index bd32dd4e..10fb4e3c 100644
--- a/launcher/minecraft/WorldList.h
+++ b/launcher/minecraft/WorldList.h
@@ -21,6 +21,7 @@
#include
#include
#include "minecraft/World.h"
+#include "BaseInstance.h"
class QFileSystemWatcher;
@@ -49,7 +50,7 @@ public:
IconFileRole
};
- WorldList(const QString &dir);
+ WorldList(const QString &dir, std::shared_ptr instance);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
@@ -127,6 +128,7 @@ signals:
void changed();
protected:
+ std::shared_ptr m_instance;
QFileSystemWatcher *m_watcher;
bool is_watching;
QDir m_dir;
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index 91d16175..6ae25d33 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -54,7 +54,8 @@
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
-ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), nullptr, create_dir), m_is_indexed(is_indexed)
+ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr instance, bool is_indexed, bool create_dir)
+ : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
}
diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h
index 84e70db9..46f5087f 100644
--- a/launcher/minecraft/mod/ModFolderModel.h
+++ b/launcher/minecraft/mod/ModFolderModel.h
@@ -75,7 +75,7 @@ public:
Enable,
Toggle
};
- ModFolderModel(const QString &dir, bool is_indexed = false, bool create_dir = true);
+ ModFolderModel(const QString &dir, std::shared_ptr instance, bool is_indexed = false, bool create_dir = true);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index 29a0c736..e1973468 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -16,7 +16,8 @@
#include "tasks/Task.h"
-ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_dir) : QAbstractListModel(parent), m_dir(dir), m_watcher(this)
+ResourceFolderModel::ResourceFolderModel(QDir dir, std::shared_ptr instance, QObject* parent, bool create_dir)
+ : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
{
if (create_dir) {
FS::ensureFolderPathExists(m_dir.absolutePath());
@@ -26,7 +27,7 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
- connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this]{ m_helper_thread_task.clear(); });
+ connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); });
}
ResourceFolderModel::~ResourceFolderModel()
@@ -556,5 +557,5 @@ void ResourceFolderModel::enableInteraction(bool enabled)
}
QString ResourceFolderModel::instDirPath() const {
- return QFileInfo(m_dir.filePath("../..")).absoluteFilePath();
+ return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
}
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index f840b2de..fdf5f331 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -9,6 +9,8 @@
#include "Resource.h"
+#include "BaseInstance.h"
+
#include "tasks/Task.h"
#include "tasks/ConcurrentTask.h"
@@ -24,7 +26,7 @@ class QSortFilterProxyModel;
class ResourceFolderModel : public QAbstractListModel {
Q_OBJECT
public:
- ResourceFolderModel(QDir, QObject* parent = nullptr, bool create_dir = true);
+ ResourceFolderModel(QDir, std::shared_ptr, QObject* parent = nullptr, bool create_dir = true);
~ResourceFolderModel() override;
/** Starts watching the paths for changes.
@@ -189,6 +191,7 @@ class ResourceFolderModel : public QAbstractListModel {
bool m_can_interact = true;
QDir m_dir;
+ std::shared_ptr m_instance;
QFileSystemWatcher m_watcher;
bool m_is_watching = false;
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 0480d8ba..6eba4e2e 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -45,7 +45,8 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
-ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir) : ResourceFolderModel(QDir(dir))
+ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, std::shared_ptr instance)
+ : ResourceFolderModel(QDir(dir), instance)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
}
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h
index cb620ce2..66d5a074 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.h
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.h
@@ -17,7 +17,7 @@ public:
NUM_COLUMNS
};
- explicit ResourcePackFolderModel(const QString &dir);
+ explicit ResourcePackFolderModel(const QString &dir, std::shared_ptr instance);
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h
index a3aa958f..6f3f2811 100644
--- a/launcher/minecraft/mod/ShaderPackFolderModel.h
+++ b/launcher/minecraft/mod/ShaderPackFolderModel.h
@@ -6,5 +6,7 @@ class ShaderPackFolderModel : public ResourceFolderModel {
Q_OBJECT
public:
- explicit ShaderPackFolderModel(const QString& dir) : ResourceFolderModel(QDir(dir)) {}
+ explicit ShaderPackFolderModel(const QString& dir, std::shared_ptr instance)
+ : ResourceFolderModel(QDir(dir), instance)
+ {}
};
diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp
index 5a32cfaf..1e218537 100644
--- a/launcher/minecraft/mod/TexturePackFolderModel.cpp
+++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp
@@ -39,7 +39,9 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
-TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ResourceFolderModel(QDir(dir)) {}
+TexturePackFolderModel::TexturePackFolderModel(const QString& dir, std::shared_ptr instance)
+ : ResourceFolderModel(QDir(dir), instance)
+{}
Task* TexturePackFolderModel::createUpdateTask()
{
diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h
index 261f83b4..246997bd 100644
--- a/launcher/minecraft/mod/TexturePackFolderModel.h
+++ b/launcher/minecraft/mod/TexturePackFolderModel.h
@@ -43,7 +43,7 @@ class TexturePackFolderModel : public ResourceFolderModel
Q_OBJECT
public:
- explicit TexturePackFolderModel(const QString &dir);
+ explicit TexturePackFolderModel(const QString &dir, std::shared_ptr instance);
[[nodiscard]] Task* createUpdateTask() override;
[[nodiscard]] Task* createParseTask(Resource&) override;
};
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index e20a7613..72b7db64 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -1341,10 +1341,10 @@ void MainWindow::on_actionDeleteInstance_triggered()
if (!linkedInstances.empty()) {
response = CustomMessageBox::selectable(
this, tr("There are linked instances"),
- tr("The folowing instance(s) might reference files in this instance:\n\n"
+ tr("The following instance(s) might reference files in this instance:\n\n"
"%1\n\n"
"Deleting it could break the other instance(s), \n\n"
- "Do you wish to proceed?").arg(linkedInstances.join("\n")),
+ "Do you wish to proceed?", nullptr, linkedInstances.count()).arg(linkedInstances.join("\n")),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No
)->exec();
if (response != QMessageBox::Yes)
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 347bd39f..d75bb5fe 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -108,7 +108,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent)
#if defined(Q_OS_WIN)
ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") +
- tr("\nOn windows symbolic links may require admin permission to create."));
+ "\n" + tr("On Windows, symbolic links may require admin permission to create."));
#endif
updateLinkOptions();
diff --git a/tests/ResourceFolderModel_test.cpp b/tests/ResourceFolderModel_test.cpp
index e38b8e93..054d81c4 100644
--- a/tests/ResourceFolderModel_test.cpp
+++ b/tests/ResourceFolderModel_test.cpp
@@ -36,6 +36,7 @@
#include
#include
#include
+#include "BaseInstance.h"
#include
@@ -89,7 +90,9 @@ slots:
QEventLoop loop;
- ModFolderModel m(tempDir.path(), true);
+ InstancePtr instance;
+
+ ModFolderModel m(tempDir.path(), instance, true);
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
@@ -113,7 +116,8 @@ slots:
QString folder = source + '/';
QTemporaryDir tempDir;
QEventLoop loop;
- ModFolderModel m(tempDir.path(), true);
+ InstancePtr instance;
+ ModFolderModel m(tempDir.path(), instance, true);
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
@@ -136,8 +140,8 @@ slots:
void test_addFromWatch()
{
QString source = QFINDTESTDATA("testdata/ResourceFolderModel");
-
- ModFolderModel model(source);
+ InstancePtr instance;
+ ModFolderModel model(source, instance);
QCOMPARE(model.size(), 0);
@@ -157,8 +161,9 @@ slots:
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
QTemporaryDir tmp;
+ InstancePtr instance;
- ResourceFolderModel model(QDir(tmp.path()));
+ ResourceFolderModel model(QDir(tmp.path()), instance);
QCOMPARE(model.size(), 0);
@@ -209,7 +214,8 @@ slots:
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
QTemporaryDir tmp;
- ResourceFolderModel model(tmp.path());
+ InstancePtr instance;
+ ResourceFolderModel model(tmp.path(), instance);
QCOMPARE(model.size(), 0);
--
cgit
From f997529cd4fb077b06d05da9c6ff0c23b85b4ebb Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 30 Mar 2023 11:22:55 -0700
Subject: feat: better task tracking
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 3 +
launcher/net/Download.cpp | 58 +++++++++------
launcher/net/Download.h | 10 +++
launcher/net/NetAction.h | 33 +++++++--
launcher/tasks/ConcurrentTask.cpp | 99 +++++++++++++++++++-------
launcher/tasks/ConcurrentTask.h | 20 +++---
launcher/tasks/Task.cpp | 26 ++++---
launcher/tasks/Task.h | 35 +++++++--
launcher/ui/dialogs/ProgressDialog.cpp | 109 ++++++++++++++++++++++++-----
launcher/ui/dialogs/ProgressDialog.h | 54 +++++++++++---
launcher/ui/dialogs/ProgressDialog.ui | 61 ++++++++--------
launcher/ui/widgets/SubTaskProgressBar.cpp | 58 +++++++++++++++
launcher/ui/widgets/SubTaskProgressBar.h | 50 +++++++++++++
launcher/ui/widgets/SubTaskProgressBar.ui | 70 ++++++++++++++++++
tests/Task_test.cpp | 4 +-
15 files changed, 552 insertions(+), 138 deletions(-)
create mode 100644 launcher/ui/widgets/SubTaskProgressBar.cpp
create mode 100644 launcher/ui/widgets/SubTaskProgressBar.h
create mode 100644 launcher/ui/widgets/SubTaskProgressBar.ui
(limited to 'launcher/ui')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index ee36175f..24330adf 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -909,6 +909,8 @@ SET(LAUNCHER_SOURCES
ui/widgets/VariableSizedImageObject.cpp
ui/widgets/ProjectItem.h
ui/widgets/ProjectItem.cpp
+ ui/widgets/SubTaskProgressBar.h
+ ui/widgets/SubTaskProgressBar.cpp
ui/widgets/VersionListView.cpp
ui/widgets/VersionListView.h
ui/widgets/VersionSelectWidget.cpp
@@ -969,6 +971,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/widgets/CustomCommands.ui
ui/widgets/InfoFrame.ui
ui/widgets/ModFilterWidget.ui
+ ui/widgets/SubTaskProgressBar.ui
ui/widgets/ThemeCustomizationWidget.ui
ui/dialogs/CopyInstanceDialog.ui
ui/dialogs/ProfileSetupDialog.ui
diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp
index e8a1d0b0..26488a43 100644
--- a/launcher/net/Download.cpp
+++ b/launcher/net/Download.cpp
@@ -48,12 +48,15 @@
#include "BuildConfig.h"
#include "Application.h"
+Q_LOGGING_CATEGORY(DownloadLogC, "Task.Net.Download")
+
namespace Net {
auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
{
auto dl = makeShared();
dl->m_url = url;
+ dl->setObjectName(QString("CACHE:") + url.toString());
dl->m_options = options;
auto md5Node = new ChecksumValidator(QCryptographicHash::Md5);
auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal));
@@ -65,6 +68,7 @@ auto Download::makeByteArray(QUrl url, QByteArray* output, Options options) -> D
{
auto dl = makeShared();
dl->m_url = url;
+ dl->setObjectName(QString("BYTES:") + url.toString());
dl->m_options = options;
dl->m_sink.reset(new ByteArraySink(output));
return dl;
@@ -74,6 +78,7 @@ auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Pt
{
auto dl = makeShared();
dl->m_url = url;
+ dl->setObjectName(QString("FILE:") + url.toString());
dl->m_options = options;
dl->m_sink.reset(new FileSink(path));
return dl;
@@ -89,7 +94,7 @@ void Download::executeTask()
setStatus(tr("Downloading %1").arg(m_url.toString()));
if (getState() == Task::State::AbortedByUser) {
- qWarning() << "Attempt to start an aborted Download:" << m_url.toString();
+ qCWarning(DownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
emitAborted();
return;
}
@@ -99,10 +104,10 @@ void Download::executeTask()
switch (m_state) {
case State::Succeeded:
emit succeeded();
- qDebug() << "Download cache hit " << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();
return;
case State::Running:
- qDebug() << "Downloading " << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();
break;
case State::Inactive:
case State::Failed:
@@ -123,9 +128,12 @@ void Download::executeTask()
if (!token.isNull())
request.setRawHeader("Authorization", token.toUtf8());
}
+
+ m_last_progress_time = m_clock.now();
+ m_last_progress_bytes = 0;
QNetworkReply* rep = m_network->get(request);
-
+
m_reply.reset(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished);
@@ -140,13 +148,21 @@ void Download::executeTask()
void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
+ auto now = m_clock.now();
+ auto elapsed = now - m_last_progress_time;
+ auto elapsed_ms = std::chrono::duration_cast(elapsed).count();
+ auto bytes_recived_since = bytesReceived - m_last_progress_bytes;
+
+ auto speed = humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s";
+ m_details = speed;
+
setProgress(bytesReceived, bytesTotal);
}
void Download::downloadError(QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError) {
- qCritical() << "Aborted " << m_url.toString();
+ qCCritical(DownloadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
if (m_options & Option::AcceptLocalFiles) {
@@ -156,7 +172,7 @@ void Download::downloadError(QNetworkReply::NetworkError error)
}
}
// error happened during download.
- qCritical() << "Failed " << m_url.toString() << " with reason " << error;
+ qCCritical(DownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
}
}
@@ -165,9 +181,9 @@ void Download::sslErrors(const QList& errors)
{
int i = 1;
for (auto error : errors) {
- qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
+ qCCritical(DownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
- qCritical() << "Certificate in question:\n" << cert.toText();
+ qCCritical(DownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
@@ -210,17 +226,17 @@ auto Download::handleRedirect() -> bool
*/
redirect = QUrl(redirectStr, QUrl::TolerantMode);
if (!redirect.isValid()) {
- qWarning() << "Failed to parse redirect URL:" << redirectStr;
+ qCWarning(DownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
downloadError(QNetworkReply::ProtocolFailure);
return false;
}
- qDebug() << "Fixed location header:" << redirect;
+ qCDebug(DownloadLogC) << getUid().toString() << "Fixed location header:" << redirect;
} else {
- qDebug() << "Location header:" << redirect;
+ qCDebug(DownloadLogC) << getUid().toString() << "Location header:" << redirect;
}
m_url = QUrl(redirect.toString());
- qDebug() << "Following redirect to " << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
startAction(m_network);
return true;
@@ -230,26 +246,26 @@ void Download::downloadFinished()
{
// handle HTTP redirection first
if (handleRedirect()) {
- qDebug() << "Download redirected:" << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString();
return;
}
// if the download failed before this point ...
if (m_state == State::Succeeded) // pretend to succeed so we continue processing :)
{
- qDebug() << "Download failed but we are allowed to proceed:" << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit succeeded();
return;
} else if (m_state == State::Failed) {
- qDebug() << "Download failed in previous step:" << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
- qDebug() << "Download aborted in previous step:" << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
@@ -259,14 +275,14 @@ void Download::downloadFinished()
// make sure we got all the remaining data, if any
auto data = m_reply->readAll();
if (data.size()) {
- qDebug() << "Writing extra" << data.size() << "bytes";
+ qCDebug(DownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
m_state = m_sink->write(data);
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
- qDebug() << "Download failed to finalize:" << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
@@ -274,7 +290,7 @@ void Download::downloadFinished()
}
m_reply.reset();
- qDebug() << "Download succeeded:" << m_url.toString();
+ qCDebug(DownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();
emit succeeded();
}
@@ -284,11 +300,11 @@ void Download::downloadReadyRead()
auto data = m_reply->readAll();
m_state = m_sink->write(data);
if (m_state == State::Failed) {
- qCritical() << "Failed to process response chunk";
+ qCCritical(DownloadLogC) << getUid().toString() << "Failed to process response chunk";
}
// qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";
} else {
- qCritical() << "Cannot write download data! illegal status " << m_status;
+ qCCritical(DownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status;
}
}
diff --git a/launcher/net/Download.h b/launcher/net/Download.h
index 7e1df322..cbee0d03 100644
--- a/launcher/net/Download.h
+++ b/launcher/net/Download.h
@@ -22,6 +22,7 @@
* 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
*
@@ -36,6 +37,8 @@
#pragma once
+#include
+
#include "HttpMetaCache.h"
#include "NetAction.h"
#include "Sink.h"
@@ -63,6 +66,7 @@ class Download : public NetAction {
void addValidator(Validator* v);
auto abort() -> bool override;
auto canAbort() const -> bool override { return true; };
+ auto getDetails() const -> QString override {return m_details; };
private:
auto handleRedirect() -> bool;
@@ -80,6 +84,12 @@ class Download : public NetAction {
private:
std::unique_ptr m_sink;
Options m_options;
+
+ std::chrono::steady_clock m_clock;
+ std::chrono::time_point m_last_progress_time;
+ qint64 m_last_progress_bytes;
+
+ QString m_details;
};
} // namespace Net
diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h
index 38fe058b..f9456bd6 100644
--- a/launcher/net/NetAction.h
+++ b/launcher/net/NetAction.h
@@ -35,18 +35,39 @@
#pragma once
+#include
+
#include
#include
#include "QObjectPtr.h"
#include "tasks/Task.h"
+static const QStringList s_units_si {"kb", "MB", "GB", "TB"};
+static const QStringList s_units_kibi {"kiB", "MiB", "Gib", "TiB"};
+
+inline QString humanReadableFileSize(qint64 bytes, bool use_si = false, int decimal_points = 1) {
+ const QStringList units = use_si ? s_units_si : s_units_kibi;
+ const int scale = use_si ? 1000 : 1024;
+ double size = bytes;
+
+ int u = -1;
+ double r = pow(10, decimal_points);
+
+ do {
+ size /= scale;
+ u++;
+ } while (round(abs(size) * r) / r >= scale && u < units.length() - 1);
+
+ return QString::number(size, 'f', 2) + " " + units[u];
+}
+
class NetAction : public Task {
Q_OBJECT
- protected:
+protected:
explicit NetAction() : Task() {};
- public:
+public:
using Ptr = shared_qobject_ptr;
virtual ~NetAction() = default;
@@ -55,23 +76,23 @@ class NetAction : public Task {
void setNetwork(shared_qobject_ptr network) { m_network = network; }
- protected slots:
+protected slots:
virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0;
virtual void downloadError(QNetworkReply::NetworkError error) = 0;
virtual void downloadFinished() = 0;
virtual void downloadReadyRead() = 0;
- public slots:
+public slots:
void startAction(shared_qobject_ptr network)
{
m_network = network;
executeTask();
}
- protected:
+protected:
void executeTask() override {};
- public:
+public:
shared_qobject_ptr m_network;
/// the network reply
diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp
index 3cc37b2a..48e1bc18 100644
--- a/launcher/tasks/ConcurrentTask.cpp
+++ b/launcher/tasks/ConcurrentTask.cpp
@@ -15,14 +15,13 @@ ConcurrentTask::~ConcurrentTask()
}
}
-auto ConcurrentTask::getStepProgress() const -> qint64
+auto ConcurrentTask::getStepProgress() const -> QList
{
- return m_stepProgress;
-}
-
-auto ConcurrentTask::getStepTotalProgress() const -> qint64
-{
- return m_stepTotalProgress;
+ QList task_progress;
+ for (auto progress : task_progress) {
+ task_progress.append(task_progress);
+ }
+ return task_progress;
}
void ConcurrentTask::addTask(Task::Ptr task)
@@ -33,10 +32,13 @@ void ConcurrentTask::addTask(Task::Ptr task)
void ConcurrentTask::executeTask()
{
// Start the least amount of tasks needed, but at least one
- int num_starts = qMax(1, qMin(m_total_max_size, m_queue.size()));
- for (int i = 0; i < num_starts; i++) {
- QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
- }
+ // int num_starts = qMax(1, qMin(m_total_max_size, m_queue.size()));
+ // for (int i = 0; i < num_starts; i++) {
+ // QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
+ // }
+ // Start One task, startNext hadles starting the up to the m_total_max_size
+ // while tracking the number currently being done
+ QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
}
bool ConcurrentTask::abort()
@@ -97,17 +99,18 @@ void ConcurrentTask::startNext()
Task::Ptr next = m_queue.dequeue();
- connect(next.get(), &Task::succeeded, this, [this, next] { subTaskSucceeded(next); });
+ connect(next.get(), &Task::succeeded, this, [this, next](){ subTaskSucceeded(next); });
connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
- connect(next.get(), &Task::status, this, &ConcurrentTask::subTaskStatus);
- connect(next.get(), &Task::stepStatus, this, &ConcurrentTask::subTaskStatus);
+ connect(next.get(), &Task::status, this, [this, next](QString msg){ subTaskStatus(next, msg); });
+ connect(next.get(), &Task::stepProgress, this, [this, next](QList tp){ subTaskStepProgress(next, tp); });
- connect(next.get(), &Task::progress, this, &ConcurrentTask::subTaskProgress);
+ connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total){ subTaskProgress(next, current, total); });
m_doing.insert(next.get(), next);
+ m_task_progress.insert(next->getUid(), std::make_shared(TaskStepProgress({next->getUid()})));
+
- setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus());
updateState();
QCoreApplication::processEvents();
@@ -123,7 +126,10 @@ void ConcurrentTask::startNext()
void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
{
m_done.insert(task.get(), task);
+ m_succeeded.insert(task.get(), task);
+
m_doing.remove(task.get());
+ m_task_progress.value(task->getUid())->state = TaskState::Succeeded;
disconnect(task.get(), 0, this, 0);
@@ -138,6 +144,7 @@ void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg)
m_failed.insert(task.get(), task);
m_doing.remove(task.get());
+ m_task_progress.value(task->getUid())->state = TaskState::Failed;
disconnect(task.get(), 0, this, 0);
@@ -146,20 +153,64 @@ void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg)
startNext();
}
-void ConcurrentTask::subTaskStatus(const QString& msg)
+void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)
+{
+ auto taskProgress = m_task_progress.value(task->getUid());
+ taskProgress->status = msg;
+ updateState();
+}
+
+void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 total)
{
- setStepStatus(msg);
+ auto taskProgress = m_task_progress.value(task->getUid());
+
+ taskProgress->current = current;
+ taskProgress->total = total;
+
+ taskProgress->details = task->getDetails();
+
+ updateStepProgress();
+ updateState();
}
-void ConcurrentTask::subTaskProgress(qint64 current, qint64 total)
+void ConcurrentTask::subTaskStepProgress(Task::Ptr task, QList task_step_progress)
{
- m_stepProgress = current;
- m_stepTotalProgress = total;
+ for (auto progress : task_step_progress) {
+ if (!m_task_progress.contains(progress.uid))
+ m_task_progress.insert(progress.uid, std::make_shared(progress));
+
+
+ }
+
+}
+
+void ConcurrentTask::updateStepProgress()
+{
+ qint64 current = 0, total = 0;
+ for ( auto taskProgress : m_task_progress ) {
+ current += taskProgress->current;
+ total += taskProgress->total;
+ }
+
+ m_stepProgress = current;
+ m_stepTotalProgress = total;
}
void ConcurrentTask::updateState()
{
- setProgress(m_done.count(), totalSize());
- setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)")
- .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
+ if (totalSize() > 1) {
+ setProgress(m_done.count(), totalSize());
+ setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)").arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize())));
+ } else {
+ setProgress(m_stepProgress, m_stepTotalProgress);
+ QString status = tr("Please wait ...");
+ if (m_queue.size() > 0) {
+ status = tr("Waiting for 1 task to start ...");
+ } else if (m_doing.size() > 0) {
+ status = tr("Executing 1 task:");
+ } else if (m_done.size() > 0) {
+ status = tr("Task finished.");
+ }
+ setStatus(status);
+ }
}
diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h
index d074d2e2..93469766 100644
--- a/launcher/tasks/ConcurrentTask.h
+++ b/launcher/tasks/ConcurrentTask.h
@@ -1,7 +1,10 @@
#pragma once
+#include
+#include
#include
#include
+#include
#include "tasks/Task.h"
@@ -16,10 +19,7 @@ public:
bool canAbort() const override { return true; }
inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; };
- auto getStepProgress() const -> qint64 override;
- auto getStepTotalProgress() const -> qint64 override;
-
- inline auto getStepStatus() const -> QString override { return m_step_status; }
+ auto getStepProgress() const -> QList override;
void addTask(Task::Ptr task);
@@ -39,14 +39,15 @@ slots:
void subTaskSucceeded(Task::Ptr);
void subTaskFailed(Task::Ptr, const QString &msg);
- void subTaskStatus(const QString &msg);
- void subTaskProgress(qint64 current, qint64 total);
+ void subTaskStatus(Task::Ptr task, const QString &msg);
+ void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
+ void subTaskStepProgress(Task::Ptr task, QList task_step_progress);
protected:
// NOTE: This is not thread-safe.
[[nodiscard]] unsigned int totalSize() const { return m_queue.size() + m_doing.size() + m_done.size(); }
- void setStepStatus(QString status) { m_step_status = status; emit stepStatus(status); };
+ void updateStepProgress();
virtual void updateState();
@@ -56,9 +57,12 @@ protected:
QQueue m_queue;
- QHash m_doing;
+ QHash m_doing;
QHash m_done;
QHash m_failed;
+ QHash m_succeeded;
+
+ QHash> m_task_progress;
int m_total_max_size;
diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp
index 9ea1bb26..452dc2e3 100644
--- a/launcher/tasks/Task.cpp
+++ b/launcher/tasks/Task.cpp
@@ -37,8 +37,11 @@
#include
+Q_LOGGING_CATEGORY(TaskLogC, "Task")
+
Task::Task(QObject *parent, bool show_debug) : QObject(parent), m_show_debug(show_debug)
{
+ m_uid = QUuid::createUuid();
setAutoDelete(false);
}
@@ -65,31 +68,31 @@ void Task::start()
case State::Inactive:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "starting for the first time";
+ qCDebug(TaskLogC) << "Task" << describe() << "starting for the first time";
break;
}
case State::AbortedByUser:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "restarting for after being aborted by user";
+ qCDebug(TaskLogC) << "Task" << describe() << "restarting for after being aborted by user";
break;
}
case State::Failed:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "restarting for after failing at first";
+ qCDebug(TaskLogC) << "Task" << describe() << "restarting for after failing at first";
break;
}
case State::Succeeded:
{
if (m_show_debug)
- qDebug() << "Task" << describe() << "restarting for after succeeding at first";
+ qCDebug(TaskLogC) << "Task" << describe() << "restarting for after succeeding at first";
break;
}
case State::Running:
{
if (m_show_debug)
- qWarning() << "The launcher tried to start task" << describe() << "while it was already running!";
+ qCWarning(TaskLogC) << "The launcher tried to start task" << describe() << "while it was already running!";
return;
}
}
@@ -104,12 +107,12 @@ void Task::emitFailed(QString reason)
// Don't fail twice.
if (!isRunning())
{
- qCritical() << "Task" << describe() << "failed while not running!!!!: " << reason;
+ qCCritical(TaskLogC) << "Task" << describe() << "failed while not running!!!!: " << reason;
return;
}
m_state = State::Failed;
m_failReason = reason;
- qCritical() << "Task" << describe() << "failed: " << reason;
+ qCCritical(TaskLogC) << "Task" << describe() << "failed: " << reason;
emit failed(reason);
emit finished();
}
@@ -119,13 +122,13 @@ void Task::emitAborted()
// Don't abort twice.
if (!isRunning())
{
- qCritical() << "Task" << describe() << "aborted while not running!!!!";
+ qCCritical(TaskLogC) << "Task" << describe() << "aborted while not running!!!!";
return;
}
m_state = State::AbortedByUser;
m_failReason = "Aborted.";
if (m_show_debug)
- qDebug() << "Task" << describe() << "aborted.";
+ qCDebug(TaskLogC) << "Task" << describe() << "aborted.";
emit aborted();
emit finished();
}
@@ -135,12 +138,12 @@ void Task::emitSucceeded()
// Don't succeed twice.
if (!isRunning())
{
- qCritical() << "Task" << describe() << "succeeded while not running!!!!";
+ qCCritical(TaskLogC) << "Task" << describe() << "succeeded while not running!!!!";
return;
}
m_state = State::Succeeded;
if (m_show_debug)
- qDebug() << "Task" << describe() << "succeeded";
+ qCDebug(TaskLogC) << "Task" << describe() << "succeeded";
emit succeeded();
emit finished();
}
@@ -159,6 +162,7 @@ QString Task::describe()
{
out << name;
}
+ out << " ID: " << m_uid.toString(QUuid::WithoutBraces);
out << QChar(')');
out.flush();
return outStr;
diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h
index 3d607dca..a6ab15b8 100644
--- a/launcher/tasks/Task.h
+++ b/launcher/tasks/Task.h
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
- * PolyMC - Minecraft Launcher
+ * PrismLauncher - Minecraft Launcher
* Copyright (c) 2022 flowln
+ * 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
@@ -36,9 +37,29 @@
#pragma once
#include
+#include
+#include
#include "QObjectPtr.h"
+enum class TaskState {
+ Waiting,
+ Running,
+ Failed,
+ Succeeded,
+ Finished
+};
+
+struct TaskStepProgress {
+ QUuid uid;
+ qint64 current;
+ qint64 total;
+ QString status;
+ QString details;
+ TaskState state = TaskState::Waiting;
+ bool isDone() { return (state == TaskState::Failed) || (state == TaskState::Succeeded) || (state == TaskState::Finished); }
+};
+
class Task : public QObject, public QRunnable {
Q_OBJECT
public:
@@ -73,12 +94,14 @@ class Task : public QObject, public QRunnable {
auto getState() const -> State { return m_state; }
QString getStatus() { return m_status; }
- virtual auto getStepStatus() const -> QString { return m_status; }
qint64 getProgress() { return m_progress; }
qint64 getTotalProgress() { return m_progressTotal; }
- virtual auto getStepProgress() const -> qint64 { return 0; }
- virtual auto getStepTotalProgress() const -> qint64 { return 100; }
+ virtual auto getStepProgress() const -> QList { return {}; }
+
+ virtual auto getDetails() const -> QString { return ""; }
+
+ QUuid getUid() { return m_uid; }
protected:
void logWarning(const QString& line);
@@ -94,7 +117,7 @@ class Task : public QObject, public QRunnable {
void aborted();
void failed(QString reason);
void status(QString status);
- void stepStatus(QString status);
+ void stepProgress(QList task_progress); //
/** Emitted when the canAbort() status has changed.
*/
@@ -135,4 +158,6 @@ class Task : public QObject, public QRunnable {
private:
// Change using setAbortStatus
bool m_can_abort = false;
+ QUuid m_uid;
+
};
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index da73a449..da627af3 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -1,26 +1,66 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
- * 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
+ * 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.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
*
- * 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.
+ * 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 "ProgressDialog.h"
#include "ui_ProgressDialog.h"
+#include
#include
#include
#include "tasks/Task.h"
+#include "ui/widgets/SubTaskProgressBar.h"
+
+
+template
+int map_int_range(T value)
+{
+ auto type_min = std::numeric_limits::min();
+ auto type_max = std::numeric_limits::max();
+
+ auto int_min = std::numeric_limits::min();
+ auto int_max = std::numeric_limits::max();
+
+ auto type_range = type_max - type_min;
+ auto int_range = int_max - int_min;
+
+ return static_cast((value - type_min) * int_range / type_range + int_min);
+}
+
+
ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog)
{
ui->setupUi(this);
@@ -79,7 +119,7 @@ int ProgressDialog::execWithTask(Task* task)
connect(task, &Task::failed, this, &ProgressDialog::onTaskFailed);
connect(task, &Task::succeeded, this, &ProgressDialog::onTaskSucceeded);
connect(task, &Task::status, this, &ProgressDialog::changeStatus);
- connect(task, &Task::stepStatus, this, &ProgressDialog::changeStatus);
+ connect(task, &Task::stepProgress, this, &ProgressDialog::changeStepProgress);
connect(task, &Task::progress, this, &ProgressDialog::changeProgress);
connect(task, &Task::aborted, this, &ProgressDialog::hide);
@@ -149,23 +189,54 @@ void ProgressDialog::onTaskSucceeded()
void ProgressDialog::changeStatus(const QString& status)
{
ui->globalStatusLabel->setText(task->getStatus());
- ui->statusLabel->setText(task->getStepStatus());
+ // ui->statusLabel->setText(task->getStepStatus());
updateSize();
}
+void ProgressDialog::addTaskProgress(TaskStepProgress progress)
+{
+ SubTaskProgressBar* task_bar = new SubTaskProgressBar(this);
+ taskProgress.insert(progress.uid, task_bar);
+ ui->taskProgressLayout->addWidget(task_bar);
+}
+
+void ProgressDialog::changeStepProgress(QList task_progress)
+{
+ for (auto tp : task_progress) {
+ if (!taskProgress.contains(tp.uid))
+ addTaskProgress(tp);
+ auto task_bar = taskProgress.value(tp.uid);
+
+ if (tp.total < 0) {
+ task_bar->setRange(0, 0);
+ } else {
+ task_bar->setRange(0, map_int_range(tp.total));
+ }
+
+ task_bar->setValue(map_int_range(tp.current));
+ task_bar->setStatus(tp.status);
+ task_bar->setDetails(tp.details);
+
+ if (tp.isDone()) {
+ task_bar->setVisible(false);
+ }
+
+ }
+}
+
void ProgressDialog::changeProgress(qint64 current, qint64 total)
{
ui->globalProgressBar->setMaximum(total);
ui->globalProgressBar->setValue(current);
- if (!m_is_multi_step) {
- ui->taskProgressBar->setMaximum(total);
- ui->taskProgressBar->setValue(current);
- } else {
- ui->taskProgressBar->setMaximum(task->getStepProgress());
- ui->taskProgressBar->setValue(task->getStepTotalProgress());
- }
+ // if (!m_is_multi_step) {
+ // ui->taskProgressBar->setMaximum(total);
+ // ui->taskProgressBar->setValue(current);
+ // } else {
+ // ui->taskProgressBar->setMaximum(task->getStepProgress());
+ // ui->taskProgressBar->setValue(task->getStepTotalProgress());
+ // }
}
void ProgressDialog::keyPressEvent(QKeyEvent* e)
diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h
index 0b4b78a4..a7e203fb 100644
--- a/launcher/ui/dialogs/ProgressDialog.h
+++ b/launcher/ui/dialogs/ProgressDialog.h
@@ -1,22 +1,50 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
- * 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
+ * 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.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
*
- * 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.
+ * 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
#include
+#include
+#include
+
+#include "QObjectPtr.h"
+#include "tasks/Task.h"
+
+#include "ui/widgets/SubTaskProgressBar.h"
class Task;
class SequentialTask;
@@ -52,6 +80,7 @@ slots:
void changeStatus(const QString &status);
void changeProgress(qint64 current, qint64 total);
+ void changeStepProgress(QList task_progress);
private
@@ -64,6 +93,7 @@ protected:
private:
bool handleImmediateResult(QDialog::DialogCode &result);
+ void addTaskProgress(TaskStepProgress progress);
private:
Ui::ProgressDialog *ui;
@@ -71,4 +101,8 @@ private:
Task *task;
bool m_is_multi_step = false;
+ QHash taskProgress;
+
+
};
+
diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui
index 34ab71e3..0a998987 100644
--- a/launcher/ui/dialogs/ProgressDialog.ui
+++ b/launcher/ui/dialogs/ProgressDialog.ui
@@ -2,6 +2,20 @@
ProgressDialog
+
+
+ 0
+ 0
+ 400
+ 109
+
+
+
+
+ 0
+ 0
+
+
400
@@ -18,6 +32,16 @@
Please wait...
+ -
+
+
+ true
+
+
+ 24
+
+
+
-
@@ -31,15 +55,11 @@
- -
-
-
- Global Task Status...
-
-
+
-
+
- -
-
+
-
+
0
@@ -47,30 +67,7 @@
- Task Status...
-
-
- true
-
-
-
- -
-
-
- 24
-
-
- false
-
-
-
- -
-
-
- true
-
-
- 24
+ Global Task Status...
diff --git a/launcher/ui/widgets/SubTaskProgressBar.cpp b/launcher/ui/widgets/SubTaskProgressBar.cpp
new file mode 100644
index 00000000..84ea5f20
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.cpp
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2022 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 .
+ *
+ */
+
+#include "SubTaskProgressBar.h"
+#include "ui_SubTaskProgressBar.h"
+
+unique_qobject_ptr SubTaskProgressBar::create(QWidget* parent)
+{
+ auto progress_bar = new SubTaskProgressBar(parent);
+ return unique_qobject_ptr(progress_bar);
+}
+
+SubTaskProgressBar::SubTaskProgressBar(QWidget* parent)
+ : ui(new Ui::SubTaskProgressBar)
+{
+ ui->setupUi(this);
+}
+SubTaskProgressBar::~SubTaskProgressBar()
+{
+ delete ui;
+}
+
+void SubTaskProgressBar::setRange(int min, int max)
+{
+ ui->progressBar->setRange(min, max);
+}
+
+void SubTaskProgressBar::setValue(int value)
+{
+ ui->progressBar->setValue(value);
+}
+
+void SubTaskProgressBar::setStatus(QString status)
+{
+ ui->statusLabel->setText(status);
+}
+
+void SubTaskProgressBar::setDetails(QString details)
+{
+ ui->statusDetailsLabel->setText(details);
+}
+
diff --git a/launcher/ui/widgets/SubTaskProgressBar.h b/launcher/ui/widgets/SubTaskProgressBar.h
new file mode 100644
index 00000000..3375a0bc
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2022 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
+#include
+#include
+#include "QObjectPtr.h"
+
+namespace Ui {
+class SubTaskProgressBar;
+}
+
+class SubTaskProgressBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ static unique_qobject_ptr create(QWidget* parent = nullptr);
+
+ SubTaskProgressBar(QWidget* parent = nullptr);
+ ~SubTaskProgressBar();
+
+ void setRange(int min, int max);
+ void setValue(int value);
+ void setStatus(QString status);
+ void setDetails(QString details);
+
+
+
+private:
+ Ui::SubTaskProgressBar* ui;
+
+};
diff --git a/launcher/ui/widgets/SubTaskProgressBar.ui b/launcher/ui/widgets/SubTaskProgressBar.ui
new file mode 100644
index 00000000..966fdb88
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.ui
@@ -0,0 +1,70 @@
+
+
+ SubTaskProgressBar
+
+
+
+ 0
+ 0
+ 265
+ 65
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Sub Task Status...
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Status Details
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ 24
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp
index 95eb4a30..678382ba 100644
--- a/tests/Task_test.cpp
+++ b/tests/Task_test.cpp
@@ -99,7 +99,7 @@ class TaskTest : public QObject {
t.setStatus(status);
QCOMPARE(t.getStatus(), status);
- QCOMPARE(t.getStepStatus(), status);
+ QCOMPARE(t.getStepProgress().isEmpty(), QList{}.isEmpty());
}
void test_SetStatus_MultiStep(){
@@ -111,7 +111,7 @@ class TaskTest : public QObject {
QCOMPARE(t.getStatus(), status);
// Even though it is multi step, it does not override the getStepStatus method,
// so it should remain the same.
- QCOMPARE(t.getStepStatus(), status);
+ QCOMPARE(t.getStepProgress().isEmpty(), QList{}.isEmpty());
}
void test_SetProgress(){
--
cgit
From 9d2f0e4dc8fc3995052770c6a7948cb0372fdcbb Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 30 Mar 2023 23:50:29 -0700
Subject: feat: Propogated subtask progress
Oh boy this is big.
> TaskStepProgress struct is now QMetaObject compatabile and can be sent through signals
> Task now has a method to propogates sub task progress it must be signal bound by each task containing a task wishing to report progress of it's children.
> Downloads report speed
> Tasks now have UUIDS to track them
- use when reporting
- use when logging
- use when storeing them or objects related to them
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/InstanceImportTask.cpp | 3 +
launcher/InstanceList.cpp | 1 +
launcher/ResourceDownloadTask.cpp | 1 +
launcher/launch/steps/Update.cpp | 5 +-
launcher/minecraft/MinecraftLoadAndCheck.cpp | 1 +
launcher/minecraft/MinecraftUpdate.cpp | 2 +
launcher/minecraft/update/AssetUpdateTask.cpp | 2 +
launcher/minecraft/update/FMLLibrariesTask.cpp | 1 +
launcher/minecraft/update/LibrariesTask.cpp | 2 +
.../modplatform/atlauncher/ATLPackInstallTask.cpp | 2 +
.../flame/FlameInstanceCreationTask.cpp | 4 +-
.../modplatform/legacy_ftb/PackInstallTask.cpp | 1 +
.../modrinth/ModrinthInstanceCreationTask.cpp | 2 +
.../technic/SingleZipPackInstallTask.cpp | 1 +
.../modplatform/technic/SolderPackInstallTask.cpp | 1 +
launcher/net/Download.cpp | 39 +++++++-
launcher/tasks/ConcurrentTask.cpp | 44 +++++----
launcher/tasks/ConcurrentTask.h | 6 +-
launcher/tasks/Task.cpp | 5 +
launcher/tasks/Task.h | 26 +++--
launcher/ui/dialogs/ProgressDialog.cpp | 65 +++++++------
launcher/ui/dialogs/ProgressDialog.h | 4 +-
launcher/ui/dialogs/ProgressDialog.ui | 108 +++++++++++++++++----
launcher/ui/widgets/SubTaskProgressBar.ui | 31 +++++-
tests/Task_test.cpp | 9 +-
25 files changed, 269 insertions(+), 97 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 080828a8..c196396d 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -98,6 +98,7 @@ void InstanceImportTask::executeTask()
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);
@@ -291,6 +292,7 @@ void InstanceImportTask::processFlame()
});
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
+ connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
@@ -382,6 +384,7 @@ void InstanceImportTask::processModrinth()
});
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
+ connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp
index 68e3e92c..dbc891ff 100644
--- a/launcher/InstanceList.cpp
+++ b/launcher/InstanceList.cpp
@@ -788,6 +788,7 @@ class InstanceStaging : public Task {
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
connect(child, &Task::status, this, &InstanceStaging::setStatus);
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
+ connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress);
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
}
diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp
index 98bcf259..61b918aa 100644
--- a/launcher/ResourceDownloadTask.cpp
+++ b/launcher/ResourceDownloadTask.cpp
@@ -53,6 +53,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack,
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed);
addTask(m_filesNetJob);
diff --git a/launcher/launch/steps/Update.cpp b/launcher/launch/steps/Update.cpp
index 28bd153d..1640d115 100644
--- a/launcher/launch/steps/Update.cpp
+++ b/launcher/launch/steps/Update.cpp
@@ -27,8 +27,9 @@ void Update::executeTask()
if(m_updateTask)
{
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
- connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
- connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
+ connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress);
+ connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propogateStepProgress);
+ connect(m_updateTask.get(), &Task::status, this, &Update::setStatus);
emit progressReportingRequest();
return;
}
diff --git a/launcher/minecraft/MinecraftLoadAndCheck.cpp b/launcher/minecraft/MinecraftLoadAndCheck.cpp
index d72bc7be..1c3f6fb7 100644
--- a/launcher/minecraft/MinecraftLoadAndCheck.cpp
+++ b/launcher/minecraft/MinecraftLoadAndCheck.cpp
@@ -22,6 +22,7 @@ void MinecraftLoadAndCheck::executeTask()
connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); });
connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
+ connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propogateStepProgress);
connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
}
diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp
index 07ad4882..3ce808f8 100644
--- a/launcher/minecraft/MinecraftUpdate.cpp
+++ b/launcher/minecraft/MinecraftUpdate.cpp
@@ -100,6 +100,7 @@ void MinecraftUpdate::next()
disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
disconnect(task.get(), &Task::aborted, this, &Task::abort);
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
+ disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
}
if(m_currentTask == m_tasks.size())
@@ -118,6 +119,7 @@ void MinecraftUpdate::next()
connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed);
connect(task.get(), &Task::aborted, this, &Task::abort);
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
+ connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
// if the task is already running, do not start it again
if(!task->isRunning())
diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp
index 8ccb0e1d..31fd5eb1 100644
--- a/launcher/minecraft/update/AssetUpdateTask.cpp
+++ b/launcher/minecraft/update/AssetUpdateTask.cpp
@@ -45,6 +45,7 @@ void AssetUpdateTask::executeTask()
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
+ connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress);
qDebug() << m_inst->name() << ": Starting asset index download";
downloadJob->start();
@@ -83,6 +84,7 @@ void AssetUpdateTask::assetIndexFinished()
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
+ connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress);
downloadJob->start();
return;
}
diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp
index 96fd3ba3..75e5c572 100644
--- a/launcher/minecraft/update/FMLLibrariesTask.cpp
+++ b/launcher/minecraft/update/FMLLibrariesTask.cpp
@@ -75,6 +75,7 @@ void FMLLibrariesTask::executeTask()
connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed);
connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress);
+ connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propogateStepProgress);
downloadJob.reset(dljob);
downloadJob->start();
}
diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp
index b9410111..415b9a66 100644
--- a/launcher/minecraft/update/LibrariesTask.cpp
+++ b/launcher/minecraft/update/LibrariesTask.cpp
@@ -70,6 +70,8 @@ void LibrariesTask::executeTask()
connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); });
connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
+ connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propogateStepProgress);
+
downloadJob->start();
}
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
index 4bd8b7f2..28026732 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
@@ -683,6 +683,7 @@ void PackInstallTask::installConfigs()
abortable = true;
setProgress(current, total);
});
+ connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&]{
abortable = false;
jobPtr.reset();
@@ -849,6 +850,7 @@ void PackInstallTask::downloadMods()
abortable = true;
setProgress(current, total);
});
+ connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(jobPtr.get(), &NetJob::aborted, [&]
{
abortable = false;
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index 964b559c..3cb6b61a 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -35,6 +35,7 @@
#include "FlameInstanceCreationTask.h"
+#include "modplatform/flame/FileResolvingTask.h"
#include "modplatform/flame/FlameAPI.h"
#include "modplatform/flame/PackManifest.h"
@@ -382,7 +383,7 @@ bool FlameCreationTask::createInstance()
});
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress);
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus);
-
+ connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress);
m_mod_id_resolver->start();
loop.exec();
@@ -497,6 +498,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
setError(reason);
});
connect(m_files_job.get(), &NetJob::progress, this, &FlameCreationTask::setProgress);
+ connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress);
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
setStatus(tr("Downloading mods..."));
diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
index 8d45fc5c..36c142ac 100644
--- a/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
+++ b/launcher/modplatform/legacy_ftb/PackInstallTask.cpp
@@ -81,6 +81,7 @@ void PackInstallTask::downloadPack()
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
+ connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
netJobContainer->start();
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
index 6814e645..2fb656ea 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -11,6 +11,7 @@
#include "net/ChecksumValidator.h"
+#include "net/NetJob.h"
#include "settings/INISettingsObject.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -263,6 +264,7 @@ bool ModrinthCreationTask::createInstance()
});
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); });
+ connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress);
setStatus(tr("Downloading mods..."));
m_files_job->start();
diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
index 8fd43d21..f07ca24a 100644
--- a/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SingleZipPackInstallTask.cpp
@@ -50,6 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask()
auto job = m_filesNetJob.get();
connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged);
+ connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress);
connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed);
m_filesNetJob->start();
}
diff --git a/launcher/modplatform/technic/SolderPackInstallTask.cpp b/launcher/modplatform/technic/SolderPackInstallTask.cpp
index 77c503f0..c26d6a5a 100644
--- a/launcher/modplatform/technic/SolderPackInstallTask.cpp
+++ b/launcher/modplatform/technic/SolderPackInstallTask.cpp
@@ -127,6 +127,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress);
connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted);
m_filesNetJob->start();
diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp
index 26488a43..a4c3ebfc 100644
--- a/launcher/net/Download.cpp
+++ b/launcher/net/Download.cpp
@@ -36,6 +36,8 @@
*/
#include "Download.h"
+#include
+#include
#include
#include
@@ -52,6 +54,33 @@ Q_LOGGING_CATEGORY(DownloadLogC, "Task.Net.Download")
namespace Net {
+QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false)
+{
+ auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments;
+ auto str_url = url.toDisplayString(display_options);
+ if (str_url.length() <= max_len)
+ return str_url;
+
+ QRegularExpression re(R"(^([\w]+:\/\/)([\w._-]+\/)([\w._-]+\/).*(\/[^]+[^]+)$)");
+
+ auto url_compact = QString(str_url);
+ url_compact.replace(re, "\\1\\2\\3...\\4");
+ if (url_compact.length() >= max_len) {
+ auto url_compact = QString(str_url);
+ url_compact.replace(re, "\\1\\2...\\4");
+ }
+
+
+ if ((url_compact.length() >= max_len) && hard_limit) {
+ auto to_remove = url_compact.length() - max_len + 3;
+ url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
+ url_compact.append("...");
+ }
+
+ return url_compact;
+
+}
+
auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr
{
auto dl = makeShared();
@@ -91,7 +120,7 @@ void Download::addValidator(Validator* v)
void Download::executeTask()
{
- setStatus(tr("Downloading %1").arg(m_url.toString()));
+ setStatus(tr("Downloading %1").arg(truncateUrlHumanFriendly(m_url, 60)));
if (getState() == Task::State::AbortedByUser) {
qCWarning(DownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
@@ -152,9 +181,11 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
auto elapsed = now - m_last_progress_time;
auto elapsed_ms = std::chrono::duration_cast(elapsed).count();
auto bytes_recived_since = bytesReceived - m_last_progress_bytes;
-
- auto speed = humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s";
- m_details = speed;
+ if (elapsed_ms > 0) {
+ m_details = humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s";
+ } else {
+ m_details = "0 b/s";
+ }
setProgress(bytesReceived, bytesTotal);
}
diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp
index 48e1bc18..fde7d0ad 100644
--- a/launcher/tasks/ConcurrentTask.cpp
+++ b/launcher/tasks/ConcurrentTask.cpp
@@ -2,6 +2,7 @@
#include
#include
+#include "tasks/Task.h"
ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concurrent)
: Task(parent), m_name(task_name), m_total_max_size(max_concurrent)
@@ -15,13 +16,9 @@ ConcurrentTask::~ConcurrentTask()
}
}
-auto ConcurrentTask::getStepProgress() const -> QList
+auto ConcurrentTask::getStepProgress() const -> TaskStepProgressList
{
- QList task_progress;
- for (auto progress : task_progress) {
- task_progress.append(task_progress);
- }
- return task_progress;
+ return m_task_progress.values();
}
void ConcurrentTask::addTask(Task::Ptr task)
@@ -103,7 +100,7 @@ void ConcurrentTask::startNext()
connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); });
connect(next.get(), &Task::status, this, [this, next](QString msg){ subTaskStatus(next, msg); });
- connect(next.get(), &Task::stepProgress, this, [this, next](QList tp){ subTaskStepProgress(next, tp); });
+ connect(next.get(), &Task::stepProgress, this, [this, next](TaskStepProgressList tp){ subTaskStepProgress(next, tp); });
connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total){ subTaskProgress(next, current, total); });
@@ -112,6 +109,7 @@ void ConcurrentTask::startNext()
updateState();
+ updateStepProgress();
QCoreApplication::processEvents();
@@ -129,12 +127,12 @@ void ConcurrentTask::subTaskSucceeded(Task::Ptr task)
m_succeeded.insert(task.get(), task);
m_doing.remove(task.get());
- m_task_progress.value(task->getUid())->state = TaskState::Succeeded;
+ m_task_progress.value(task->getUid())->state = TaskStepState::Succeeded;
disconnect(task.get(), 0, this, 0);
updateState();
-
+ updateStepProgress();
startNext();
}
@@ -144,20 +142,22 @@ void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg)
m_failed.insert(task.get(), task);
m_doing.remove(task.get());
- m_task_progress.value(task->getUid())->state = TaskState::Failed;
+ m_task_progress.value(task->getUid())->state = TaskStepState::Failed;
disconnect(task.get(), 0, this, 0);
updateState();
-
+ updateStepProgress();
startNext();
}
void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg)
{
auto taskProgress = m_task_progress.value(task->getUid());
- taskProgress->status = msg;
+ taskProgress->status = msg;
+ taskProgress->state = TaskStepState::Running;
updateState();
+ updateStepProgress();
}
void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 total)
@@ -166,21 +166,28 @@ void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 tota
taskProgress->current = current;
taskProgress->total = total;
-
+ taskProgress->state = TaskStepState::Running;
taskProgress->details = task->getDetails();
updateStepProgress();
updateState();
}
-void ConcurrentTask::subTaskStepProgress(Task::Ptr task, QList task_step_progress)
+void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgressList task_step_progress)
{
for (auto progress : task_step_progress) {
- if (!m_task_progress.contains(progress.uid))
- m_task_progress.insert(progress.uid, std::make_shared(progress));
-
-
+ if (!m_task_progress.contains(progress->uid)) {
+ m_task_progress.insert(progress->uid, progress);
+ } else {
+ auto tp = m_task_progress.value(progress->uid);
+ tp->current = progress->current;
+ tp->total = progress->total;
+ tp->status = progress->status;
+ tp->details = progress->details;
+ }
}
+
+ updateStepProgress();
}
@@ -194,6 +201,7 @@ void ConcurrentTask::updateStepProgress()
m_stepProgress = current;
m_stepTotalProgress = total;
+ emit stepProgress(m_task_progress.values());
}
void ConcurrentTask::updateState()
diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h
index 93469766..9d4413c6 100644
--- a/launcher/tasks/ConcurrentTask.h
+++ b/launcher/tasks/ConcurrentTask.h
@@ -18,8 +18,8 @@ public:
bool canAbort() const override { return true; }
- inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; };
- auto getStepProgress() const -> QList override;
+ inline auto isMultiStep() const -> bool override { return totalSize() > 1; };
+ auto getStepProgress() const -> TaskStepProgressList override;
void addTask(Task::Ptr task);
@@ -41,7 +41,7 @@ slots:
void subTaskFailed(Task::Ptr, const QString &msg);
void subTaskStatus(Task::Ptr task, const QString &msg);
void subTaskProgress(Task::Ptr task, qint64 current, qint64 total);
- void subTaskStepProgress(Task::Ptr task, QList task_step_progress);
+ void subTaskStepProgress(Task::Ptr task, TaskStepProgressList task_step_progress);
protected:
// NOTE: This is not thread-safe.
diff --git a/launcher/tasks/Task.cpp b/launcher/tasks/Task.cpp
index 452dc2e3..5aada876 100644
--- a/launcher/tasks/Task.cpp
+++ b/launcher/tasks/Task.cpp
@@ -148,6 +148,11 @@ void Task::emitSucceeded()
emit finished();
}
+void Task::propogateStepProgress(TaskStepProgressList task_progress)
+{
+ emit stepProgress(task_progress);
+}
+
QString Task::describe()
{
QString outStr;
diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h
index a6ab15b8..863f8a4c 100644
--- a/launcher/tasks/Task.h
+++ b/launcher/tasks/Task.h
@@ -42,7 +42,7 @@
#include "QObjectPtr.h"
-enum class TaskState {
+enum class TaskStepState {
Waiting,
Running,
Failed,
@@ -50,16 +50,22 @@ enum class TaskState {
Finished
};
+Q_DECLARE_METATYPE(TaskStepState)
+
struct TaskStepProgress {
QUuid uid;
- qint64 current;
- qint64 total;
- QString status;
- QString details;
- TaskState state = TaskState::Waiting;
- bool isDone() { return (state == TaskState::Failed) || (state == TaskState::Succeeded) || (state == TaskState::Finished); }
+ qint64 current = 0;
+ qint64 total = -1;
+ QString status = "";
+ QString details = "";
+ TaskStepState state = TaskStepState::Waiting;
+ bool isDone() { return (state == TaskStepState::Failed) || (state == TaskStepState::Succeeded) || (state == TaskStepState::Finished); }
};
+Q_DECLARE_METATYPE(TaskStepProgress)
+
+typedef QList> TaskStepProgressList;
+
class Task : public QObject, public QRunnable {
Q_OBJECT
public:
@@ -97,7 +103,7 @@ class Task : public QObject, public QRunnable {
qint64 getProgress() { return m_progress; }
qint64 getTotalProgress() { return m_progressTotal; }
- virtual auto getStepProgress() const -> QList { return {}; }
+ virtual auto getStepProgress() const -> TaskStepProgressList { return {}; }
virtual auto getDetails() const -> QString { return ""; }
@@ -117,7 +123,7 @@ class Task : public QObject, public QRunnable {
void aborted();
void failed(QString reason);
void status(QString status);
- void stepProgress(QList task_progress); //
+ void stepProgress(TaskStepProgressList task_progress); //
/** Emitted when the canAbort() status has changed.
*/
@@ -140,6 +146,8 @@ class Task : public QObject, public QRunnable {
virtual void emitAborted();
virtual void emitFailed(QString reason = "");
+ virtual void propogateStepProgress(TaskStepProgressList task_progress);
+
public slots:
void setStatus(const QString& status);
void setProgress(qint64 current, qint64 total);
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index da627af3..f7a3a862 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -48,10 +48,12 @@
template
int map_int_range(T value)
{
- auto type_min = std::numeric_limits::min();
+ // auto type_min = std::numeric_limits::min();
+ auto type_min = 0;
auto type_max = std::numeric_limits::max();
- auto int_min = std::numeric_limits::min();
+ // auto int_min = std::numeric_limits::min();
+ auto int_min = 0;
auto int_max = std::numeric_limits::max();
auto type_range = type_max - type_min;
@@ -64,6 +66,7 @@ int map_int_range(T value)
ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog)
{
ui->setupUi(this);
+ ui->taskProgressScrollArea->setHidden(true);
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
setSkipButton(false);
@@ -94,10 +97,17 @@ ProgressDialog::~ProgressDialog()
}
void ProgressDialog::updateSize()
-{
+{
+ QSize lastSize = this->size();
QSize qSize = QSize(480, minimumSizeHint().height());
resize(qSize);
setFixedSize(qSize);
+ // keep the dialog in the center after a resize
+ if (lastSize != qSize)
+ this->move(
+ this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
+ this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
+ );
}
int ProgressDialog::execWithTask(Task* task)
@@ -126,10 +136,8 @@ int ProgressDialog::execWithTask(Task* task)
connect(task, &Task::abortStatusChanged, ui->skipButton, &QPushButton::setEnabled);
m_is_multi_step = task->isMultiStep();
- if (!m_is_multi_step) {
- ui->globalStatusLabel->setHidden(true);
- ui->globalProgressBar->setHidden(true);
- }
+ ui->taskProgressScrollArea->setHidden(!m_is_multi_step);
+ updateSize();
// It's a good idea to start the task after we entered the dialog's event loop :^)
if (!task->isRunning()) {
@@ -139,6 +147,9 @@ int ProgressDialog::execWithTask(Task* task)
changeProgress(task->getProgress(), task->getTotalProgress());
}
+ // auto size_hint = ui->verticalLayout->sizeHint();
+ // resize(size_hint.width(), size_hint.height());
+
return QDialog::exec();
}
@@ -189,40 +200,45 @@ void ProgressDialog::onTaskSucceeded()
void ProgressDialog::changeStatus(const QString& status)
{
ui->globalStatusLabel->setText(task->getStatus());
- // ui->statusLabel->setText(task->getStepStatus());
+ ui->globalStatusDetailsLabel->setText(task->getDetails());
updateSize();
}
-void ProgressDialog::addTaskProgress(TaskStepProgress progress)
+void ProgressDialog::addTaskProgress(TaskStepProgress* progress)
{
SubTaskProgressBar* task_bar = new SubTaskProgressBar(this);
- taskProgress.insert(progress.uid, task_bar);
- ui->taskProgressLayout->addWidget(task_bar);
+ taskProgress.insert(progress->uid, task_bar);
+ ui->taskProgressLayout->insertWidget(0, task_bar);
}
-void ProgressDialog::changeStepProgress(QList task_progress)
+void ProgressDialog::changeStepProgress(TaskStepProgressList task_progress)
{
+ m_is_multi_step = true;
+ ui->taskProgressScrollArea->setHidden(false);
+
for (auto tp : task_progress) {
- if (!taskProgress.contains(tp.uid))
- addTaskProgress(tp);
- auto task_bar = taskProgress.value(tp.uid);
+ if (!taskProgress.contains(tp->uid))
+ addTaskProgress(tp.get());
+ auto task_bar = taskProgress.value(tp->uid);
- if (tp.total < 0) {
+ if (tp->total < 0) {
task_bar->setRange(0, 0);
} else {
- task_bar->setRange(0, map_int_range(tp.total));
+ task_bar->setRange(0, map_int_range(tp->total));
}
- task_bar->setValue(map_int_range(tp.current));
- task_bar->setStatus(tp.status);
- task_bar->setDetails(tp.details);
+ task_bar->setValue(map_int_range(tp->current));
+ task_bar->setStatus(tp->status);
+ task_bar->setDetails(tp->details);
- if (tp.isDone()) {
+ if (tp->isDone()) {
task_bar->setVisible(false);
}
}
+
+ updateSize();
}
void ProgressDialog::changeProgress(qint64 current, qint64 total)
@@ -230,13 +246,6 @@ void ProgressDialog::changeProgress(qint64 current, qint64 total)
ui->globalProgressBar->setMaximum(total);
ui->globalProgressBar->setValue(current);
- // if (!m_is_multi_step) {
- // ui->taskProgressBar->setMaximum(total);
- // ui->taskProgressBar->setValue(current);
- // } else {
- // ui->taskProgressBar->setMaximum(task->getStepProgress());
- // ui->taskProgressBar->setValue(task->getStepTotalProgress());
- // }
}
void ProgressDialog::keyPressEvent(QKeyEvent* e)
diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h
index a7e203fb..95a4db16 100644
--- a/launcher/ui/dialogs/ProgressDialog.h
+++ b/launcher/ui/dialogs/ProgressDialog.h
@@ -80,7 +80,7 @@ slots:
void changeStatus(const QString &status);
void changeProgress(qint64 current, qint64 total);
- void changeStepProgress(QList task_progress);
+ void changeStepProgress(TaskStepProgressList task_progress);
private
@@ -93,7 +93,7 @@ protected:
private:
bool handleImmediateResult(QDialog::DialogCode &result);
- void addTaskProgress(TaskStepProgress progress);
+ void addTaskProgress(TaskStepProgress* progress);
private:
Ui::ProgressDialog *ui;
diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui
index 0a998987..47597689 100644
--- a/launcher/ui/dialogs/ProgressDialog.ui
+++ b/launcher/ui/dialogs/ProgressDialog.ui
@@ -6,20 +6,20 @@
0
0
- 400
- 109
+ 600
+ 260
-
- 0
- 0
+
+ 1
+ 1
- 400
- 0
+ 600
+ 260
@@ -31,43 +31,109 @@
Please wait...
-
- -
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 15
+
+
+
+ Global Task Status...
+
+
+
+ -
+
+
+ Global Status Details...
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
true
+
+
+ 0
+ 24
+
+
24
- -
-
+
-
+
-
+
0
0
-
- Skip
+
+
+ 0
+ 100
+
+
+
+ QFrame::StyledPanel
+
+ Qt::ScrollBarAsNeeded
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 584
+ 146
+
+
+
+
+ 2
+
+
+
- -
-
-
- -
-
+
-
+
-
+
0
0
- Global Task Status...
+ Skip
diff --git a/launcher/ui/widgets/SubTaskProgressBar.ui b/launcher/ui/widgets/SubTaskProgressBar.ui
index 966fdb88..ceae5e26 100644
--- a/launcher/ui/widgets/SubTaskProgressBar.ui
+++ b/launcher/ui/widgets/SubTaskProgressBar.ui
@@ -6,12 +6,12 @@
0
0
- 265
- 65
+ 597
+ 61
-
+
0
0
@@ -20,29 +20,45 @@
Form
+
+ 0
+
-
-
-
+
0
0
+
+
+ 8
+
+
Sub Task Status...
+
+ true
+
-
-
+
0
0
+
+
+ 8
+
+
Status Details
@@ -55,6 +71,11 @@
-
+
+
+ 8
+
+
24
diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp
index 678382ba..dabe5da2 100644
--- a/tests/Task_test.cpp
+++ b/tests/Task_test.cpp
@@ -69,8 +69,9 @@ class BigConcurrentTaskThread : public QThread {
auto sub_tasks = new BasicTask::Ptr[s_num_tasks];
for (unsigned i = 0; i < s_num_tasks; i++) {
- sub_tasks[i] = makeShared(false);
- big_task.addTask(sub_tasks[i]);
+ auto sub_task = makeShared(false);
+ sub_tasks[i] = sub_task;
+ big_task.addTask(sub_task);
}
big_task.run();
@@ -99,7 +100,7 @@ class TaskTest : public QObject {
t.setStatus(status);
QCOMPARE(t.getStatus(), status);
- QCOMPARE(t.getStepProgress().isEmpty(), QList{}.isEmpty());
+ QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
}
void test_SetStatus_MultiStep(){
@@ -111,7 +112,7 @@ class TaskTest : public QObject {
QCOMPARE(t.getStatus(), status);
// Even though it is multi step, it does not override the getStepStatus method,
// so it should remain the same.
- QCOMPARE(t.getStepProgress().isEmpty(), QList{}.isEmpty());
+ QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
}
void test_SetProgress(){
--
cgit
From f1028fa66d556b024765ba4e21ac76d4510a3e3e Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Fri, 31 Mar 2023 12:29:59 -0700
Subject: fix: properly map progress range - doument PCRE used for URL
compacting
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/net/Download.cpp | 20 +++++++++---
launcher/tasks/ConcurrentTask.cpp | 7 +----
launcher/ui/dialogs/ProgressDialog.cpp | 56 ++++++++++++++++++----------------
launcher/ui/dialogs/ProgressDialog.ui | 21 ++++++-------
4 files changed, 55 insertions(+), 49 deletions(-)
(limited to 'launcher/ui')
diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp
index a4c3ebfc..3eef9117 100644
--- a/launcher/net/Download.cpp
+++ b/launcher/net/Download.cpp
@@ -61,13 +61,23 @@ QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false
if (str_url.length() <= max_len)
return str_url;
- QRegularExpression re(R"(^([\w]+:\/\/)([\w._-]+\/)([\w._-]+\/).*(\/[^]+[^]+)$)");
+ /* this is a PCRE regular expression that splits a URL (given by the display rules above) into 5 capture groups
+ * the scheme (ie https://) is group 1
+ * the host (with trailing /) is group 2
+ * the first part of the path (with trailing /) is group 3
+ * the last part of the path (with leading /) is group 5
+ * the remainder of the URL is in the .* and in group 4
+ *
+ * See: https://regex101.com/r/inHkek/1
+ * for an interactive breakdown
+ */
+ QRegularExpression re(R"(^([\w]+:\/\/)([\w._-]+\/)([\w._-]+\/)(.*)(\/[^]+[^]+)$)");
auto url_compact = QString(str_url);
- url_compact.replace(re, "\\1\\2\\3...\\4");
+ url_compact.replace(re, "\\1\\2\\3...\\5");
if (url_compact.length() >= max_len) {
- auto url_compact = QString(str_url);
- url_compact.replace(re, "\\1\\2...\\4");
+ url_compact = QString(str_url);
+ url_compact.replace(re, "\\1\\2...\\5");
}
@@ -120,7 +130,7 @@ void Download::addValidator(Validator* v)
void Download::executeTask()
{
- setStatus(tr("Downloading %1").arg(truncateUrlHumanFriendly(m_url, 60)));
+ setStatus(tr("Downloading %1").arg(truncateUrlHumanFriendly(m_url, 100)));
if (getState() == Task::State::AbortedByUser) {
qCWarning(DownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp
index fde7d0ad..41c405db 100644
--- a/launcher/tasks/ConcurrentTask.cpp
+++ b/launcher/tasks/ConcurrentTask.cpp
@@ -28,12 +28,7 @@ void ConcurrentTask::addTask(Task::Ptr task)
void ConcurrentTask::executeTask()
{
- // Start the least amount of tasks needed, but at least one
- // int num_starts = qMax(1, qMin(m_total_max_size, m_queue.size()));
- // for (int i = 0; i < num_starts; i++) {
- // QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
- // }
- // Start One task, startNext hadles starting the up to the m_total_max_size
+ // Start One task, startNext hadels starting the up to the m_total_max_size
// while tracking the number currently being done
QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection);
}
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index f7a3a862..7ab766e4 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -45,21 +45,18 @@
#include "ui/widgets/SubTaskProgressBar.h"
-template
-int map_int_range(T value)
+// map a value in a numaric range of an arbatray type to between 0 and INT_MAX
+// for getting the best percision out of the qt progress bar
+template::value, T>::type>
+std::tuple map_int_zero_max(T current, T range_max, T range_min)
{
- // auto type_min = std::numeric_limits::min();
- auto type_min = 0;
- auto type_max = std::numeric_limits::max();
+ int int_max = std::numeric_limits::max();
- // auto int_min = std::numeric_limits::min();
- auto int_min = 0;
- auto int_max = std::numeric_limits::max();
+ auto type_range = range_max - range_min;
+ double percentage = static_cast(current - range_min) / static_cast(type_range);
+ int mapped_current = percentage * int_max;
- auto type_range = type_max - type_min;
- auto int_range = int_max - int_min;
-
- return static_cast((value - type_min) * int_range / type_range + int_min);
+ return {mapped_current, int_max};
}
@@ -100,14 +97,21 @@ void ProgressDialog::updateSize()
{
QSize lastSize = this->size();
QSize qSize = QSize(480, minimumSizeHint().height());
- resize(qSize);
- setFixedSize(qSize);
- // keep the dialog in the center after a resize
- if (lastSize != qSize)
+
+ // if the current window is too small
+ if ((lastSize != qSize) && (lastSize.height() < qSize.height()))
+ {
+ resize(qSize);
+
+ // keep the dialog in the center after a resize
this->move(
this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
);
+ }
+
+ setMinimumSize(qSize);
+
}
int ProgressDialog::execWithTask(Task* task)
@@ -147,9 +151,6 @@ int ProgressDialog::execWithTask(Task* task)
changeProgress(task->getProgress(), task->getTotalProgress());
}
- // auto size_hint = ui->verticalLayout->sizeHint();
- // resize(size_hint.width(), size_hint.height());
-
return QDialog::exec();
}
@@ -209,26 +210,31 @@ void ProgressDialog::addTaskProgress(TaskStepProgress* progress)
{
SubTaskProgressBar* task_bar = new SubTaskProgressBar(this);
taskProgress.insert(progress->uid, task_bar);
- ui->taskProgressLayout->insertWidget(0, task_bar);
+ ui->taskProgressLayout->addWidget(task_bar);
}
void ProgressDialog::changeStepProgress(TaskStepProgressList task_progress)
{
m_is_multi_step = true;
- ui->taskProgressScrollArea->setHidden(false);
+ if(ui->taskProgressScrollArea->isHidden()) {
+ ui->taskProgressScrollArea->setHidden(false);
+ updateSize();
+ }
for (auto tp : task_progress) {
if (!taskProgress.contains(tp->uid))
addTaskProgress(tp.get());
auto task_bar = taskProgress.value(tp->uid);
- if (tp->total < 0) {
+
+ auto const [mapped_current, mapped_total] = map_int_zero_max(tp->current, tp->total, 0);
+ if (tp->total <= 0) {
task_bar->setRange(0, 0);
} else {
- task_bar->setRange(0, map_int_range(tp->total));
+ task_bar->setRange(0, mapped_total);
}
- task_bar->setValue(map_int_range(tp->current));
+ task_bar->setValue(mapped_current);
task_bar->setStatus(tp->status);
task_bar->setDetails(tp->details);
@@ -237,8 +243,6 @@ void ProgressDialog::changeStepProgress(TaskStepProgressList task_progress)
}
}
-
- updateSize();
}
void ProgressDialog::changeProgress(qint64 current, qint64 total)
diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui
index 47597689..a4d08124 100644
--- a/launcher/ui/dialogs/ProgressDialog.ui
+++ b/launcher/ui/dialogs/ProgressDialog.ui
@@ -6,8 +6,8 @@
0
0
- 600
- 260
+ 480
+ 210
@@ -18,19 +18,16 @@
- 600
- 260
-
-
-
-
- 600
- 16777215
+ 480
+ 210
Please wait...
+
+ true
+
-
@@ -112,8 +109,8 @@
0
0
- 584
- 146
+ 464
+ 96
--
cgit
From b6452215c16f6b1ee45fea746f9498767e48d049 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Fri, 31 Mar 2023 19:25:01 -0700
Subject: feat: add `details` signal to `Task` feat: add details to mod pack
downloading feat: add logging rule sloading form `ligging.ini at data path
root feat: add `launcher.task` `launcher.task.net` and
`launcher.task.net.[down|up]load` logging categories fix: add new subtask
progress to the end of the lay out not the beginning (cuts down on
flickering)
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/Application.cpp | 19 +++++++++
launcher/CMakeLists.txt | 33 +++++++++++++++
launcher/InstanceImportTask.cpp | 2 +
launcher/InstanceList.cpp | 1 +
launcher/java/JavaInstallList.cpp | 1 +
launcher/launch/steps/Update.cpp | 1 +
launcher/minecraft/MinecraftUpdate.cpp | 2 +
.../modplatform/atlauncher/ATLPackInstallTask.cpp | 3 +-
.../flame/FlameInstanceCreationTask.cpp | 7 +++-
.../modrinth/ModrinthInstanceCreationTask.cpp | 7 +++-
launcher/net/Download.cpp | 47 +++++++++++-----------
launcher/net/Download.h | 3 --
launcher/net/FileSink.cpp | 10 +++--
launcher/net/HttpMetaCache.cpp | 16 ++++----
launcher/net/MetaCacheSink.cpp | 10 +++--
launcher/net/PasteUpload.cpp | 22 +++++-----
launcher/net/Upload.cpp | 38 ++++++++---------
launcher/net/logging.cpp | 24 +++++++++++
launcher/net/logging.h | 26 ++++++++++++
launcher/tasks/ConcurrentTask.cpp | 11 ++++-
launcher/tasks/ConcurrentTask.h | 1 +
launcher/tasks/Task.cpp | 42 ++++++++++++-------
launcher/tasks/Task.h | 8 +++-
launcher/ui/dialogs/ProgressDialog.cpp | 2 +-
launcher/ui/widgets/ProgressWidget.cpp | 1 +
launcher/ui/widgets/SubTaskProgressBar.ui | 7 +++-
26 files changed, 249 insertions(+), 95 deletions(-)
create mode 100644 launcher/net/logging.cpp
create mode 100644 launcher/net/logging.h
(limited to 'launcher/ui')
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index a7c97aa7..c8855cbc 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -46,6 +46,7 @@
#include "net/PasteUpload.h"
#include "pathmatcher/MultiMatcher.h"
#include "pathmatcher/SimplePrefixMatcher.h"
+#include "settings/INIFile.h"
#include "ui/MainWindow.h"
#include "ui/InstanceWindow.h"
@@ -410,6 +411,24 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
" " "|" " "
"%{if-category}[%{category}]: %{endif}"
"%{message}");
+
+ if(QFile::exists("logging.ini")) {
+ // load and set logging rules
+ qDebug() << "Loading logging rules from:" << QString("%1/logging.ini").arg(dataPath);
+ INIFile loggingRules;
+ bool rulesLoaded = loggingRules.loadFile(QString("logging.ini"));
+ if (rulesLoaded) {
+ QStringList rules;
+ qDebug() << "Setting log rules:";
+ for (auto it = loggingRules.begin(); it != loggingRules.end(); ++it) {
+ auto rule = it.key() + "=" + it.value().toString();
+ rules.append(rule);
+ qDebug() << " " << rule;
+ }
+ auto rules_str = rules.join("\n");
+ QLoggingCategory::setFilterRules(rules_str);
+ }
+ }
qDebug() << "<> Log initialized.";
}
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 24330adf..e49a4562 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -123,6 +123,8 @@ set(NET_SOURCES
net/HttpMetaCache.h
net/MetaCacheSink.cpp
net/MetaCacheSink.h
+ net/logging.h
+ net/logging.cpp
net/NetAction.h
net/NetJob.cpp
net/NetJob.h
@@ -563,6 +565,37 @@ ecm_qt_declare_logging_category(CORE_SOURCES
EXPORT "${Launcher_Name}"
)
+ecm_qt_export_logging_category(
+ IDENTIFIER taskLogC
+ CATEGORY_NAME "launcher.task"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "Task actions"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER taskNetLogC
+ CATEGORY_NAME "launcher.task.net"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network action"
+ EXPORT "${Launcher_Name}"
+)
+
+ecm_qt_export_logging_category(
+ IDENTIFIER taskDownloadLogC
+ CATEGORY_NAME "launcher.task.net.download"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network download actions"
+ EXPORT "${Launcher_Name}"
+)
+ecm_qt_export_logging_category(
+ IDENTIFIER taskUploadLogC
+ CATEGORY_NAME "launcher.task.net.upload"
+ DEFAULT_SEVERITY Debug
+ DESCRIPTION "task network upload actions"
+ EXPORT "${Launcher_Name}"
+)
+
if(KDE_INSTALL_LOGGINGCATEGORIESDIR) # only install if there is a standard path for this
ecm_qt_install_logging_categories(
EXPORT "${Launcher_Name}"
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index c196396d..8a48873e 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -294,6 +294,7 @@ void InstanceImportTask::processFlame()
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
+ connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
@@ -386,6 +387,7 @@ void InstanceImportTask::processModrinth()
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
+ connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp
index dbc891ff..5f98a184 100644
--- a/launcher/InstanceList.cpp
+++ b/launcher/InstanceList.cpp
@@ -787,6 +787,7 @@ class InstanceStaging : public Task {
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
connect(child, &Task::status, this, &InstanceStaging::setStatus);
+ connect(child, &Task::details, this, &InstanceStaging::setDetails);
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress);
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp
index b29af857..5f133622 100644
--- a/launcher/java/JavaInstallList.cpp
+++ b/launcher/java/JavaInstallList.cpp
@@ -170,6 +170,7 @@ void JavaListLoadTask::executeTask()
m_job.reset(new JavaCheckerJob("Java detection"));
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress);
+ // stepProgress?
qDebug() << "Probing the following Java paths: ";
int id = 0;
diff --git a/launcher/launch/steps/Update.cpp b/launcher/launch/steps/Update.cpp
index 1640d115..c8e576a5 100644
--- a/launcher/launch/steps/Update.cpp
+++ b/launcher/launch/steps/Update.cpp
@@ -30,6 +30,7 @@ void Update::executeTask()
connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress);
connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propogateStepProgress);
connect(m_updateTask.get(), &Task::status, this, &Update::setStatus);
+ connect(m_updateTask.get(), &Task::details, this, &Update::setDetails);
emit progressReportingRequest();
return;
}
diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp
index 3ce808f8..35430bb0 100644
--- a/launcher/minecraft/MinecraftUpdate.cpp
+++ b/launcher/minecraft/MinecraftUpdate.cpp
@@ -102,6 +102,7 @@ void MinecraftUpdate::next()
disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
+ disconnect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
}
if(m_currentTask == m_tasks.size())
{
@@ -121,6 +122,7 @@ void MinecraftUpdate::next()
connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress);
connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress);
connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus);
+ connect(task.get(), &Task::details, this, &MinecraftUpdate::setDetails);
// if the task is already running, do not start it again
if(!task->isRunning())
{
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
index 28026732..d130914f 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp
@@ -846,7 +846,8 @@ void PackInstallTask::downloadMods()
emitFailed(reason);
});
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
- {
+ {
+ setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
abortable = true;
setProgress(current, total);
});
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index 3cb6b61a..86fd2ab4 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -453,7 +453,7 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
{
- m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
+ m_files_job.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network()));
for (const auto& result : m_mod_id_resolver->getResults().files) {
QString filename = result.fileName;
if (!result.required) {
@@ -497,7 +497,10 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
m_files_job.reset();
setError(reason);
});
- connect(m_files_job.get(), &NetJob::progress, this, &FlameCreationTask::setProgress);
+ connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total){
+ setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
+ setProgress(current, total);
+ });
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress);
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
index 2fb656ea..bb8227aa 100644
--- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp
@@ -224,7 +224,7 @@ bool ModrinthCreationTask::createInstance()
instance.setName(name());
instance.saveNow();
- m_files_job.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
+ m_files_job.reset(new NetJob(tr("Mod Download Modrinth"), APPLICATION->network()));
auto root_modpack_path = FS::PathCombine(m_stagingPath, ".minecraft");
auto root_modpack_url = QUrl::fromLocalFile(root_modpack_path);
@@ -263,7 +263,10 @@ bool ModrinthCreationTask::createInstance()
setError(reason);
});
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
- connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); });
+ connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
+ setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
+ setProgress(current, total);
+ });
connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress);
setStatus(tr("Downloading mods..."));
diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp
index 3eef9117..86e4bd97 100644
--- a/launcher/net/Download.cpp
+++ b/launcher/net/Download.cpp
@@ -4,6 +4,7 @@
* Copyright (c) 2022 flowln
* Copyright (C) 2022 Sefa Eyeoglu
* Copyright (C) 2023 TheKodeToad
+ * 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
@@ -50,7 +51,7 @@
#include "BuildConfig.h"
#include "Application.h"
-Q_LOGGING_CATEGORY(DownloadLogC, "Task.Net.Download")
+#include "logging.h"
namespace Net {
@@ -133,7 +134,7 @@ void Download::executeTask()
setStatus(tr("Downloading %1").arg(truncateUrlHumanFriendly(m_url, 100)));
if (getState() == Task::State::AbortedByUser) {
- qCWarning(DownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
+ qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString();
emitAborted();
return;
}
@@ -143,10 +144,10 @@ void Download::executeTask()
switch (m_state) {
case State::Succeeded:
emit succeeded();
- qCDebug(DownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download cache hit " << m_url.toString();
return;
case State::Running:
- qCDebug(DownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Downloading " << m_url.toString();
break;
case State::Inactive:
case State::Failed:
@@ -192,9 +193,9 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
auto elapsed_ms = std::chrono::duration_cast(elapsed).count();
auto bytes_recived_since = bytesReceived - m_last_progress_bytes;
if (elapsed_ms > 0) {
- m_details = humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s";
+ setDetails(humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s");
} else {
- m_details = "0 b/s";
+ setDetails("0 b/s");
}
setProgress(bytesReceived, bytesTotal);
@@ -203,7 +204,7 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
void Download::downloadError(QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError) {
- qCCritical(DownloadLogC) << getUid().toString() << "Aborted " << m_url.toString();
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Aborted " << m_url.toString();
m_state = State::AbortedByUser;
} else {
if (m_options & Option::AcceptLocalFiles) {
@@ -213,7 +214,7 @@ void Download::downloadError(QNetworkReply::NetworkError error)
}
}
// error happened during download.
- qCCritical(DownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Failed " << m_url.toString() << " with reason " << error;
m_state = State::Failed;
}
}
@@ -222,9 +223,9 @@ void Download::sslErrors(const QList& errors)
{
int i = 1;
for (auto error : errors) {
- qCCritical(DownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
- qCCritical(DownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText();
i++;
}
}
@@ -267,17 +268,17 @@ auto Download::handleRedirect() -> bool
*/
redirect = QUrl(redirectStr, QUrl::TolerantMode);
if (!redirect.isValid()) {
- qCWarning(DownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
+ qCWarning(taskDownloadLogC) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr;
downloadError(QNetworkReply::ProtocolFailure);
return false;
}
- qCDebug(DownloadLogC) << getUid().toString() << "Fixed location header:" << redirect;
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Fixed location header:" << redirect;
} else {
- qCDebug(DownloadLogC) << getUid().toString() << "Location header:" << redirect;
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Location header:" << redirect;
}
m_url = QUrl(redirect.toString());
- qCDebug(DownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Following redirect to " << m_url.toString();
startAction(m_network);
return true;
@@ -287,26 +288,26 @@ void Download::downloadFinished()
{
// handle HTTP redirection first
if (handleRedirect()) {
- qCDebug(DownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download redirected:" << m_url.toString();
return;
}
// if the download failed before this point ...
if (m_state == State::Succeeded) // pretend to succeed so we continue processing :)
{
- qCDebug(DownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed but we are allowed to proceed:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit succeeded();
return;
} else if (m_state == State::Failed) {
- qCDebug(DownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
return;
} else if (m_state == State::AbortedByUser) {
- qCDebug(DownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download aborted in previous step:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit aborted();
@@ -316,14 +317,14 @@ void Download::downloadFinished()
// make sure we got all the remaining data, if any
auto data = m_reply->readAll();
if (data.size()) {
- qCDebug(DownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Writing extra" << data.size() << "bytes";
m_state = m_sink->write(data);
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply.get());
if (m_state != State::Succeeded) {
- qCDebug(DownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download failed to finalize:" << m_url.toString();
m_sink->abort();
m_reply.reset();
emit failed("");
@@ -331,7 +332,7 @@ void Download::downloadFinished()
}
m_reply.reset();
- qCDebug(DownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();
+ qCDebug(taskDownloadLogC) << getUid().toString() << "Download succeeded:" << m_url.toString();
emit succeeded();
}
@@ -341,11 +342,11 @@ void Download::downloadReadyRead()
auto data = m_reply->readAll();
m_state = m_sink->write(data);
if (m_state == State::Failed) {
- qCCritical(DownloadLogC) << getUid().toString() << "Failed to process response chunk";
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Failed to process response chunk";
}
// qDebug() << "Download" << m_url.toString() << "gained" << data.size() << "bytes";
} else {
- qCCritical(DownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status;
+ qCCritical(taskDownloadLogC) << getUid().toString() << "Cannot write download data! illegal status " << m_status;
}
}
diff --git a/launcher/net/Download.h b/launcher/net/Download.h
index cbee0d03..ae2b034c 100644
--- a/launcher/net/Download.h
+++ b/launcher/net/Download.h
@@ -66,7 +66,6 @@ class Download : public NetAction {
void addValidator(Validator* v);
auto abort() -> bool override;
auto canAbort() const -> bool override { return true; };
- auto getDetails() const -> QString override {return m_details; };
private:
auto handleRedirect() -> bool;
@@ -88,8 +87,6 @@ class Download : public NetAction {
std::chrono::steady_clock m_clock;
std::chrono::time_point m_last_progress_time;
qint64 m_last_progress_bytes;
-
- QString m_details;
};
} // namespace Net
diff --git a/launcher/net/FileSink.cpp b/launcher/net/FileSink.cpp
index ba0caf6c..3c2948d4 100644
--- a/launcher/net/FileSink.cpp
+++ b/launcher/net/FileSink.cpp
@@ -37,6 +37,8 @@
#include "FileSystem.h"
+#include "logging.h"
+
namespace Net {
Task::State FileSink::init(QNetworkRequest& request)
@@ -48,14 +50,14 @@ Task::State FileSink::init(QNetworkRequest& request)
// create a new save file and open it for writing
if (!FS::ensureFilePathExists(m_filename)) {
- qCritical() << "Could not create folder for " + m_filename;
+ qCCritical(taskNetLogC) << "Could not create folder for " + m_filename;
return Task::State::Failed;
}
wroteAnyData = false;
m_output_file.reset(new QSaveFile(m_filename));
if (!m_output_file->open(QIODevice::WriteOnly)) {
- qCritical() << "Could not open " + m_filename + " for writing";
+ qCCritical(taskNetLogC) << "Could not open " + m_filename + " for writing";
return Task::State::Failed;
}
@@ -67,7 +69,7 @@ Task::State FileSink::init(QNetworkRequest& request)
Task::State FileSink::write(QByteArray& data)
{
if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) {
- qCritical() << "Failed writing into " + m_filename;
+ qCCritical(taskNetLogC) << "Failed writing into " + m_filename;
m_output_file->cancelWriting();
m_output_file.reset();
wroteAnyData = false;
@@ -106,7 +108,7 @@ Task::State FileSink::finalize(QNetworkReply& reply)
// nothing went wrong...
if (!m_output_file->commit()) {
- qCritical() << "Failed to commit changes to " << m_filename;
+ qCCritical(taskNetLogC) << "Failed to commit changes to " << m_filename;
m_output_file->cancelWriting();
return Task::State::Failed;
}
diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp
index 0d7ca769..855211f7 100644
--- a/launcher/net/HttpMetaCache.cpp
+++ b/launcher/net/HttpMetaCache.cpp
@@ -44,6 +44,8 @@
#include
+#include "logging.h"
+
auto MetaEntry::getFullPath() -> QString
{
// FIXME: make local?
@@ -124,7 +126,7 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
// Get rid of old entries, to prevent cache problems
auto current_time = QDateTime::currentSecsSinceEpoch();
if (entry->isExpired(current_time - ( file_last_changed / 1000 ))) {
- qWarning() << "Removing cache entry because of old age!";
+ qCWarning(taskNetLogC) << "[HttpMetaCache]" << "Removing cache entry because of old age!";
selected_base.entry_list.remove(resource_path);
return staleEntry(base, resource_path);
}
@@ -137,12 +139,12 @@ auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString ex
auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool
{
if (!m_entries.contains(stale_entry->m_baseId)) {
- qCritical() << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit();
+ qCCritical(taskNetLogC) << "[HttpMetaCache]" << "Cannot add entry with unknown base: " << stale_entry->m_baseId.toLocal8Bit();
return false;
}
if (stale_entry->m_stale) {
- qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
+ qCCritical(taskNetLogC) << "[HttpMetaCache]" << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
return false;
}
@@ -166,10 +168,10 @@ void HttpMetaCache::evictAll()
{
for (QString& base : m_entries.keys()) {
EntryMap& map = m_entries[base];
- qDebug() << "Evicting base" << base;
+ qCDebug(taskNetLogC) << "[HttpMetaCache]" << "Evicting base" << base;
for (MetaEntryPtr entry : map.entry_list) {
if (!evictEntry(entry))
- qWarning() << "Unexpected missing cache entry" << entry->m_basePath;
+ qCWarning(taskNetLogC) << "[HttpMetaCache]" << "Unexpected missing cache entry" << entry->m_basePath;
}
}
}
@@ -267,7 +269,7 @@ void HttpMetaCache::SaveNow()
if (m_index_file.isNull())
return;
- qDebug() << "[HttpMetaCache]" << "Saving metacache with" << m_entries.size() << "entries";
+ qCDebug(taskNetLogC) << "[HttpMetaCache]" << "Saving metacache with" << m_entries.size() << "entries";
QJsonObject toplevel;
Json::writeString(toplevel, "version", "1");
@@ -302,6 +304,6 @@ void HttpMetaCache::SaveNow()
try {
Json::write(toplevel, m_index_file);
} catch (const Exception& e) {
- qWarning() << e.what();
+ qCWarning(taskNetLogC) << "[HttpMetaCache]" << e.what();
}
}
diff --git a/launcher/net/MetaCacheSink.cpp b/launcher/net/MetaCacheSink.cpp
index c730fdbf..46bfe37d 100644
--- a/launcher/net/MetaCacheSink.cpp
+++ b/launcher/net/MetaCacheSink.cpp
@@ -39,6 +39,8 @@
#include
#include "Application.h"
+#include "logging.h"
+
namespace Net {
/** Maximum time to hold a cache entry
@@ -97,11 +99,11 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)
{ // Cache lifetime
if (m_is_eternal) {
- qDebug() << "[MetaCache] Adding eternal cache entry:" << m_entry->getFullPath();
+ qCDebug(taskNetLogC) << "[MetaCache] Adding eternal cache entry:" << m_entry->getFullPath();
m_entry->makeEternal(true);
} else if (reply.hasRawHeader("Cache-Control")) {
auto cache_control_header = reply.rawHeader("Cache-Control");
- // qDebug() << "[MetaCache] Parsing 'Cache-Control' header with" << cache_control_header;
+ // qCDebug(taskNetLogC) << "[MetaCache] Parsing 'Cache-Control' header with" << cache_control_header;
QRegularExpression max_age_expr("max-age=([0-9]+)");
qint64 max_age = max_age_expr.match(cache_control_header).captured(1).toLongLong();
@@ -109,7 +111,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)
} else if (reply.hasRawHeader("Expires")) {
auto expires_header = reply.rawHeader("Expires");
- // qDebug() << "[MetaCache] Parsing 'Expires' header with" << expires_header;
+ // qCDebug(taskNetLogC) << "[MetaCache] Parsing 'Expires' header with" << expires_header;
qint64 max_age = QDateTime::fromString(expires_header).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();
m_entry->setMaximumAge(max_age);
@@ -119,7 +121,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply & reply)
if (reply.hasRawHeader("Age")) {
auto age_header = reply.rawHeader("Age");
- // qDebug() << "[MetaCache] Parsing 'Age' header with" << age_header;
+ // qCDebug(taskNetLogC) << "[MetaCache] Parsing 'Age' header with" << age_header;
qint64 current_age = age_header.toLongLong();
m_entry->setCurrentAge(current_age);
diff --git a/launcher/net/PasteUpload.cpp b/launcher/net/PasteUpload.cpp
index d9e785c5..d5df3799 100644
--- a/launcher/net/PasteUpload.cpp
+++ b/launcher/net/PasteUpload.cpp
@@ -47,6 +47,8 @@
#include
#include
+#include "logging.h"
+
std::array