From 791221e923586bb717396ecf18bd13e57034df99 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 27 Jan 2015 22:31:07 +0100 Subject: NOISSUE Refactors and moving of things --- CMakeLists.txt | 38 +- MultiMC.cpp | 21 +- MultiMC.h | 7 - depends/util/include/modutils.h | 68 +++- depends/util/src/modutils.cpp | 23 +- gui/ConsoleWindow.cpp | 18 +- gui/ConsoleWindow.h | 6 +- gui/MainWindow.cpp | 273 +++++++------- gui/MainWindow.h | 8 +- gui/dialogs/AboutDialog.cpp | 2 +- gui/pages/LogPage.cpp | 4 +- gui/pages/LogPage.h | 6 +- gui/pages/ModFolderPage.cpp | 4 +- gui/pages/OtherLogsPage.cpp | 14 +- gui/pages/OtherLogsPage.h | 6 +- gui/pages/ResourcePackPage.h | 2 +- gui/pages/ScreenshotsPage.cpp | 4 +- gui/pages/ScreenshotsPage.h | 2 +- gui/pages/TexturePackPage.h | 2 +- gui/pages/VersionPage.cpp | 70 +--- gui/pages/VersionPage.h | 4 +- logic/BaseInstance.cpp | 24 +- logic/BaseInstance.h | 40 +-- logic/BaseProcess.cpp | 400 +++++++++++++++++++++ logic/BaseProcess.h | 133 +++++++ logic/InstanceFactory.cpp | 43 +-- logic/InstanceFactory.h | 8 +- logic/InstanceList.cpp | 9 +- logic/InstanceList.h | 3 +- logic/JarUtils.cpp | 3 - logic/LegacyFTBInstance.cpp | 20 -- logic/LegacyFTBInstance.h | 13 - logic/LegacyInstance.cpp | 28 +- logic/LegacyInstance.h | 31 +- logic/LegacyUpdate.cpp | 8 +- logic/LegacyUpdate.h | 2 +- logic/MinecraftProcess.cpp | 578 ------------------------------ logic/MinecraftProcess.h | 165 --------- logic/OneSixFTBInstance.cpp | 137 ------- logic/OneSixFTBInstance.h | 30 -- logic/OneSixInstance.cpp | 240 +++++-------- logic/OneSixInstance.h | 47 ++- logic/OneSixUpdate.cpp | 28 +- logic/OneSixUpdate.h | 2 +- logic/VersionFilterData.cpp | 72 ---- logic/VersionFilterData.h | 28 -- logic/assets/AssetsMigrateTask.cpp | 2 - logic/assets/AssetsUtils.cpp | 62 ++++ logic/assets/AssetsUtils.h | 2 + logic/forge/ForgeInstaller.cpp | 21 +- logic/forge/ForgeInstaller.h | 5 +- logic/forge/ForgeVersion.cpp | 2 +- logic/ftb/FTBVersion.h | 32 ++ logic/ftb/LegacyFTBInstance.cpp | 20 ++ logic/ftb/LegacyFTBInstance.h | 13 + logic/ftb/OneSixFTBInstance.cpp | 139 +++++++ logic/ftb/OneSixFTBInstance.h | 29 ++ logic/liteloader/LiteLoaderInstaller.cpp | 4 +- logic/minecraft/InstanceVersion.cpp | 550 ---------------------------- logic/minecraft/InstanceVersion.h | 184 ---------- logic/minecraft/JarMod.cpp | 34 -- logic/minecraft/JarMod.h | 4 - logic/minecraft/MinecraftInstance.cpp | 58 +++ logic/minecraft/MinecraftInstance.h | 30 ++ logic/minecraft/MinecraftProcess.cpp | 216 +++++++++++ logic/minecraft/MinecraftProcess.h | 77 ++++ logic/minecraft/MinecraftProfile.cpp | 434 ++++++++++++++++++++++ logic/minecraft/MinecraftProfile.h | 201 +++++++++++ logic/minecraft/MinecraftVersion.cpp | 18 +- logic/minecraft/MinecraftVersion.h | 20 +- logic/minecraft/MinecraftVersionList.cpp | 65 +++- logic/minecraft/MinecraftVersionList.h | 40 --- logic/minecraft/NullProfileStrategy.h | 24 ++ logic/minecraft/OneSixLibrary.cpp | 4 - logic/minecraft/OneSixProfileStrategy.cpp | 270 ++++++++++++++ logic/minecraft/OneSixProfileStrategy.h | 24 ++ logic/minecraft/ProfilePatch.h | 32 ++ logic/minecraft/ProfileStrategy.h | 30 ++ logic/minecraft/ProfileUtils.cpp | 145 ++++++++ logic/minecraft/ProfileUtils.h | 25 ++ logic/minecraft/VersionBuilder.cpp | 296 +-------------- logic/minecraft/VersionBuilder.h | 27 +- logic/minecraft/VersionFile.cpp | 7 +- logic/minecraft/VersionFile.h | 8 +- logic/minecraft/VersionFilterData.cpp | 72 ++++ logic/minecraft/VersionFilterData.h | 28 ++ logic/minecraft/VersionPatch.h | 32 -- logic/net/PasteUpload.cpp | 2 - logic/tools/BaseExternalTool.cpp | 25 -- logic/tools/BaseExternalTool.h | 2 - logic/tools/BaseProfiler.cpp | 2 +- logic/tools/BaseProfiler.h | 6 +- logic/tools/JProfiler.cpp | 4 +- logic/tools/JProfiler.h | 2 +- logic/tools/JVisualVM.cpp | 4 +- logic/tools/JVisualVM.h | 2 +- logic/tools/MCEditTool.cpp | 33 ++ logic/tools/MCEditTool.h | 1 + main.cpp | 1 - 99 files changed, 3135 insertions(+), 2909 deletions(-) create mode 100644 logic/BaseProcess.cpp create mode 100644 logic/BaseProcess.h delete mode 100644 logic/LegacyFTBInstance.cpp delete mode 100644 logic/LegacyFTBInstance.h delete mode 100644 logic/MinecraftProcess.cpp delete mode 100644 logic/MinecraftProcess.h delete mode 100644 logic/OneSixFTBInstance.cpp delete mode 100644 logic/OneSixFTBInstance.h delete mode 100644 logic/VersionFilterData.cpp delete mode 100644 logic/VersionFilterData.h create mode 100644 logic/ftb/FTBVersion.h create mode 100644 logic/ftb/LegacyFTBInstance.cpp create mode 100644 logic/ftb/LegacyFTBInstance.h create mode 100644 logic/ftb/OneSixFTBInstance.cpp create mode 100644 logic/ftb/OneSixFTBInstance.h delete mode 100644 logic/minecraft/InstanceVersion.cpp delete mode 100644 logic/minecraft/InstanceVersion.h create mode 100644 logic/minecraft/MinecraftInstance.cpp create mode 100644 logic/minecraft/MinecraftInstance.h create mode 100644 logic/minecraft/MinecraftProcess.cpp create mode 100644 logic/minecraft/MinecraftProcess.h create mode 100644 logic/minecraft/MinecraftProfile.cpp create mode 100644 logic/minecraft/MinecraftProfile.h create mode 100644 logic/minecraft/NullProfileStrategy.h create mode 100644 logic/minecraft/OneSixProfileStrategy.cpp create mode 100644 logic/minecraft/OneSixProfileStrategy.h create mode 100644 logic/minecraft/ProfilePatch.h create mode 100644 logic/minecraft/ProfileStrategy.h create mode 100644 logic/minecraft/ProfileUtils.cpp create mode 100644 logic/minecraft/ProfileUtils.h create mode 100644 logic/minecraft/VersionFilterData.cpp create mode 100644 logic/minecraft/VersionFilterData.h delete mode 100644 logic/minecraft/VersionPatch.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 32bf31b5..e4cd6387 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,6 +394,8 @@ SET(MULTIMC_SOURCES # LOGIC - Base classes and infrastructure logic/BaseVersion.h + logic/BaseProcess.h + logic/BaseProcess.cpp logic/InstanceFactory.h logic/InstanceFactory.cpp logic/BaseInstance.h @@ -404,12 +406,8 @@ SET(MULTIMC_SOURCES logic/ModList.cpp # sets and maps for deciding based on versions - logic/VersionFilterData.h - logic/VersionFilterData.cpp - - # Instance launch - logic/MinecraftProcess.h - logic/MinecraftProcess.cpp + logic/minecraft/VersionFilterData.h + logic/minecraft/VersionFilterData.cpp # Annoying nag screen logic logic/NagUtils.h @@ -503,24 +501,34 @@ SET(MULTIMC_SOURCES logic/JarUtils.h logic/JarUtils.cpp - # OneSix version json infrastructure + # Minecraft support logic/minecraft/GradleSpecifier.h - logic/minecraft/InstanceVersion.cpp - logic/minecraft/InstanceVersion.h + logic/minecraft/MinecraftProfile.cpp + logic/minecraft/MinecraftProfile.h logic/minecraft/JarMod.cpp logic/minecraft/JarMod.h + logic/minecraft/MinecraftInstance.cpp + logic/minecraft/MinecraftInstance.h + logic/minecraft/MinecraftProcess.cpp + logic/minecraft/MinecraftProcess.h logic/minecraft/MinecraftVersion.cpp logic/minecraft/MinecraftVersion.h logic/minecraft/MinecraftVersionList.cpp logic/minecraft/MinecraftVersionList.h + logic/minecraft/NullProfileStrategy.h logic/minecraft/OneSixLibrary.cpp logic/minecraft/OneSixLibrary.h + logic/minecraft/OneSixProfileStrategy.cpp + logic/minecraft/OneSixProfileStrategy.h logic/minecraft/OneSixRule.cpp logic/minecraft/OneSixRule.h logic/minecraft/OpSys.cpp logic/minecraft/OpSys.h logic/minecraft/ParseUtils.cpp logic/minecraft/ParseUtils.h + logic/minecraft/ProfileUtils.cpp + logic/minecraft/ProfileUtils.h + logic/minecraft/ProfileStrategy.h logic/minecraft/RawLibrary.cpp logic/minecraft/RawLibrary.h logic/minecraft/VersionBuilder.cpp @@ -528,7 +536,7 @@ SET(MULTIMC_SOURCES logic/minecraft/VersionBuildError.h logic/minecraft/VersionFile.cpp logic/minecraft/VersionFile.h - logic/minecraft/VersionPatch.h + logic/minecraft/ProfilePatch.h logic/minecraft/VersionSource.h # A Recursive file system watcher @@ -547,10 +555,10 @@ SET(MULTIMC_SOURCES logic/LwjglVersionList.cpp # FTB - logic/OneSixFTBInstance.h - logic/OneSixFTBInstance.cpp - logic/LegacyFTBInstance.h - logic/LegacyFTBInstance.cpp + logic/ftb/OneSixFTBInstance.h + logic/ftb/OneSixFTBInstance.cpp + logic/ftb/LegacyFTBInstance.h + logic/ftb/LegacyFTBInstance.cpp # the screenshots feature logic/screenshots/Screenshot.h @@ -597,8 +605,6 @@ SET(MULTIMC_SOURCES logic/java/JavaCheckerJob.cpp # Assets - logic/assets/AssetsMigrateTask.h - logic/assets/AssetsMigrateTask.cpp logic/assets/AssetsUtils.h logic/assets/AssetsUtils.cpp diff --git a/MultiMC.cpp b/MultiMC.cpp index abb30225..ae6070a3 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -21,8 +21,6 @@ #include "logic/forge/ForgeVersionList.h" -#include "logic/news/NewsChecker.h" - #include "logic/status/StatusChecker.h" #include "logic/net/HttpMetaCache.h" @@ -201,9 +199,6 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar // initialize the notification checker m_notificationChecker.reset(new NotificationChecker()); - // initialize the news checker - m_newsChecker.reset(new NewsChecker(BuildConfig.NEWS_RSS_URL)); - // initialize the status checker m_statusChecker.reset(new StatusChecker()); @@ -213,7 +208,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar auto InstDirSetting = m_settings->getSetting("InstanceDir"); // instance path: check for problems with '!' in instance path and warn the user in the log // and rememer that we have to show him a dialog when the gui starts (if it does so) - QString instDir = MMC->settings()->get("InstanceDir").toString(); + QString instDir = m_settings->get("InstanceDir").toString(); QLOG_INFO() << "Instance path : " << instDir; if (checkProblemticPathJava(QDir(instDir))) { @@ -243,6 +238,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar // init proxy settings updateProxySettings(); + //FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", @@ -251,6 +247,8 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar { profiler->registerSettings(m_settings); } + + //FIXME: what to do with these? m_tools.insert("mcedit", std::shared_ptr(new MCEditFactory())); for (auto tool : m_tools.values()) { @@ -296,8 +294,7 @@ void MultiMC::initTranslations() } m_mmc_translator.reset(new QTranslator()); - if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), - MMC->staticData() + "/translations")) + if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), staticData() + "/translations")) { QLOG_DEBUG() << "Loading MMC Language File for" << locale.bcp47Name().toLocal8Bit().constData() << "..."; @@ -701,13 +698,13 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags) } QLOG_INFO() << "Installing updates."; #ifdef WINDOWS - QString finishCmd = MMC->applicationFilePath(); + QString finishCmd = applicationFilePath(); QString updaterBinary = PathCombine(bin(), "updater.exe"); #elif LINUX QString finishCmd = PathCombine(root(), "MultiMC"); QString updaterBinary = PathCombine(bin(), "updater"); #elif OSX - QString finishCmd = MMC->applicationFilePath(); + QString finishCmd = applicationFilePath(); QString updaterBinary = PathCombine(bin(), "updater"); #else #error Unsupported operating system. @@ -719,7 +716,7 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags) args << "--install-dir" << root(); args << "--package-dir" << updateFilesDir; args << "--script" << PathCombine(updateFilesDir, "file_list.xml"); - args << "--wait" << QString::number(MMC->applicationPid()); + args << "--wait" << QString::number(applicationPid()); if (flags & DryRun) args << "--dry-run"; if (flags & RestartOnFinish) @@ -737,7 +734,7 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags) } // Now that we've started the updater, quit MultiMC. - MMC->quit(); + quit(); } void MultiMC::setIconTheme(const QString& name) diff --git a/MultiMC.h b/MultiMC.h index bce8a53b..02daed17 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -20,7 +20,6 @@ class LiteLoaderVersionList; class JavaVersionList; class UpdateChecker; class NotificationChecker; -class NewsChecker; class StatusChecker; class BaseProfilerFactory; class BaseDetachedToolFactory; @@ -102,11 +101,6 @@ public: return m_notificationChecker; } - std::shared_ptr newsChecker() - { - return m_newsChecker; - } - std::shared_ptr statusChecker() { return m_statusChecker; @@ -197,7 +191,6 @@ private: std::shared_ptr m_instances; std::shared_ptr m_updateChecker; std::shared_ptr m_notificationChecker; - std::shared_ptr m_newsChecker; std::shared_ptr m_statusChecker; std::shared_ptr m_accounts; std::shared_ptr m_icons; diff --git a/depends/util/include/modutils.h b/depends/util/include/modutils.h index 1fecd4d5..bc608f96 100644 --- a/depends/util/include/modutils.h +++ b/depends/util/include/modutils.h @@ -30,24 +30,76 @@ private: QString m_string; struct Section { - explicit Section(const QString &str, const int num) : numValid(true), number(num), string(str) {} - explicit Section(const QString &str) : numValid(false), string(str) {} + explicit Section(const QString &fullString) + { + m_fullString = fullString; + int cutoff = m_fullString.size(); + for(int i = 0; i < m_fullString.size(); i++) + { + if(!m_fullString[i].isDigit()) + { + cutoff = i; + break; + } + } + auto numPart = m_fullString.leftRef(cutoff); + if(numPart.size()) + { + numValid = true; + m_numPart = numPart.toInt(); + } + auto stringPart = m_fullString.midRef(cutoff); + if(stringPart.size()) + { + m_stringPart = stringPart.toString(); + } + } explicit Section() {} - bool numValid; - int number; - QString string; + bool numValid = false; + int m_numPart = 0; + QString m_stringPart; + QString m_fullString; inline bool operator!=(const Section &other) const { - return (numValid && other.numValid) ? (number != other.number) : (string != other.string); + if(numValid && other.numValid) + { + return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart; + } + else + { + return m_fullString != other.m_fullString; + } } inline bool operator<(const Section &other) const { - return (numValid && other.numValid) ? (number < other.number) : (string < other.string); + if(numValid && other.numValid) + { + if(m_numPart < other.m_numPart) + return true; + if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart) + return true; + return false; + } + else + { + return m_fullString < other.m_fullString; + } } inline bool operator>(const Section &other) const { - return (numValid && other.numValid) ? (number > other.number) : (string > other.string); + if(numValid && other.numValid) + { + if(m_numPart > other.m_numPart) + return true; + if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart) + return true; + return false; + } + else + { + return m_fullString > other.m_fullString; + } } }; QList
m_sections; diff --git a/depends/util/src/modutils.cpp b/depends/util/src/modutils.cpp index 67c09dec..5f89eacd 100644 --- a/depends/util/src/modutils.cpp +++ b/depends/util/src/modutils.cpp @@ -15,9 +15,9 @@ bool Util::Version::operator<(const Version &other) const const int size = qMax(m_sections.size(), other.m_sections.size()); for (int i = 0; i < size; ++i) { - const Section sec1 = (i >= m_sections.size()) ? Section("0", 0) : m_sections.at(i); + const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); const Section sec2 = - (i >= other.m_sections.size()) ? Section("0", 0) : other.m_sections.at(i); + (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); if (sec1 != sec2) { return sec1 < sec2; @@ -35,9 +35,9 @@ bool Util::Version::operator>(const Version &other) const const int size = qMax(m_sections.size(), other.m_sections.size()); for (int i = 0; i < size; ++i) { - const Section sec1 = (i >= m_sections.size()) ? Section("0", 0) : m_sections.at(i); + const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); const Section sec2 = - (i >= other.m_sections.size()) ? Section("0", 0) : other.m_sections.at(i); + (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); if (sec1 != sec2) { return sec1 > sec2; @@ -55,9 +55,9 @@ bool Util::Version::operator==(const Version &other) const const int size = qMax(m_sections.size(), other.m_sections.size()); for (int i = 0; i < size; ++i) { - const Section sec1 = (i >= m_sections.size()) ? Section("0", 0) : m_sections.at(i); + const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); const Section sec2 = - (i >= other.m_sections.size()) ? Section("0", 0) : other.m_sections.at(i); + (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); if (sec1 != sec2) { return false; @@ -79,16 +79,7 @@ void Util::Version::parse() for (const auto part : parts) { - bool ok = false; - int num = part.toInt(&ok); - if (ok) - { - m_sections.append(Section(part, num)); - } - else - { - m_sections.append(Section(part)); - } + m_sections.append(Section(part)); } } diff --git a/gui/ConsoleWindow.cpp b/gui/ConsoleWindow.cpp index 4ee0d487..ad3c2636 100644 --- a/gui/ConsoleWindow.cpp +++ b/gui/ConsoleWindow.cpp @@ -52,8 +52,8 @@ private: BasePage * m_log_page; }; -ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent) - : QMainWindow(parent), m_proc(mcproc) +ConsoleWindow::ConsoleWindow(BaseProcess *process, QWidget *parent) + : QMainWindow(parent), m_proc(process) { MultiMCPlatform::fixWM_CLASS(this); setAttribute(Qt::WA_DeleteOnClose); @@ -120,23 +120,23 @@ ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent) { m_trayIcon = new QSystemTrayIcon(icon, this); m_trayIcon->setToolTip(windowTitle); - + connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); m_trayIcon->show(); } // Set up signal connections - connect(mcproc, SIGNAL(ended(InstancePtr, int, QProcess::ExitStatus)), this, + connect(m_proc, SIGNAL(ended(InstancePtr, int, QProcess::ExitStatus)), this, SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus))); - connect(mcproc, SIGNAL(prelaunch_failed(InstancePtr, int, QProcess::ExitStatus)), this, + connect(m_proc, SIGNAL(prelaunch_failed(InstancePtr, int, QProcess::ExitStatus)), this, SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus))); - connect(mcproc, SIGNAL(launch_failed(InstancePtr)), this, + connect(m_proc, SIGNAL(launch_failed(InstancePtr)), this, SLOT(onLaunchFailed(InstancePtr))); setMayClose(false); - if (mcproc->instance()->settings().get("ShowConsole").toBool()) + if (m_proc->instance()->settings().get("ShowConsole").toBool()) { show(); } @@ -213,7 +213,7 @@ void ConsoleWindow::on_btnKillMinecraft_clicked() "is frozen for some reason"), QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); if (response == QMessageBox::Yes) - m_proc->killMinecraft(); + m_proc->killProcess(); else m_killButton->setEnabled(true); } @@ -254,5 +254,5 @@ void ConsoleWindow::onLaunchFailed(InstancePtr instance) } ConsoleWindow::~ConsoleWindow() { - + } diff --git a/gui/ConsoleWindow.h b/gui/ConsoleWindow.h index 1ad0f760..a14cd2fc 100644 --- a/gui/ConsoleWindow.h +++ b/gui/ConsoleWindow.h @@ -17,7 +17,7 @@ #include #include -#include "logic/MinecraftProcess.h" +#include "logic/BaseProcess.h" class QPushButton; class PageContainer; @@ -26,7 +26,7 @@ class ConsoleWindow : public QMainWindow Q_OBJECT public: - explicit ConsoleWindow(MinecraftProcess *proc, QWidget *parent = 0); + explicit ConsoleWindow(BaseProcess *proc, QWidget *parent = 0); virtual ~ConsoleWindow(); /** @@ -56,7 +56,7 @@ protected: void closeEvent(QCloseEvent *); private: - MinecraftProcess *m_proc = nullptr; + BaseProcess *m_proc = nullptr; bool m_mayclose = true; QSystemTrayIcon *m_trayIcon = nullptr; PageContainer *m_container = nullptr; diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index b28d2d9b..5ef4c09a 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -381,7 +381,7 @@ namespace Ui { #include "logic/BaseInstance.h" #include "logic/OneSixInstance.h" #include "logic/InstanceFactory.h" -#include "logic/MinecraftProcess.h" +#include "logic/BaseProcess.h" #include "logic/OneSixUpdate.h" #include "logic/java/JavaUtils.h" #include "logic/NagUtils.h" @@ -403,6 +403,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); + // initialize the news checker + m_newsChecker.reset(new NewsChecker(BuildConfig.NEWS_RSS_URL)); + QString winTitle = QString("MultiMC 5 - Version %1").arg(BuildConfig.printableVersionString()); if (!BuildConfig.BUILD_PLATFORM.isEmpty()) @@ -443,7 +446,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel); QObject::connect(newsLabel, &QAbstractButton::clicked, this, &MainWindow::newsButtonClicked); - QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this, + QObject::connect(m_newsChecker.get(), &NewsChecker::newsLoaded, this, &MainWindow::updateNewsLabel); updateNewsLabel(); } @@ -606,7 +609,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi MMC->lwjgllist()->loadList(); } - MMC->newsChecker()->reloadNews(); + m_newsChecker->reloadNews(); updateNewsLabel(); // set up the updater object. @@ -888,15 +891,14 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev) void MainWindow::updateNewsLabel() { - auto newsChecker = MMC->newsChecker(); - if (newsChecker->isLoadingNews()) + if (m_newsChecker->isLoadingNews()) { newsLabel->setText(tr("Loading news...")); newsLabel->setEnabled(false); } else { - QList entries = newsChecker->getNewsEntries(); + QList entries = m_newsChecker->getNewsEntries(); if (entries.length() > 0) { newsLabel->setText(entries[0]->title); @@ -1041,7 +1043,9 @@ static QFileInfo findRecursive(const QString &dir, const QString &name) } return QFileInfo(); } -void MainWindow::on_actionAddInstance_triggered() + +// FIXME: eliminate, should not be needed +void MainWindow::waitForMinecraftVersions() { if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && m_versionLoadTask->isRunning()) @@ -1051,121 +1055,130 @@ void MainWindow::on_actionAddInstance_triggered() waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit())); waitLoop.exec(); } +} - NewInstanceDialog newInstDlg(this); - if (!newInstDlg.exec()) - return; - - MMC->settings()->set("LastUsedGroupForNewInstance", newInstDlg.instGroup()); - +void MainWindow::instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url) +{ InstancePtr newInstance; QString instancesDir = MMC->settings()->get("InstanceDir").toString(); - QString instDirName = DirNameFromString(newInstDlg.instName(), instancesDir); + QString instDirName = DirNameFromString(instName, instancesDir); QString instDir = PathCombine(instancesDir, instDirName); auto &loader = InstanceFactory::get(); - - const QUrl modpackUrl = newInstDlg.modpackUrl(); - if (modpackUrl.isValid()) + QString archivePath; + if (url.isLocalFile()) { - QString archivePath; - if (modpackUrl.isLocalFile()) - { - archivePath = modpackUrl.toLocalFile(); - } - else - { - const QString path = modpackUrl.host() + '/' + modpackUrl.path(); - auto entry = MMC->metacache()->resolveEntry("general", path); - CacheDownloadPtr dl = CacheDownload::make(modpackUrl, entry); - NetJob job(tr("Modpack download")); - job.addNetAction(dl); - - // FIXME: possibly causes endless loop problems - ProgressDialog dlDialog(this); - if (dlDialog.exec(&job) != QDialog::Accepted) - { - return; - } - - archivePath = entry->getFullPath(); - } - + archivePath = url.toLocalFile(); + } + else + { + const QString path = url.host() + '/' + url.path(); + auto entry = MMC->metacache()->resolveEntry("general", path); + CacheDownloadPtr dl = CacheDownload::make(url, entry); + NetJob job(tr("Modpack download")); + job.addNetAction(dl); - QTemporaryDir extractTmpDir; - QDir extractDir(extractTmpDir.path()); - QLOG_INFO() << "Attempting to create instance from" << archivePath; - if (JlCompress::extractDir(archivePath, extractDir.absolutePath()).isEmpty()) - { - CustomMessageBox::selectable(this, tr("Error"), - tr("Failed to extract modpack"), QMessageBox::Warning)->show(); - return; - } - const QFileInfo instanceCfgFile = findRecursive(extractDir.absolutePath(), "instance.cfg"); - if (!instanceCfgFile.isFile() || !instanceCfgFile.exists()) + // FIXME: possibly causes endless loop problems + ProgressDialog dlDialog(this); + if (dlDialog.exec(&job) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Error"), tr("Archive does not contain instance.cfg"))->show(); - return; - } - if (!copyPath(instanceCfgFile.absoluteDir().absolutePath(), instDir)) - { - CustomMessageBox::selectable(this, tr("Error"), tr("Unable to copy instance"))->show(); return; } + archivePath = entry->getFullPath(); + } - auto error = loader.loadInstance(newInstance, instDir); - QString errorMsg = tr("Failed to load instance %1: ").arg(instDirName); - switch (error) - { - case InstanceFactory::UnknownLoadError: - errorMsg += tr("Unkown error"); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - case InstanceFactory::NotAnInstance: - errorMsg += tr("Not an instance"); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - } + QTemporaryDir extractTmpDir; + QDir extractDir(extractTmpDir.path()); + QLOG_INFO() << "Attempting to create instance from" << archivePath; + if (JlCompress::extractDir(archivePath, extractDir.absolutePath()).isEmpty()) + { + CustomMessageBox::selectable(this, tr("Error"), + tr("Failed to extract modpack"), QMessageBox::Warning)->show(); + return; } - else + const QFileInfo instanceCfgFile = findRecursive(extractDir.absolutePath(), "instance.cfg"); + if (!instanceCfgFile.isFile() || !instanceCfgFile.exists()) { - auto error = loader.createInstance(newInstance, newInstDlg.selectedVersion(), instDir); - QString errorMsg = tr("Failed to create instance %1: ").arg(instDirName); - switch (error) - { - case InstanceFactory::NoCreateError: break; - case InstanceFactory::InstExists: - { - errorMsg += tr("An instance with the given directory name already exists."); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - } + CustomMessageBox::selectable(this, tr("Error"), tr("Archive does not contain instance.cfg"))->show(); + return; + } + if (!copyPath(instanceCfgFile.absoluteDir().absolutePath(), instDir)) + { + CustomMessageBox::selectable(this, tr("Error"), tr("Unable to copy instance"))->show(); + return; + } - case InstanceFactory::CantCreateDir: - { - errorMsg += tr("Failed to create the instance directory."); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - } + auto error = loader.loadInstance(newInstance, instDir); + QString errorMsg = tr("Failed to load instance %1: ").arg(instDirName); + switch (error) + { + case InstanceFactory::UnknownLoadError: + errorMsg += tr("Unkown error"); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + case InstanceFactory::NotAnInstance: + errorMsg += tr("Not an instance"); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } - default: - { - errorMsg += tr("Unknown instance loader error %1").arg(error); - CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); - return; - } - } + newInstance->setName(instName); + newInstance->setIconKey(instIcon); + newInstance->setGroupInitial(instGroup); + MMC->instances()->add(InstancePtr(newInstance)); + MMC->instances()->saveGroupList(); + + finalizeInstance(newInstance); +} + +void MainWindow::instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version) +{ + InstancePtr newInstance; + + QString instancesDir = MMC->settings()->get("InstanceDir").toString(); + QString instDirName = DirNameFromString(instName, instancesDir); + QString instDir = PathCombine(instancesDir, instDirName); + auto &loader = InstanceFactory::get(); + auto error = loader.createInstance(newInstance, version, instDir); + QString errorMsg = tr("Failed to create instance %1: ").arg(instDirName); + switch (error) + { + case InstanceFactory::NoCreateError: break; + case InstanceFactory::InstExists: + { + errorMsg += tr("An instance with the given directory name already exists."); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } + + case InstanceFactory::CantCreateDir: + { + errorMsg += tr("Failed to create the instance directory."); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; } - newInstance->setName(newInstDlg.instName()); - newInstance->setIconKey(newInstDlg.iconKey()); - newInstance->setGroupInitial(newInstDlg.instGroup()); + default: + { + errorMsg += tr("Unknown instance loader error %1").arg(error); + CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); + return; + } + } + newInstance->setName(instName); + newInstance->setIconKey(instIcon); + newInstance->setGroupInitial(instGroup); MMC->instances()->add(InstancePtr(newInstance)); + MMC->instances()->saveGroupList(); + finalizeInstance(newInstance); +} +void MainWindow::finalizeInstance(InstancePtr inst) +{ if (MMC->accounts()->anyAccountIsValid()) { ProgressDialog loadDialog(this); - auto update = newInstance->doUpdate(); + auto update = inst->doUpdate(); connect(update.get(), &Task::failed, [this](QString reason) { QString error = QString("Instance load failed: %1").arg(reason); @@ -1184,6 +1197,30 @@ void MainWindow::on_actionAddInstance_triggered() } } + +void MainWindow::on_actionAddInstance_triggered() +{ + waitForMinecraftVersions(); + + NewInstanceDialog newInstDlg(this); + if (!newInstDlg.exec()) + return; + + MMC->settings()->set("LastUsedGroupForNewInstance", newInstDlg.instGroup()); + + const QUrl modpackUrl = newInstDlg.modpackUrl(); + + + if (modpackUrl.isValid()) + { + instanceFromZipPack(newInstDlg.instName(), newInstDlg.instGroup(), newInstDlg.iconKey(), modpackUrl); + } + else + { + instanceFromVersion(newInstDlg.instName(), newInstDlg.instGroup(), newInstDlg.iconKey(), newInstDlg.selectedVersion()); + } +} + void MainWindow::on_actionCopyInstance_triggered() { if (!m_selectedInstance) @@ -1389,7 +1426,7 @@ void MainWindow::on_actionMoreNews_triggered() void MainWindow::newsButtonClicked() { - QList entries = MMC->newsChecker()->getNewsEntries(); + QList entries = m_newsChecker->getNewsEntries(); if (entries.count() > 0) openWebPage(QUrl(entries[0]->link)); else @@ -1686,19 +1723,15 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session, QString launchScript; - if (!instance->prepareForLaunch(session, launchScript)) + BaseProcess *proc = instance->prepareForLaunch(session); + if (!proc) return; - MinecraftProcess *proc = new MinecraftProcess(instance); - proc->setLaunchScript(launchScript); - proc->setWorkdir(instance->minecraftRoot()); - this->hide(); console = new ConsoleWindow(proc); connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded())); - proc->setLogin(session); proc->arm(); if (profiler) @@ -1725,7 +1758,7 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session, { dialog.accept(); QMessageBox msg; - msg.setText(tr("The launch of Minecraft itself is delayed until you press the " + msg.setText(tr("The game launch is delayed until you press the " "button. This is the right time to setup the profiler, as the " "profiler server is running now.\n\n%1").arg(message)); msg.setWindowTitle(tr("Waiting")); @@ -1837,32 +1870,6 @@ void MainWindow::instanceEnded() this->show(); } -void MainWindow::checkMigrateLegacyAssets() -{ - int legacyAssets = AssetsUtils::findLegacyAssets(); - if (legacyAssets > 0) - { - ProgressDialog migrateDlg(this); - AssetsMigrateTask migrateTask(legacyAssets, &migrateDlg); - { - ThreadTask threadTask(&migrateTask); - - if (migrateDlg.exec(&threadTask)) - { - QLOG_INFO() << "Assets migration task completed successfully"; - } - else - { - QLOG_INFO() << "Assets migration task reported failure"; - } - } - } - else - { - QLOG_INFO() << "Didn't find any legacy assets to migrate"; - } -} - void MainWindow::checkSetDefaultJava() { const QString javaHack = "IntelHack"; diff --git a/gui/MainWindow.h b/gui/MainWindow.h index 4d417f15..4e8e8f35 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -24,6 +24,7 @@ #include "logic/auth/MojangAccount.h" #include "logic/net/NetJob.h" +class NewsChecker; class QToolButton; class LabeledToolButton; class QLabel; @@ -51,7 +52,6 @@ public: void openWebPage(QUrl url); void checkSetDefaultJava(); - void checkMigrateLegacyAssets(); void checkInstancePathForProblems(); private @@ -182,6 +182,11 @@ protected: void setSelectedInstanceById(const QString &id); + void waitForMinecraftVersions(); + void instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version); + void instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url); + void finalizeInstance(InstancePtr inst); + private: Ui::MainWindow *ui; class GroupView *view; @@ -194,6 +199,7 @@ private: QToolButton *newsLabel; std::shared_ptr m_globalSettingsProvider; + std::shared_ptr m_newsChecker; InstancePtr m_selectedInstance; QString m_currentInstIcon; diff --git a/gui/dialogs/AboutDialog.cpp b/gui/dialogs/AboutDialog.cpp index e15da142..ee08beea 100644 --- a/gui/dialogs/AboutDialog.cpp +++ b/gui/dialogs/AboutDialog.cpp @@ -99,7 +99,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); - MMC->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt())); + connect(ui->aboutQt, &QPushButton::clicked, &QApplication::aboutQt); loadPatronList(); } diff --git a/gui/pages/LogPage.cpp b/gui/pages/LogPage.cpp index 957c2f72..4e9e4f52 100644 --- a/gui/pages/LogPage.cpp +++ b/gui/pages/LogPage.cpp @@ -7,10 +7,10 @@ #include #include -#include "logic/MinecraftProcess.h" +#include "logic/BaseProcess.h" #include "gui/GuiUtil.h" -LogPage::LogPage(MinecraftProcess *proc, QWidget *parent) +LogPage::LogPage(BaseProcess *proc, QWidget *parent) : QWidget(parent), ui(new Ui::LogPage), m_process(proc) { ui->setupUi(this); diff --git a/gui/pages/LogPage.h b/gui/pages/LogPage.h index 7d073a53..3d5580dc 100644 --- a/gui/pages/LogPage.h +++ b/gui/pages/LogPage.h @@ -19,7 +19,7 @@ #include "logic/BaseInstance.h" #include "logic/net/NetJob.h" -#include "logic/MinecraftProcess.h" +#include "logic/BaseProcess.h" #include "BasePage.h" #include @@ -36,7 +36,7 @@ class LogPage : public QWidget, public BasePage Q_OBJECT public: - explicit LogPage(MinecraftProcess *proc, QWidget *parent = 0); + explicit LogPage(BaseProcess *proc, QWidget *parent = 0); virtual ~LogPage(); virtual QString displayName() const override { @@ -78,7 +78,7 @@ private slots: private: Ui::LogPage *ui; - MinecraftProcess *m_process; + BaseProcess *m_process; int m_last_scroll_value = 0; bool m_scroll_active = true; int m_saved_offset = 0; diff --git a/gui/pages/ModFolderPage.cpp b/gui/pages/ModFolderPage.cpp index 5172aa1e..938ec421 100644 --- a/gui/pages/ModFolderPage.cpp +++ b/gui/pages/ModFolderPage.cpp @@ -30,7 +30,7 @@ #include "gui/dialogs/ModEditDialogCommon.h" #include "logic/ModList.h" #include "logic/Mod.h" -#include "logic/VersionFilterData.h" +#include "logic/minecraft/VersionFilterData.h" ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr mods, QString id, QString iconName, QString displayName, QString helpPage, @@ -80,7 +80,7 @@ bool CoreModFolderPage::shouldDisplay() const auto inst = dynamic_cast(m_inst); if (!inst) return true; - auto version = inst->getFullVersion(); + auto version = inst->getMinecraftProfile(); if (!version) return true; if (version->m_releaseTime < g_VersionFilterData.legacyCutoffDate) diff --git a/gui/pages/OtherLogsPage.cpp b/gui/pages/OtherLogsPage.cpp index f12ff8a3..c9b0aa51 100644 --- a/gui/pages/OtherLogsPage.cpp +++ b/gui/pages/OtherLogsPage.cpp @@ -21,17 +21,17 @@ #include "gui/GuiUtil.h" #include "logic/RecursiveFileSystemWatcher.h" -#include "logic/BaseInstance.h" +#include -OtherLogsPage::OtherLogsPage(BaseInstance *instance, QWidget *parent) - : QWidget(parent), ui(new Ui::OtherLogsPage), m_instance(instance), +OtherLogsPage::OtherLogsPage(QString path, QWidget *parent) + : QWidget(parent), ui(new Ui::OtherLogsPage), m_path(path), m_watcher(new RecursiveFileSystemWatcher(this)) { ui->setupUi(this); ui->tabWidget->tabBar()->hide(); m_watcher->setFileExpression("(.*\\.log(\\.[0-9]*)?$)|(crash-.*\\.txt)"); - m_watcher->setRootDir(QDir::current().absoluteFilePath(m_instance->minecraftRoot())); + m_watcher->setRootDir(QDir::current().absoluteFilePath(m_path)); connect(m_watcher, &RecursiveFileSystemWatcher::filesChanged, this, &OtherLogsPage::populateSelectLogBox); @@ -76,7 +76,7 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index) file = ui->selectLogBox->itemText(index); } - if (file.isEmpty() || !QFile::exists(m_instance->minecraftRoot() + "/" + file)) + if (file.isEmpty() || !QFile::exists(PathCombine(m_path, file))) { m_currentFile = QString(); ui->text->clear(); @@ -92,7 +92,7 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index) void OtherLogsPage::on_btnReload_clicked() { - QFile file(m_instance->minecraftRoot() + "/" + m_currentFile); + QFile file(PathCombine(m_path, m_currentFile)); if (!file.open(QFile::ReadOnly)) { setControlsEnabled(false); @@ -132,7 +132,7 @@ void OtherLogsPage::on_btnDelete_clicked() { return; } - QFile file(m_instance->minecraftRoot() + "/" + m_currentFile); + QFile file(PathCombine(m_path, m_currentFile)); if (!file.remove()) { QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2") diff --git a/gui/pages/OtherLogsPage.h b/gui/pages/OtherLogsPage.h index 5777cb83..d6e4ec9f 100644 --- a/gui/pages/OtherLogsPage.h +++ b/gui/pages/OtherLogsPage.h @@ -27,14 +27,12 @@ class OtherLogsPage; class RecursiveFileSystemWatcher; -class BaseInstance; - class OtherLogsPage : public QWidget, public BasePage { Q_OBJECT public: - explicit OtherLogsPage(BaseInstance *instance, QWidget *parent = 0); + explicit OtherLogsPage(QString path, QWidget *parent = 0); ~OtherLogsPage(); QString id() const override @@ -66,7 +64,7 @@ private slots: private: Ui::OtherLogsPage *ui; - BaseInstance *m_instance; + QString m_path; RecursiveFileSystemWatcher *m_watcher; QString m_currentFile; diff --git a/gui/pages/ResourcePackPage.h b/gui/pages/ResourcePackPage.h index 1a9039ff..d79590df 100644 --- a/gui/pages/ResourcePackPage.h +++ b/gui/pages/ResourcePackPage.h @@ -4,7 +4,7 @@ class ResourcePackPage : public ModFolderPage { public: - explicit ResourcePackPage(BaseInstance *instance, QWidget *parent = 0) + explicit ResourcePackPage(MinecraftInstance *instance, QWidget *parent = 0) : ModFolderPage(instance, instance->resourcePackList(), "resourcepacks", "resourcepacks", tr("Resource packs"), "Resource-packs", parent) { diff --git a/gui/pages/ScreenshotsPage.cpp b/gui/pages/ScreenshotsPage.cpp index 1771a118..219a47ae 100644 --- a/gui/pages/ScreenshotsPage.cpp +++ b/gui/pages/ScreenshotsPage.cpp @@ -209,7 +209,7 @@ public: } }; -ScreenshotsPage::ScreenshotsPage(BaseInstance *instance, QWidget *parent) +ScreenshotsPage::ScreenshotsPage(QString path, QWidget *parent) : QWidget(parent), ui(new Ui::ScreenshotsPage) { m_model.reset(new QFileSystemModel()); @@ -219,7 +219,7 @@ ScreenshotsPage::ScreenshotsPage(BaseInstance *instance, QWidget *parent) m_model->setReadOnly(false); m_model->setNameFilters({"*.png"}); m_model->setNameFilterDisables(false); - m_folder = PathCombine(instance->minecraftRoot(), "screenshots"); + m_folder = path; m_valid = ensureFolderPathExists(m_folder); ui->setupUi(this); diff --git a/gui/pages/ScreenshotsPage.h b/gui/pages/ScreenshotsPage.h index 6be2a26d..08ffbf26 100644 --- a/gui/pages/ScreenshotsPage.h +++ b/gui/pages/ScreenshotsPage.h @@ -37,7 +37,7 @@ class ScreenshotsPage : public QWidget, public BasePage Q_OBJECT public: - explicit ScreenshotsPage(BaseInstance *instance, QWidget *parent = 0); + explicit ScreenshotsPage(QString path, QWidget *parent = 0); virtual ~ScreenshotsPage(); virtual void opened() override; diff --git a/gui/pages/TexturePackPage.h b/gui/pages/TexturePackPage.h index 7e663789..3c5c27d7 100644 --- a/gui/pages/TexturePackPage.h +++ b/gui/pages/TexturePackPage.h @@ -4,7 +4,7 @@ class TexturePackPage : public ModFolderPage { public: - explicit TexturePackPage(BaseInstance *instance, QWidget *parent = 0) + explicit TexturePackPage(MinecraftInstance *instance, QWidget *parent = 0) : ModFolderPage(instance, instance->texturePackList(), "texturepacks", "resourcepacks", tr("Texture packs"), "Texture-packs", parent) { diff --git a/gui/pages/VersionPage.cpp b/gui/pages/VersionPage.cpp index 554bf8a4..17869589 100644 --- a/gui/pages/VersionPage.cpp +++ b/gui/pages/VersionPage.cpp @@ -38,7 +38,7 @@ #include #include "logic/ModList.h" -#include "logic/minecraft/InstanceVersion.h" +#include "logic/minecraft/MinecraftProfile.h" #include "logic/EnabledItemFilter.h" #include "logic/forge/ForgeVersionList.h" #include "logic/forge/ForgeInstaller.h" @@ -65,7 +65,7 @@ VersionPage::VersionPage(OneSixInstance *inst, QWidget *parent) ui->setupUi(this); ui->tabWidget->tabBar()->hide(); - m_version = m_inst->getFullVersion(); + m_version = m_inst->getMinecraftProfile(); if (m_version) { main_model = new EnabledItemFilter(this); @@ -109,11 +109,11 @@ void VersionPage::disableVersionControls() ui->removeLibraryBtn->setEnabled(false); } -bool VersionPage::reloadInstanceVersion() +bool VersionPage::reloadMinecraftProfile() { try { - m_inst->reloadVersion(); + m_inst->reloadProfile(); return true; } catch (MMCError &e) @@ -132,7 +132,7 @@ bool VersionPage::reloadInstanceVersion() void VersionPage::on_reloadLibrariesBtn_clicked() { - reloadInstanceVersion(); + reloadMinecraftProfile(); } void VersionPage::on_removeLibraryBtn_clicked() @@ -202,7 +202,7 @@ void VersionPage::on_moveLibraryUpBtn_clicked() try { const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row(); - m_version->move(row, InstanceVersion::MoveUp); + m_version->move(row, MinecraftProfile::MoveUp); } catch (MMCError &e) { @@ -219,7 +219,7 @@ void VersionPage::on_moveLibraryDownBtn_clicked() try { const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row(); - m_version->move(row, InstanceVersion::MoveDown); + m_version->move(row, MinecraftProfile::MoveDown); } catch (MMCError &e) { @@ -244,7 +244,7 @@ void VersionPage::on_changeMCVersionBtn_clicked() return; } - if (m_inst->versionIsCustom()) + if (!m_version->isVanilla()) { auto result = CustomMessageBox::selectable( this, tr("Are you sure?"), @@ -256,7 +256,7 @@ void VersionPage::on_changeMCVersionBtn_clicked() if (result != QMessageBox::Ok) return; m_version->revertToVanilla(); - reloadInstanceVersion(); + reloadMinecraftProfile(); } m_inst->setIntendedVersionId(vselect.selectedVersion()->descriptor()); @@ -272,31 +272,6 @@ void VersionPage::on_changeMCVersionBtn_clicked() void VersionPage::on_forgeBtn_clicked() { - // FIXME: use actual model, not reloading. Move logic to model. - if (m_version->hasFtbPack()) - { - if (QMessageBox::question( - this, tr("Revert?"), - tr("This action will remove the FTB pack version patch. Continue?")) != - QMessageBox::Yes) - { - return; - } - m_version->removeFtbPack(); - reloadInstanceVersion(); - } - if (m_version->hasDeprecatedVersionFiles()) - { - if (QMessageBox::question(this, tr("Revert?"), - tr("This action will remove deprecated version files " - "(custom.json and version.json). Continue?")) != - QMessageBox::Yes) - { - return; - } - m_version->removeDeprecatedVersionFiles(); - reloadInstanceVersion(); - } VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); vselect.setExactFilter(1, m_inst->currentVersionId()); vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + @@ -311,30 +286,6 @@ void VersionPage::on_forgeBtn_clicked() void VersionPage::on_liteloaderBtn_clicked() { - if (m_version->hasFtbPack()) - { - if (QMessageBox::question( - this, tr("Revert?"), - tr("This action will remove the FTB pack version patch. Continue?")) != - QMessageBox::Yes) - { - return; - } - m_version->removeFtbPack(); - reloadInstanceVersion(); - } - if (m_version->hasDeprecatedVersionFiles()) - { - if (QMessageBox::question(this, tr("Revert?"), - tr("This action will remove deprecated version files " - "(custom.json and version.json). Continue?")) != - QMessageBox::Yes) - { - return; - } - m_version->removeDeprecatedVersionFiles(); - reloadInstanceVersion(); - } VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"), this); vselect.setExactFilter(1, m_inst->currentVersionId()); @@ -364,8 +315,7 @@ void VersionPage::versionCurrent(const QModelIndex ¤t, const QModelIndex & ui->moveLibraryUpBtn->setEnabled(enabled); } QString selectedId = m_version->versionFileId(current.row()); - if (selectedId == "net.minecraft" || selectedId == "org.multimc.custom.json" || - selectedId == "org.multimc.version.json") + if (selectedId == "net.minecraft") { ui->changeMCVersionBtn->setEnabled(true); } diff --git a/gui/pages/VersionPage.h b/gui/pages/VersionPage.h index 1249f47e..f1b98dbe 100644 --- a/gui/pages/VersionPage.h +++ b/gui/pages/VersionPage.h @@ -67,11 +67,11 @@ slots: protected: /// FIXME: this shouldn't be necessary! - bool reloadInstanceVersion(); + bool reloadMinecraftProfile(); private: Ui::VersionPage *ui; - std::shared_ptr m_version; + std::shared_ptr m_version; EnabledItemFilter *main_model; OneSixInstance *m_inst; NetJobPtr forgeJob; diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index 8048beea..a073a921 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -28,7 +28,6 @@ #include #include "logic/minecraft/MinecraftVersionList.h" #include "logic/icons/IconList.h" -#include "logic/InstanceList.h" BaseInstance::BaseInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : QObject(parent) @@ -115,30 +114,9 @@ QString BaseInstance::instanceRoot() const return m_rootDir; } -QString BaseInstance::minecraftRoot() const -{ - QFileInfo mcDir(PathCombine(instanceRoot(), "minecraft")); - QFileInfo dotMCDir(PathCombine(instanceRoot(), ".minecraft")); - - if (dotMCDir.exists() && !mcDir.exists()) - return dotMCDir.filePath(); - else - return mcDir.filePath(); -} - -InstanceList *BaseInstance::instList() const -{ - return qobject_cast(parent()); -} - InstancePtr BaseInstance::getSharedPtr() { - return instList()->getInstanceById(id()); -} - -std::shared_ptr BaseInstance::versionList() const -{ - return MMC->minecraftlist(); + return shared_from_this(); } SettingsObject &BaseInstance::settings() const diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index bcf28031..cbac701c 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -24,14 +24,14 @@ #include "logic/settings/INIFile.h" #include "logic/BaseVersionList.h" #include "logic/auth/MojangAccount.h" +#include "Mod.h" class ModList; class QDialog; class QDir; class Task; -class MinecraftProcess; +class BaseProcess; class OneSixUpdate; -class InstanceList; class BaseInstancePrivate; // pointer for lazy people @@ -46,7 +46,7 @@ typedef std::shared_ptr InstancePtr; * To create a new instance type, create a new class inheriting from this class * and implement the pure virtual functions. */ -class BaseInstance : public QObject +class BaseInstance : public QObject, public std::enable_shared_from_this { Q_OBJECT protected: @@ -57,9 +57,10 @@ public: /// virtual destructor to make sure the destruction is COMPLETE virtual ~BaseInstance() {}; - virtual void init() {} virtual void copy(const QDir &newDir) {} + virtual void init() = 0; + /// nuke thoroughly - deletes the instance contents, notifies the list/model which is /// responsible of cleaning up the husk void nuke(); @@ -77,9 +78,6 @@ public: /// Path to the instance's root directory. QString instanceRoot() const; - /// Path to the instance's minecraft directory. - QString minecraftRoot() const; - QString name() const; void setName(QString val); @@ -101,8 +99,6 @@ public: virtual QString intendedVersionId() const = 0; virtual bool setIntendedVersionId(QString version) = 0; - virtual bool versionIsCustom() = 0; - /*! * The instance's current version. * This value represents the instance's current version. If this value is @@ -112,21 +108,11 @@ public: virtual QString currentVersionId() const = 0; /*! - * Whether or not Minecraft should be downloaded when the instance is launched. + * Whether or not 'the game' should be downloaded when the instance is launched. */ virtual bool shouldUpdate() const = 0; virtual void setShouldUpdate(bool val) = 0; - ////// Mod Lists ////// - virtual std::shared_ptr resourcePackList() - { - return nullptr; - } - virtual std::shared_ptr texturePackList() - { - return nullptr; - } - /// Traits. Normally inside the version, depends on instance implementation. virtual QSet traits() = 0; @@ -138,21 +124,13 @@ public: /// Sets the last launched time to 'val' milliseconds since epoch void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); - /*! - * \brief Gets the instance list that this instance is a part of. - * Returns NULL if this instance is not in a list - * (the parent is not an InstanceList). - * \return A pointer to the InstanceList containing this instance. - */ - InstanceList *instList() const; - InstancePtr getSharedPtr(); /*! * \brief Gets a pointer to this instance's version list. * \return A pointer to the available version list for this instance. */ - virtual std::shared_ptr versionList() const; + virtual std::shared_ptr versionList() const = 0; /*! * \brief Gets this instance's settings object. @@ -164,8 +142,8 @@ public: /// returns a valid update task virtual std::shared_ptr doUpdate() = 0; - /// returns a valid minecraft process, ready for launch with the given account. - virtual bool prepareForLaunch(AuthSessionPtr account, QString & launchScript) = 0; + /// returns a valid process, ready for launch with the given account. + virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) = 0; /// do any necessary cleanups after the instance finishes. also runs before /// 'prepareForLaunch' diff --git a/logic/BaseProcess.cpp b/logic/BaseProcess.cpp new file mode 100644 index 00000000..d65e76d9 --- /dev/null +++ b/logic/BaseProcess.cpp @@ -0,0 +1,400 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Authors: Orochimarufan + * + * 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 "logic/BaseProcess.h" +#include "logger/QsLog.h" +#include +#include + +MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) +{ + if (levelName == "MultiMC") + return MessageLevel::MultiMC; + else if (levelName == "Debug") + return MessageLevel::Debug; + else if (levelName == "Info") + return MessageLevel::Info; + else if (levelName == "Message") + return MessageLevel::Message; + else if (levelName == "Warning") + return MessageLevel::Warning; + else if (levelName == "Error") + return MessageLevel::Error; + else if (levelName == "Fatal") + return MessageLevel::Fatal; + // Skip PrePost, it's not exposed to !![]! + else + return MessageLevel::Message; +} + +BaseProcess::BaseProcess(InstancePtr instance): QProcess(), m_instance(instance) +{ +} + +void BaseProcess::init() +{ + connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), + SLOT(finish(int, QProcess::ExitStatus))); + + // prepare the process environment + QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); + + QProcessEnvironment env; + + QStringList ignored = + { + "JAVA_ARGS", + "CLASSPATH", + "CONFIGPATH", + "JAVA_HOME", + "JRE_HOME", + "_JAVA_OPTIONS", + "JAVA_OPTIONS", + "JAVA_TOOL_OPTIONS" + }; + for(auto key: rawenv.keys()) + { + auto value = rawenv.value(key); + // filter out dangerous java crap + if(ignored.contains(key)) + { + QLOG_INFO() << "Env: ignoring" << key << value; + continue; + } + // filter MultiMC-related things + if(key.startsWith("QT_")) + { + QLOG_INFO() << "Env: ignoring" << key << value; + continue; + } +#ifdef LINUX + // Do not pass LD_* variables to java. They were intended for MultiMC + if(key.startsWith("LD_")) + { + QLOG_INFO() << "Env: ignoring" << key << value; + continue; + } + // Strip IBus + // IBus is a Linux IME framework. For some reason, it breaks MC? + if (key == "XMODIFIERS" && value.contains(IBUS)) + { + QString save = value; + value.replace(IBUS, ""); + QLOG_INFO() << "Env: stripped" << IBUS << "from" << save << ":" << value; + } +#endif + QLOG_INFO() << "Env: " << key << value; + env.insert(key, value); + } +#ifdef LINUX + // HACK: Workaround for QTBUG-42500 + env.insert("LD_LIBRARY_PATH", ""); +#endif + + // export some infos + auto variables = getVariables(); + for (auto it = variables.begin(); it != variables.end(); ++it) + { + env.insert(it.key(), it.value()); + } + + this->setProcessEnvironment(env); + m_prepostlaunchprocess.setProcessEnvironment(env); + + // std channels + connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr())); + connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut())); + + // Log prepost launch command output (can be disabled.) + if (m_instance->settings().get("LogPrePostOutput").toBool()) + { + connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this, + &BaseProcess::on_prepost_stdErr); + connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this, + &BaseProcess::on_prepost_stdOut); + } + + // a process has been constructed for the instance. It is running from MultiMC POV + m_instance->setRunning(true); +} + + +void BaseProcess::setWorkdir(QString path) +{ + QDir mcDir(path); + this->setWorkingDirectory(mcDir.absolutePath()); + m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath()); +} + +void BaseProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel, + bool guessLevel, bool censor) +{ + for (int i = 0; i < lines.size(); ++i) + logOutput(lines[i], defaultLevel, guessLevel, censor); +} + +void BaseProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel, + bool censor) +{ + MessageLevel::Enum level = defaultLevel; + + //FIXME: make more flexible in the future + if(line.contains("ignoring option PermSize")) + { + return; + } + + // Level prefix + int endmark = line.indexOf("]!"); + if (line.startsWith("!![") && endmark != -1) + { + level = MessageLevel::getLevel(line.left(endmark).mid(3)); + line = line.mid(endmark + 2); + } + // Guess level + else if (guessLevel) + level = this->guessLevel(line, defaultLevel); + + if (censor) + line = censorPrivateInfo(line); + + emit log(line, level); +} + +void BaseProcess::on_stdErr() +{ + QByteArray data = readAllStandardError(); + QString str = m_err_leftover + QString::fromLocal8Bit(data); + + str.remove('\r'); + QStringList lines = str.split("\n"); + m_err_leftover = lines.takeLast(); + + logOutput(lines, MessageLevel::Error); +} + +void BaseProcess::on_stdOut() +{ + QByteArray data = readAllStandardOutput(); + QString str = m_out_leftover + QString::fromLocal8Bit(data); + + str.remove('\r'); + QStringList lines = str.split("\n"); + m_out_leftover = lines.takeLast(); + + logOutput(lines); +} + +void BaseProcess::on_prepost_stdErr() +{ + QByteArray data = m_prepostlaunchprocess.readAllStandardError(); + QString str = m_err_leftover + QString::fromLocal8Bit(data); + + str.remove('\r'); + QStringList lines = str.split("\n"); + m_err_leftover = lines.takeLast(); + + logOutput(lines, MessageLevel::PrePost, false, false); +} + +void BaseProcess::on_prepost_stdOut() +{ + QByteArray data = m_prepostlaunchprocess.readAllStandardOutput(); + QString str = m_out_leftover + QString::fromLocal8Bit(data); + + str.remove('\r'); + QStringList lines = str.split("\n"); + m_out_leftover = lines.takeLast(); + + logOutput(lines, MessageLevel::PrePost, false, false); +} + +// exit handler +void BaseProcess::finish(int code, ExitStatus status) +{ + // Flush console window + if (!m_err_leftover.isEmpty()) + { + logOutput(m_err_leftover, MessageLevel::Error); + m_err_leftover.clear(); + } + if (!m_out_leftover.isEmpty()) + { + logOutput(m_out_leftover); + m_out_leftover.clear(); + } + + if (!killed) + { + if (status == NormalExit) + { + //: Message displayed on instance exit + emit log(tr("Game exited with exitcode %1.").arg(code)); + } + else + { + //: Message displayed on instance crashed + emit log(tr("Game crashed with exitcode %1.").arg(code)); + } + } + else + { + //: Message displayed after the instance exits due to kill request + emit log(tr("Game was killed by user."), MessageLevel::Error); + } + + m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); + + // run post-exit + postLaunch(); + m_instance->cleanupAfterRun(); + // no longer running... + m_instance->setRunning(false); + emit ended(m_instance, code, status); +} + +void BaseProcess::killProcess() +{ + killed = true; + if (m_prepostlaunchprocess.state() == QProcess::Running) + { + m_prepostlaunchprocess.kill(); + } + else + { + kill(); + } +} + +bool BaseProcess::preLaunch() +{ + QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString(); + if (!prelaunch_cmd.isEmpty()) + { + prelaunch_cmd = substituteVariables(prelaunch_cmd); + // Launch + emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd)); + m_prepostlaunchprocess.start(prelaunch_cmd); + if (!waitForPrePost()) + { + emit log(tr("The command failed to start"), MessageLevel::Fatal); + return false; + } + // Flush console window + if (!m_err_leftover.isEmpty()) + { + logOutput(m_err_leftover, MessageLevel::PrePost); + m_err_leftover.clear(); + } + if (!m_out_leftover.isEmpty()) + { + logOutput(m_out_leftover, MessageLevel::PrePost); + m_out_leftover.clear(); + } + // Process return values + if (m_prepostlaunchprocess.exitStatus() != NormalExit) + { + emit log(tr("Pre-Launch command failed with code %1.\n\n") + .arg(m_prepostlaunchprocess.exitCode()), + MessageLevel::Fatal); + m_instance->cleanupAfterRun(); + emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(), + m_prepostlaunchprocess.exitStatus()); + // not running, failed + m_instance->setRunning(false); + return false; + } + else + emit log(tr("Pre-Launch command ran successfully.\n\n")); + + return m_instance->reload(); + } + return true; +} +bool BaseProcess::postLaunch() +{ + QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString(); + if (!postlaunch_cmd.isEmpty()) + { + postlaunch_cmd = substituteVariables(postlaunch_cmd); + emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd)); + m_prepostlaunchprocess.start(postlaunch_cmd); + if (!waitForPrePost()) + { + return false; + } + // Flush console window + if (!m_err_leftover.isEmpty()) + { + logOutput(m_err_leftover, MessageLevel::PrePost); + m_err_leftover.clear(); + } + if (!m_out_leftover.isEmpty()) + { + logOutput(m_out_leftover, MessageLevel::PrePost); + m_out_leftover.clear(); + } + if (m_prepostlaunchprocess.exitStatus() != NormalExit) + { + emit log(tr("Post-Launch command failed with code %1.\n\n") + .arg(m_prepostlaunchprocess.exitCode()), + MessageLevel::Error); + emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(), + m_prepostlaunchprocess.exitStatus()); + // not running, failed + m_instance->setRunning(false); + } + else + emit log(tr("Post-Launch command ran successfully.\n\n")); + + return m_instance->reload(); + } + return true; +} + +bool BaseProcess::waitForPrePost() +{ + if (!m_prepostlaunchprocess.waitForStarted()) + return false; + QEventLoop eventLoop; + auto finisher = [this, &eventLoop](QProcess::ProcessState state) + { + if (state == QProcess::NotRunning) + { + eventLoop.quit(); + } + }; + auto conne