NOISSUE Refactors and moving of things

This commit is contained in:
Petr Mrázek 2015-01-27 22:31:07 +01:00
parent 593111b144
commit 791221e923
88 changed files with 1894 additions and 1668 deletions

View File

@ -394,6 +394,8 @@ SET(MULTIMC_SOURCES
# LOGIC - Base classes and infrastructure # LOGIC - Base classes and infrastructure
logic/BaseVersion.h logic/BaseVersion.h
logic/BaseProcess.h
logic/BaseProcess.cpp
logic/InstanceFactory.h logic/InstanceFactory.h
logic/InstanceFactory.cpp logic/InstanceFactory.cpp
logic/BaseInstance.h logic/BaseInstance.h
@ -404,12 +406,8 @@ SET(MULTIMC_SOURCES
logic/ModList.cpp logic/ModList.cpp
# sets and maps for deciding based on versions # sets and maps for deciding based on versions
logic/VersionFilterData.h logic/minecraft/VersionFilterData.h
logic/VersionFilterData.cpp logic/minecraft/VersionFilterData.cpp
# Instance launch
logic/MinecraftProcess.h
logic/MinecraftProcess.cpp
# Annoying nag screen logic # Annoying nag screen logic
logic/NagUtils.h logic/NagUtils.h
@ -503,24 +501,34 @@ SET(MULTIMC_SOURCES
logic/JarUtils.h logic/JarUtils.h
logic/JarUtils.cpp logic/JarUtils.cpp
# OneSix version json infrastructure # Minecraft support
logic/minecraft/GradleSpecifier.h logic/minecraft/GradleSpecifier.h
logic/minecraft/InstanceVersion.cpp logic/minecraft/MinecraftProfile.cpp
logic/minecraft/InstanceVersion.h logic/minecraft/MinecraftProfile.h
logic/minecraft/JarMod.cpp logic/minecraft/JarMod.cpp
logic/minecraft/JarMod.h 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.cpp
logic/minecraft/MinecraftVersion.h logic/minecraft/MinecraftVersion.h
logic/minecraft/MinecraftVersionList.cpp logic/minecraft/MinecraftVersionList.cpp
logic/minecraft/MinecraftVersionList.h logic/minecraft/MinecraftVersionList.h
logic/minecraft/NullProfileStrategy.h
logic/minecraft/OneSixLibrary.cpp logic/minecraft/OneSixLibrary.cpp
logic/minecraft/OneSixLibrary.h logic/minecraft/OneSixLibrary.h
logic/minecraft/OneSixProfileStrategy.cpp
logic/minecraft/OneSixProfileStrategy.h
logic/minecraft/OneSixRule.cpp logic/minecraft/OneSixRule.cpp
logic/minecraft/OneSixRule.h logic/minecraft/OneSixRule.h
logic/minecraft/OpSys.cpp logic/minecraft/OpSys.cpp
logic/minecraft/OpSys.h logic/minecraft/OpSys.h
logic/minecraft/ParseUtils.cpp logic/minecraft/ParseUtils.cpp
logic/minecraft/ParseUtils.h logic/minecraft/ParseUtils.h
logic/minecraft/ProfileUtils.cpp
logic/minecraft/ProfileUtils.h
logic/minecraft/ProfileStrategy.h
logic/minecraft/RawLibrary.cpp logic/minecraft/RawLibrary.cpp
logic/minecraft/RawLibrary.h logic/minecraft/RawLibrary.h
logic/minecraft/VersionBuilder.cpp logic/minecraft/VersionBuilder.cpp
@ -528,7 +536,7 @@ SET(MULTIMC_SOURCES
logic/minecraft/VersionBuildError.h logic/minecraft/VersionBuildError.h
logic/minecraft/VersionFile.cpp logic/minecraft/VersionFile.cpp
logic/minecraft/VersionFile.h logic/minecraft/VersionFile.h
logic/minecraft/VersionPatch.h logic/minecraft/ProfilePatch.h
logic/minecraft/VersionSource.h logic/minecraft/VersionSource.h
# A Recursive file system watcher # A Recursive file system watcher
@ -547,10 +555,10 @@ SET(MULTIMC_SOURCES
logic/LwjglVersionList.cpp logic/LwjglVersionList.cpp
# FTB # FTB
logic/OneSixFTBInstance.h logic/ftb/OneSixFTBInstance.h
logic/OneSixFTBInstance.cpp logic/ftb/OneSixFTBInstance.cpp
logic/LegacyFTBInstance.h logic/ftb/LegacyFTBInstance.h
logic/LegacyFTBInstance.cpp logic/ftb/LegacyFTBInstance.cpp
# the screenshots feature # the screenshots feature
logic/screenshots/Screenshot.h logic/screenshots/Screenshot.h
@ -597,8 +605,6 @@ SET(MULTIMC_SOURCES
logic/java/JavaCheckerJob.cpp logic/java/JavaCheckerJob.cpp
# Assets # Assets
logic/assets/AssetsMigrateTask.h
logic/assets/AssetsMigrateTask.cpp
logic/assets/AssetsUtils.h logic/assets/AssetsUtils.h
logic/assets/AssetsUtils.cpp logic/assets/AssetsUtils.cpp

View File

@ -21,8 +21,6 @@
#include "logic/forge/ForgeVersionList.h" #include "logic/forge/ForgeVersionList.h"
#include "logic/news/NewsChecker.h"
#include "logic/status/StatusChecker.h" #include "logic/status/StatusChecker.h"
#include "logic/net/HttpMetaCache.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 // initialize the notification checker
m_notificationChecker.reset(new NotificationChecker()); m_notificationChecker.reset(new NotificationChecker());
// initialize the news checker
m_newsChecker.reset(new NewsChecker(BuildConfig.NEWS_RSS_URL));
// initialize the status checker // initialize the status checker
m_statusChecker.reset(new StatusChecker()); 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"); auto InstDirSetting = m_settings->getSetting("InstanceDir");
// instance path: check for problems with '!' in instance path and warn the user in the log // 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) // 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; QLOG_INFO() << "Instance path : " << instDir;
if (checkProblemticPathJava(QDir(instDir))) if (checkProblemticPathJava(QDir(instDir)))
{ {
@ -243,6 +238,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
// init proxy settings // init proxy settings
updateProxySettings(); updateProxySettings();
//FIXME: what to do with these?
m_profilers.insert("jprofiler", m_profilers.insert("jprofiler",
std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory())); std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
m_profilers.insert("jvisualvm", m_profilers.insert("jvisualvm",
@ -251,6 +247,8 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
{ {
profiler->registerSettings(m_settings); profiler->registerSettings(m_settings);
} }
//FIXME: what to do with these?
m_tools.insert("mcedit", std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory())); m_tools.insert("mcedit", std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory()));
for (auto tool : m_tools.values()) for (auto tool : m_tools.values())
{ {
@ -296,8 +294,7 @@ void MultiMC::initTranslations()
} }
m_mmc_translator.reset(new QTranslator()); m_mmc_translator.reset(new QTranslator());
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), staticData() + "/translations"))
MMC->staticData() + "/translations"))
{ {
QLOG_DEBUG() << "Loading MMC Language File for" QLOG_DEBUG() << "Loading MMC Language File for"
<< locale.bcp47Name().toLocal8Bit().constData() << "..."; << locale.bcp47Name().toLocal8Bit().constData() << "...";
@ -701,13 +698,13 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
} }
QLOG_INFO() << "Installing updates."; QLOG_INFO() << "Installing updates.";
#ifdef WINDOWS #ifdef WINDOWS
QString finishCmd = MMC->applicationFilePath(); QString finishCmd = applicationFilePath();
QString updaterBinary = PathCombine(bin(), "updater.exe"); QString updaterBinary = PathCombine(bin(), "updater.exe");
#elif LINUX #elif LINUX
QString finishCmd = PathCombine(root(), "MultiMC"); QString finishCmd = PathCombine(root(), "MultiMC");
QString updaterBinary = PathCombine(bin(), "updater"); QString updaterBinary = PathCombine(bin(), "updater");
#elif OSX #elif OSX
QString finishCmd = MMC->applicationFilePath(); QString finishCmd = applicationFilePath();
QString updaterBinary = PathCombine(bin(), "updater"); QString updaterBinary = PathCombine(bin(), "updater");
#else #else
#error Unsupported operating system. #error Unsupported operating system.
@ -719,7 +716,7 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
args << "--install-dir" << root(); args << "--install-dir" << root();
args << "--package-dir" << updateFilesDir; args << "--package-dir" << updateFilesDir;
args << "--script" << PathCombine(updateFilesDir, "file_list.xml"); args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
args << "--wait" << QString::number(MMC->applicationPid()); args << "--wait" << QString::number(applicationPid());
if (flags & DryRun) if (flags & DryRun)
args << "--dry-run"; args << "--dry-run";
if (flags & RestartOnFinish) if (flags & RestartOnFinish)
@ -737,7 +734,7 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
} }
// Now that we've started the updater, quit MultiMC. // Now that we've started the updater, quit MultiMC.
MMC->quit(); quit();
} }
void MultiMC::setIconTheme(const QString& name) void MultiMC::setIconTheme(const QString& name)

View File

@ -20,7 +20,6 @@ class LiteLoaderVersionList;
class JavaVersionList; class JavaVersionList;
class UpdateChecker; class UpdateChecker;
class NotificationChecker; class NotificationChecker;
class NewsChecker;
class StatusChecker; class StatusChecker;
class BaseProfilerFactory; class BaseProfilerFactory;
class BaseDetachedToolFactory; class BaseDetachedToolFactory;
@ -102,11 +101,6 @@ public:
return m_notificationChecker; return m_notificationChecker;
} }
std::shared_ptr<NewsChecker> newsChecker()
{
return m_newsChecker;
}
std::shared_ptr<StatusChecker> statusChecker() std::shared_ptr<StatusChecker> statusChecker()
{ {
return m_statusChecker; return m_statusChecker;
@ -197,7 +191,6 @@ private:
std::shared_ptr<InstanceList> m_instances; std::shared_ptr<InstanceList> m_instances;
std::shared_ptr<UpdateChecker> m_updateChecker; std::shared_ptr<UpdateChecker> m_updateChecker;
std::shared_ptr<NotificationChecker> m_notificationChecker; std::shared_ptr<NotificationChecker> m_notificationChecker;
std::shared_ptr<NewsChecker> m_newsChecker;
std::shared_ptr<StatusChecker> m_statusChecker; std::shared_ptr<StatusChecker> m_statusChecker;
std::shared_ptr<MojangAccountList> m_accounts; std::shared_ptr<MojangAccountList> m_accounts;
std::shared_ptr<IconList> m_icons; std::shared_ptr<IconList> m_icons;

View File

@ -30,24 +30,76 @@ private:
QString m_string; QString m_string;
struct Section struct Section
{ {
explicit Section(const QString &str, const int num) : numValid(true), number(num), string(str) {} explicit Section(const QString &fullString)
explicit Section(const QString &str) : numValid(false), string(str) {} {
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() {} explicit Section() {}
bool numValid; bool numValid = false;
int number; int m_numPart = 0;
QString string; QString m_stringPart;
QString m_fullString;
inline bool operator!=(const Section &other) const 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 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 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<Section> m_sections; QList<Section> m_sections;

View File

@ -15,9 +15,9 @@ bool Util::Version::operator<(const Version &other) const
const int size = qMax(m_sections.size(), other.m_sections.size()); const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) 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 = 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) if (sec1 != sec2)
{ {
return 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()); const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) 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 = 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) if (sec1 != sec2)
{ {
return 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()); const int size = qMax(m_sections.size(), other.m_sections.size());
for (int i = 0; i < size; ++i) 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 = 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) if (sec1 != sec2)
{ {
return false; return false;
@ -78,19 +78,10 @@ void Util::Version::parse()
QStringList parts = m_string.split('.'); QStringList parts = m_string.split('.');
for (const auto part : parts) 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));
} }
} }
}
bool Util::versionIsInInterval(const QString &version, const QString &interval) bool Util::versionIsInInterval(const QString &version, const QString &interval)
{ {

View File

@ -52,8 +52,8 @@ private:
BasePage * m_log_page; BasePage * m_log_page;
}; };
ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent) ConsoleWindow::ConsoleWindow(BaseProcess *process, QWidget *parent)
: QMainWindow(parent), m_proc(mcproc) : QMainWindow(parent), m_proc(process)
{ {
MultiMCPlatform::fixWM_CLASS(this); MultiMCPlatform::fixWM_CLASS(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
@ -127,16 +127,16 @@ ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
} }
// Set up signal connections // 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))); 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))); SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
connect(mcproc, SIGNAL(launch_failed(InstancePtr)), this, connect(m_proc, SIGNAL(launch_failed(InstancePtr)), this,
SLOT(onLaunchFailed(InstancePtr))); SLOT(onLaunchFailed(InstancePtr)));
setMayClose(false); setMayClose(false);
if (mcproc->instance()->settings().get("ShowConsole").toBool()) if (m_proc->instance()->settings().get("ShowConsole").toBool())
{ {
show(); show();
} }
@ -213,7 +213,7 @@ void ConsoleWindow::on_btnKillMinecraft_clicked()
"is frozen for some reason"), "is frozen for some reason"),
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec(); QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
if (response == QMessageBox::Yes) if (response == QMessageBox::Yes)
m_proc->killMinecraft(); m_proc->killProcess();
else else
m_killButton->setEnabled(true); m_killButton->setEnabled(true);
} }

View File

@ -17,7 +17,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include "logic/MinecraftProcess.h" #include "logic/BaseProcess.h"
class QPushButton; class QPushButton;
class PageContainer; class PageContainer;
@ -26,7 +26,7 @@ class ConsoleWindow : public QMainWindow
Q_OBJECT Q_OBJECT
public: public:
explicit ConsoleWindow(MinecraftProcess *proc, QWidget *parent = 0); explicit ConsoleWindow(BaseProcess *proc, QWidget *parent = 0);
virtual ~ConsoleWindow(); virtual ~ConsoleWindow();
/** /**
@ -56,7 +56,7 @@ protected:
void closeEvent(QCloseEvent *); void closeEvent(QCloseEvent *);
private: private:
MinecraftProcess *m_proc = nullptr; BaseProcess *m_proc = nullptr;
bool m_mayclose = true; bool m_mayclose = true;
QSystemTrayIcon *m_trayIcon = nullptr; QSystemTrayIcon *m_trayIcon = nullptr;
PageContainer *m_container = nullptr; PageContainer *m_container = nullptr;

View File

@ -381,7 +381,7 @@ namespace Ui {
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/InstanceFactory.h" #include "logic/InstanceFactory.h"
#include "logic/MinecraftProcess.h" #include "logic/BaseProcess.h"
#include "logic/OneSixUpdate.h" #include "logic/OneSixUpdate.h"
#include "logic/java/JavaUtils.h" #include "logic/java/JavaUtils.h"
#include "logic/NagUtils.h" #include "logic/NagUtils.h"
@ -403,6 +403,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
MultiMCPlatform::fixWM_CLASS(this); MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this); ui->setupUi(this);
// initialize the news checker
m_newsChecker.reset(new NewsChecker(BuildConfig.NEWS_RSS_URL));
QString winTitle = QString winTitle =
QString("MultiMC 5 - Version %1").arg(BuildConfig.printableVersionString()); QString("MultiMC 5 - Version %1").arg(BuildConfig.printableVersionString());
if (!BuildConfig.BUILD_PLATFORM.isEmpty()) 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); ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
QObject::connect(newsLabel, &QAbstractButton::clicked, this, QObject::connect(newsLabel, &QAbstractButton::clicked, this,
&MainWindow::newsButtonClicked); &MainWindow::newsButtonClicked);
QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this, QObject::connect(m_newsChecker.get(), &NewsChecker::newsLoaded, this,
&MainWindow::updateNewsLabel); &MainWindow::updateNewsLabel);
updateNewsLabel(); updateNewsLabel();
} }
@ -606,7 +609,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
MMC->lwjgllist()->loadList(); MMC->lwjgllist()->loadList();
} }
MMC->newsChecker()->reloadNews(); m_newsChecker->reloadNews();
updateNewsLabel(); updateNewsLabel();
// set up the updater object. // set up the updater object.
@ -888,15 +891,14 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
void MainWindow::updateNewsLabel() void MainWindow::updateNewsLabel()
{ {
auto newsChecker = MMC->newsChecker(); if (m_newsChecker->isLoadingNews())
if (newsChecker->isLoadingNews())
{ {
newsLabel->setText(tr("Loading news...")); newsLabel->setText(tr("Loading news..."));
newsLabel->setEnabled(false); newsLabel->setEnabled(false);
} }
else else
{ {
QList<NewsEntryPtr> entries = newsChecker->getNewsEntries(); QList<NewsEntryPtr> entries = m_newsChecker->getNewsEntries();
if (entries.length() > 0) if (entries.length() > 0)
{ {
newsLabel->setText(entries[0]->title); newsLabel->setText(entries[0]->title);
@ -1041,7 +1043,9 @@ static QFileInfo findRecursive(const QString &dir, const QString &name)
} }
return QFileInfo(); return QFileInfo();
} }
void MainWindow::on_actionAddInstance_triggered()
// FIXME: eliminate, should not be needed
void MainWindow::waitForMinecraftVersions()
{ {
if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask &&
m_versionLoadTask->isRunning()) m_versionLoadTask->isRunning())
@ -1051,33 +1055,26 @@ void MainWindow::on_actionAddInstance_triggered()
waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit())); waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit()));
waitLoop.exec(); waitLoop.exec();
} }
}
NewInstanceDialog newInstDlg(this); void MainWindow::instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url)
if (!newInstDlg.exec()) {
return;
MMC->settings()->set("LastUsedGroupForNewInstance", newInstDlg.instGroup());
InstancePtr newInstance; InstancePtr newInstance;
QString instancesDir = MMC->settings()->get("InstanceDir").toString(); QString instancesDir = MMC->settings()->get("InstanceDir").toString();
QString instDirName = DirNameFromString(newInstDlg.instName(), instancesDir); QString instDirName = DirNameFromString(instName, instancesDir);
QString instDir = PathCombine(instancesDir, instDirName); QString instDir = PathCombine(instancesDir, instDirName);
auto &loader = InstanceFactory::get(); auto &loader = InstanceFactory::get();
const QUrl modpackUrl = newInstDlg.modpackUrl();
if (modpackUrl.isValid())
{
QString archivePath; QString archivePath;
if (modpackUrl.isLocalFile()) if (url.isLocalFile())
{ {
archivePath = modpackUrl.toLocalFile(); archivePath = url.toLocalFile();
} }
else else
{ {
const QString path = modpackUrl.host() + '/' + modpackUrl.path(); const QString path = url.host() + '/' + url.path();
auto entry = MMC->metacache()->resolveEntry("general", path); auto entry = MMC->metacache()->resolveEntry("general", path);
CacheDownloadPtr dl = CacheDownload::make(modpackUrl, entry); CacheDownloadPtr dl = CacheDownload::make(url, entry);
NetJob job(tr("Modpack download")); NetJob job(tr("Modpack download"));
job.addNetAction(dl); job.addNetAction(dl);
@ -1087,11 +1084,9 @@ void MainWindow::on_actionAddInstance_triggered()
{ {
return; return;
} }
archivePath = entry->getFullPath(); archivePath = entry->getFullPath();
} }
QTemporaryDir extractTmpDir; QTemporaryDir extractTmpDir;
QDir extractDir(extractTmpDir.path()); QDir extractDir(extractTmpDir.path());
QLOG_INFO() << "Attempting to create instance from" << archivePath; QLOG_INFO() << "Attempting to create instance from" << archivePath;
@ -1126,10 +1121,25 @@ void MainWindow::on_actionAddInstance_triggered()
CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show(); CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
return; return;
} }
newInstance->setName(instName);
newInstance->setIconKey(instIcon);
newInstance->setGroupInitial(instGroup);
MMC->instances()->add(InstancePtr(newInstance));
MMC->instances()->saveGroupList();
finalizeInstance(newInstance);
} }
else
void MainWindow::instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version)
{ {
auto error = loader.createInstance(newInstance, newInstDlg.selectedVersion(), instDir); 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); QString errorMsg = tr("Failed to create instance %1: ").arg(instDirName);
switch (error) switch (error)
{ {
@ -1155,17 +1165,20 @@ void MainWindow::on_actionAddInstance_triggered()
return; return;
} }
} }
newInstance->setName(instName);
newInstance->setIconKey(instIcon);
newInstance->setGroupInitial(instGroup);
MMC->instances()->add(InstancePtr(newInstance));
MMC->instances()->saveGroupList();
finalizeInstance(newInstance);
} }
newInstance->setName(newInstDlg.instName()); void MainWindow::finalizeInstance(InstancePtr inst)
newInstance->setIconKey(newInstDlg.iconKey()); {
newInstance->setGroupInitial(newInstDlg.instGroup());
MMC->instances()->add(InstancePtr(newInstance));
if (MMC->accounts()->anyAccountIsValid()) if (MMC->accounts()->anyAccountIsValid())
{ {
ProgressDialog loadDialog(this); ProgressDialog loadDialog(this);
auto update = newInstance->doUpdate(); auto update = inst->doUpdate();
connect(update.get(), &Task::failed, [this](QString reason) connect(update.get(), &Task::failed, [this](QString reason)
{ {
QString error = QString("Instance load failed: %1").arg(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() void MainWindow::on_actionCopyInstance_triggered()
{ {
if (!m_selectedInstance) if (!m_selectedInstance)
@ -1389,7 +1426,7 @@ void MainWindow::on_actionMoreNews_triggered()
void MainWindow::newsButtonClicked() void MainWindow::newsButtonClicked()
{ {
QList<NewsEntryPtr> entries = MMC->newsChecker()->getNewsEntries(); QList<NewsEntryPtr> entries = m_newsChecker->getNewsEntries();
if (entries.count() > 0) if (entries.count() > 0)
openWebPage(QUrl(entries[0]->link)); openWebPage(QUrl(entries[0]->link));
else else
@ -1686,19 +1723,15 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session,
QString launchScript; QString launchScript;
if (!instance->prepareForLaunch(session, launchScript)) BaseProcess *proc = instance->prepareForLaunch(session);
if (!proc)
return; return;
MinecraftProcess *proc = new MinecraftProcess(instance);
proc->setLaunchScript(launchScript);
proc->setWorkdir(instance->minecraftRoot());
this->hide(); this->hide();
console = new ConsoleWindow(proc); console = new ConsoleWindow(proc);
connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded())); connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded()));
proc->setLogin(session);
proc->arm(); proc->arm();
if (profiler) if (profiler)
@ -1725,7 +1758,7 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session,
{ {
dialog.accept(); dialog.accept();
QMessageBox msg; 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 " "button. This is the right time to setup the profiler, as the "
"profiler server is running now.\n\n%1").arg(message)); "profiler server is running now.\n\n%1").arg(message));
msg.setWindowTitle(tr("Waiting")); msg.setWindowTitle(tr("Waiting"));
@ -1837,32 +1870,6 @@ void MainWindow::instanceEnded()
this->show(); 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() void MainWindow::checkSetDefaultJava()
{ {
const QString javaHack = "IntelHack"; const QString javaHack = "IntelHack";

View File

@ -24,6 +24,7 @@
#include "logic/auth/MojangAccount.h" #include "logic/auth/MojangAccount.h"
#include "logic/net/NetJob.h" #include "logic/net/NetJob.h"
class NewsChecker;
class QToolButton; class QToolButton;
class LabeledToolButton; class LabeledToolButton;
class QLabel; class QLabel;
@ -51,7 +52,6 @@ public:
void openWebPage(QUrl url); void openWebPage(QUrl url);
void checkSetDefaultJava(); void checkSetDefaultJava();
void checkMigrateLegacyAssets();
void checkInstancePathForProblems(); void checkInstancePathForProblems();
private private
@ -182,6 +182,11 @@ protected:
void setSelectedInstanceById(const QString &id); 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: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
class GroupView *view; class GroupView *view;
@ -194,6 +199,7 @@ private:
QToolButton *newsLabel; QToolButton *newsLabel;
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider; std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::shared_ptr<NewsChecker> m_newsChecker;
InstancePtr m_selectedInstance; InstancePtr m_selectedInstance;
QString m_currentInstIcon; QString m_currentInstIcon;

View File

@ -99,7 +99,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
MMC->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt())); connect(ui->aboutQt, &QPushButton::clicked, &QApplication::aboutQt);
loadPatronList(); loadPatronList();
} }

View File

@ -7,10 +7,10 @@
#include <QScrollBar> #include <QScrollBar>
#include <QShortcut> #include <QShortcut>
#include "logic/MinecraftProcess.h" #include "logic/BaseProcess.h"
#include "gui/GuiUtil.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) : QWidget(parent), ui(new Ui::LogPage), m_process(proc)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -19,7 +19,7 @@
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "logic/net/NetJob.h" #include "logic/net/NetJob.h"
#include "logic/MinecraftProcess.h" #include "logic/BaseProcess.h"
#include "BasePage.h" #include "BasePage.h"
#include <MultiMC.h> #include <MultiMC.h>
@ -36,7 +36,7 @@ class LogPage : public QWidget, public BasePage
Q_OBJECT Q_OBJECT
public: public:
explicit LogPage(MinecraftProcess *proc, QWidget *parent = 0); explicit LogPage(BaseProcess *proc, QWidget *parent = 0);
virtual ~LogPage(); virtual ~LogPage();
virtual QString displayName() const override virtual QString displayName() const override
{ {
@ -78,7 +78,7 @@ private slots:
private: private:
Ui::LogPage *ui; Ui::LogPage *ui;
MinecraftProcess *m_process; BaseProcess *m_process;
int m_last_scroll_value = 0; int m_last_scroll_value = 0;
bool m_scroll_active = true; bool m_scroll_active = true;
int m_saved_offset = 0; int m_saved_offset = 0;

View File

@ -30,7 +30,7 @@
#include "gui/dialogs/ModEditDialogCommon.h" #include "gui/dialogs/ModEditDialogCommon.h"
#include "logic/ModList.h" #include "logic/ModList.h"
#include "logic/Mod.h" #include "logic/Mod.h"
#include "logic/VersionFilterData.h" #include "logic/minecraft/VersionFilterData.h"
ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods, QString id, ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods, QString id,
QString iconName, QString displayName, QString helpPage, QString iconName, QString displayName, QString helpPage,
@ -80,7 +80,7 @@ bool CoreModFolderPage::shouldDisplay() const
auto inst = dynamic_cast<OneSixInstance *>(m_inst); auto inst = dynamic_cast<OneSixInstance *>(m_inst);
if (!inst) if (!inst)
return true; return true;
auto version = inst->getFullVersion(); auto version = inst->getMinecraftProfile();
if (!version) if (!version)
return true; return true;
if (version->m_releaseTime < g_VersionFilterData.legacyCutoffDate) if (version->m_releaseTime < g_VersionFilterData.legacyCutoffDate)

View File

@ -21,17 +21,17 @@
#include "gui/GuiUtil.h" #include "gui/GuiUtil.h"
#include "logic/RecursiveFileSystemWatcher.h" #include "logic/RecursiveFileSystemWatcher.h"
#include "logic/BaseInstance.h" #include <pathutils.h>
OtherLogsPage::OtherLogsPage(BaseInstance *instance, QWidget *parent) OtherLogsPage::OtherLogsPage(QString path, QWidget *parent)
: QWidget(parent), ui(new Ui::OtherLogsPage), m_instance(instance), : QWidget(parent), ui(new Ui::OtherLogsPage), m_path(path),
m_watcher(new RecursiveFileSystemWatcher(this)) m_watcher(new RecursiveFileSystemWatcher(this))
{ {
ui->setupUi(this); ui->setupUi(this);
ui->tabWidget->tabBar()->hide(); ui->tabWidget->tabBar()->hide();
m_watcher->setFileExpression("(.*\\.log(\\.[0-9]*)?$)|(crash-.*\\.txt)"); 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, connect(m_watcher, &RecursiveFileSystemWatcher::filesChanged, this,
&OtherLogsPage::populateSelectLogBox); &OtherLogsPage::populateSelectLogBox);
@ -76,7 +76,7 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index)
file = ui->selectLogBox->itemText(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(); m_currentFile = QString();
ui->text->clear(); ui->text->clear();
@ -92,7 +92,7 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index)
void OtherLogsPage::on_btnReload_clicked() void OtherLogsPage::on_btnReload_clicked()
{ {
QFile file(m_instance->minecraftRoot() + "/" + m_currentFile); QFile file(PathCombine(m_path, m_currentFile));
if (!file.open(QFile::ReadOnly)) if (!file.open(QFile::ReadOnly))
{ {
setControlsEnabled(false); setControlsEnabled(false);
@ -132,7 +132,7 @@ void OtherLogsPage::on_btnDelete_clicked()
{ {
return; return;
} }
QFile file(m_instance->minecraftRoot() + "/" + m_currentFile); QFile file(PathCombine(m_path, m_currentFile));
if (!file.remove()) if (!file.remove())
{ {
QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2") QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2")

View File

@ -27,14 +27,12 @@ class OtherLogsPage;
class RecursiveFileSystemWatcher; class RecursiveFileSystemWatcher;
class BaseInstance;
class OtherLogsPage : public QWidget, public BasePage class OtherLogsPage : public QWidget, public BasePage
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit OtherLogsPage(BaseInstance *instance, QWidget *parent = 0); explicit OtherLogsPage(QString path, QWidget *parent = 0);
~OtherLogsPage(); ~OtherLogsPage();
QString id() const override QString id() const override
@ -66,7 +64,7 @@ private slots:
private: private:
Ui::OtherLogsPage *ui; Ui::OtherLogsPage *ui;
BaseInstance *m_instance; QString m_path;
RecursiveFileSystemWatcher *m_watcher; RecursiveFileSystemWatcher *m_watcher;
QString m_currentFile; QString m_currentFile;

View File

@ -4,7 +4,7 @@
class ResourcePackPage : public ModFolderPage class ResourcePackPage : public ModFolderPage
{ {
public: public:
explicit ResourcePackPage(BaseInstance *instance, QWidget *parent = 0) explicit ResourcePackPage(MinecraftInstance *instance, QWidget *parent = 0)
: ModFolderPage(instance, instance->resourcePackList(), "resourcepacks", : ModFolderPage(instance, instance->resourcePackList(), "resourcepacks",
"resourcepacks", tr("Resource packs"), "Resource-packs", parent) "resourcepacks", tr("Resource packs"), "Resource-packs", parent)
{ {

View File

@ -209,7 +209,7 @@ public:
} }
}; };
ScreenshotsPage::ScreenshotsPage(BaseInstance *instance, QWidget *parent) ScreenshotsPage::ScreenshotsPage(QString path, QWidget *parent)
: QWidget(parent), ui(new Ui::ScreenshotsPage) : QWidget(parent), ui(new Ui::ScreenshotsPage)
{ {
m_model.reset(new QFileSystemModel()); m_model.reset(new QFileSystemModel());
@ -219,7 +219,7 @@ ScreenshotsPage::ScreenshotsPage(BaseInstance *instance, QWidget *parent)
m_model->setReadOnly(false); m_model->setReadOnly(false);
m_model->setNameFilters({"*.png"}); m_model->setNameFilters({"*.png"});
m_model->setNameFilterDisables(false); m_model->setNameFilterDisables(false);
m_folder = PathCombine(instance->minecraftRoot(), "screenshots"); m_folder = path;
m_valid = ensureFolderPathExists(m_folder); m_valid = ensureFolderPathExists(m_folder);
ui->setupUi(this); ui->setupUi(this);

View File

@ -37,7 +37,7 @@ class ScreenshotsPage : public QWidget, public BasePage
Q_OBJECT Q_OBJECT
public: public:
explicit ScreenshotsPage(BaseInstance *instance, QWidget *parent = 0); explicit ScreenshotsPage(QString path, QWidget *parent = 0);
virtual ~ScreenshotsPage(); virtual ~ScreenshotsPage();
virtual void opened() override; virtual void opened() override;

View File

@ -4,7 +4,7 @@
class TexturePackPage : public ModFolderPage class TexturePackPage : public ModFolderPage
{ {
public: public:
explicit TexturePackPage(BaseInstance *instance, QWidget *parent = 0) explicit TexturePackPage(MinecraftInstance *instance, QWidget *parent = 0)
: ModFolderPage(instance, instance->texturePackList(), "texturepacks", "resourcepacks", : ModFolderPage(instance, instance->texturePackList(), "texturepacks", "resourcepacks",
tr("Texture packs"), "Texture-packs", parent) tr("Texture packs"), "Texture-packs", parent)
{ {

View File

@ -38,7 +38,7 @@
#include <QUrl> #include <QUrl>
#include "logic/ModList.h" #include "logic/ModList.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/EnabledItemFilter.h" #include "logic/EnabledItemFilter.h"
#include "logic/forge/ForgeVersionList.h" #include "logic/forge/ForgeVersionList.h"
#include "logic/forge/ForgeInstaller.h" #include "logic/forge/ForgeInstaller.h"
@ -65,7 +65,7 @@ VersionPage::VersionPage(OneSixInstance *inst, QWidget *parent)
ui->setupUi(this); ui->setupUi(this);
ui->tabWidget->tabBar()->hide(); ui->tabWidget->tabBar()->hide();
m_version = m_inst->getFullVersion(); m_version = m_inst->getMinecraftProfile();
if (m_version) if (m_version)
{ {
main_model = new EnabledItemFilter(this); main_model = new EnabledItemFilter(this);
@ -109,11 +109,11 @@ void VersionPage::disableVersionControls()
ui->removeLibraryBtn->setEnabled(false); ui->removeLibraryBtn->setEnabled(false);
} }
bool VersionPage::reloadInstanceVersion() bool VersionPage::reloadMinecraftProfile()
{ {
try try
{ {
m_inst->reloadVersion(); m_inst->reloadProfile();
return true; return true;
} }
catch (MMCError &e) catch (MMCError &e)
@ -132,7 +132,7 @@ bool VersionPage::reloadInstanceVersion()
void VersionPage::on_reloadLibrariesBtn_clicked() void VersionPage::on_reloadLibrariesBtn_clicked()
{ {
reloadInstanceVersion(); reloadMinecraftProfile();
} }
void VersionPage::on_removeLibraryBtn_clicked() void VersionPage::on_removeLibraryBtn_clicked()
@ -202,7 +202,7 @@ void VersionPage::on_moveLibraryUpBtn_clicked()
try try
{ {
const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row(); const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
m_version->move(row, InstanceVersion::MoveUp); m_version->move(row, MinecraftProfile::MoveUp);
} }
catch (MMCError &e) catch (MMCError &e)
{ {
@ -219,7 +219,7 @@ void VersionPage::on_moveLibraryDownBtn_clicked()
try try
{ {
const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row(); const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
m_version->move(row, InstanceVersion::MoveDown); m_version->move(row, MinecraftProfile::MoveDown);
} }
catch (MMCError &e) catch (MMCError &e)
{ {
@ -244,7 +244,7 @@ void VersionPage::on_changeMCVersionBtn_clicked()
return; return;
} }
if (m_inst->versionIsCustom()) if (!m_version->isVanilla())
{ {
auto result = CustomMessageBox::selectable( auto result = CustomMessageBox::selectable(
this, tr("Are you sure?"), this, tr("Are you sure?"),
@ -256,7 +256,7 @@ void VersionPage::on_changeMCVersionBtn_clicked()
if (result != QMessageBox::Ok) if (result != QMessageBox::Ok)
return; return;
m_version->revertToVanilla(); m_version->revertToVanilla();
reloadInstanceVersion(); reloadMinecraftProfile();
} }
m_inst->setIntendedVersionId(vselect.selectedVersion()->descriptor()); m_inst->setIntendedVersionId(vselect.selectedVersion()->descriptor());
@ -272,31 +272,6 @@ void VersionPage::on_changeMCVersionBtn_clicked()
void VersionPage::on_forgeBtn_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); VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
vselect.setExactFilter(1, m_inst->currentVersionId()); vselect.setExactFilter(1, m_inst->currentVersionId());
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + 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() 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"), VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
this); this);
vselect.setExactFilter(1, m_inst->currentVersionId()); vselect.setExactFilter(1, m_inst->currentVersionId());
@ -364,8 +315,7 @@ void VersionPage::versionCurrent(const QModelIndex &current, const QModelIndex &
ui->moveLibraryUpBtn->setEnabled(enabled); ui->moveLibraryUpBtn->setEnabled(enabled);
} }
QString selectedId = m_version->versionFileId(current.row()); QString selectedId = m_version->versionFileId(current.row());
if (selectedId == "net.minecraft" || selectedId == "org.multimc.custom.json" || if (selectedId == "net.minecraft")
selectedId == "org.multimc.version.json")
{ {
ui->changeMCVersionBtn->setEnabled(true); ui->changeMCVersionBtn->setEnabled(true);
} }

View File

@ -67,11 +67,11 @@ slots:
protected: protected:
/// FIXME: this shouldn't be necessary! /// FIXME: this shouldn't be necessary!
bool reloadInstanceVersion(); bool reloadMinecraftProfile();
private: private:
Ui::VersionPage *ui; Ui::VersionPage *ui;
std::shared_ptr<InstanceVersion> m_version; std::shared_ptr<MinecraftProfile> m_version;
EnabledItemFilter *main_model; EnabledItemFilter *main_model;
OneSixInstance *m_inst; OneSixInstance *m_inst;
NetJobPtr forgeJob; NetJobPtr forgeJob;

View File

@ -28,7 +28,6 @@
#include <cmdutils.h> #include <cmdutils.h>
#include "logic/minecraft/MinecraftVersionList.h" #include "logic/minecraft/MinecraftVersionList.h"
#include "logic/icons/IconList.h" #include "logic/icons/IconList.h"
#include "logic/InstanceList.h"
BaseInstance::BaseInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) BaseInstance::BaseInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: QObject(parent) : QObject(parent)
@ -115,30 +114,9 @@ QString BaseInstance::instanceRoot() const
return m_rootDir; 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<InstanceList *>(parent());
}
InstancePtr BaseInstance::getSharedPtr() InstancePtr BaseInstance::getSharedPtr()
{ {
return instList()->getInstanceById(id()); return shared_from_this();
}
std::shared_ptr<BaseVersionList> BaseInstance::versionList() const
{
return MMC->minecraftlist();
} }
SettingsObject &BaseInstance::settings() const SettingsObject &BaseInstance::settings() const

View File

@ -24,14 +24,14 @@
#include "logic/settings/INIFile.h" #include "logic/settings/INIFile.h"
#include "logic/BaseVersionList.h" #include "logic/BaseVersionList.h"
#include "logic/auth/MojangAccount.h" #include "logic/auth/MojangAccount.h"
#include "Mod.h"
class ModList; class ModList;
class QDialog; class QDialog;
class QDir; class QDir;
class Task; class Task;
class MinecraftProcess; class BaseProcess;
class OneSixUpdate; class OneSixUpdate;
class InstanceList;
class BaseInstancePrivate; class BaseInstancePrivate;
// pointer for lazy people // pointer for lazy people
@ -46,7 +46,7 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
* To create a new instance type, create a new class inheriting from this class * To create a new instance type, create a new class inheriting from this class
* and implement the pure virtual functions. * and implement the pure virtual functions.
*/ */
class BaseInstance : public QObject class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
{ {
Q_OBJECT Q_OBJECT
protected: protected:
@ -57,9 +57,10 @@ public:
/// virtual destructor to make sure the destruction is COMPLETE /// virtual destructor to make sure the destruction is COMPLETE
virtual ~BaseInstance() {}; virtual ~BaseInstance() {};
virtual void init() {}
virtual void copy(const QDir &newDir) {} virtual void copy(const QDir &newDir) {}
virtual void init() = 0;
/// nuke thoroughly - deletes the instance contents, notifies the list/model which is /// nuke thoroughly - deletes the instance contents, notifies the list/model which is
/// responsible of cleaning up the husk /// responsible of cleaning up the husk
void nuke(); void nuke();
@ -77,9 +78,6 @@ public:
/// Path to the instance's root directory. /// Path to the instance's root directory.
QString instanceRoot() const; QString instanceRoot() const;
/// Path to the instance's minecraft directory.
QString minecraftRoot() const;
QString name() const; QString name() const;
void setName(QString val); void setName(QString val);
@ -101,8 +99,6 @@ public:
virtual QString intendedVersionId() const = 0; virtual QString intendedVersionId() const = 0;
virtual bool setIntendedVersionId(QString version) = 0; virtual bool setIntendedVersionId(QString version) = 0;
virtual bool versionIsCustom() = 0;
/*! /*!
* The instance's current version. * The instance's current version.
* This value represents the instance's current version. If this value is * This value represents the instance's current version. If this value is
@ -112,21 +108,11 @@ public:
virtual QString currentVersionId() const = 0; 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 bool shouldUpdate() const = 0;
virtual void setShouldUpdate(bool val) = 0; virtual void setShouldUpdate(bool val) = 0;
////// Mod Lists //////
virtual std::shared_ptr<ModList> resourcePackList()
{
return nullptr;
}
virtual std::shared_ptr<ModList> texturePackList()
{
return nullptr;
}
/// Traits. Normally inside the version, depends on instance implementation. /// Traits. Normally inside the version, depends on instance implementation.
virtual QSet <QString> traits() = 0; virtual QSet <QString> traits() = 0;
@ -138,21 +124,13 @@ public:
/// Sets the last launched time to 'val' milliseconds since epoch /// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); 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(); InstancePtr getSharedPtr();
/*! /*!
* \brief Gets a pointer to this instance's version list. * \brief Gets a pointer to this instance's version list.
* \return A pointer to the available version list for this instance. * \return A pointer to the available version list for this instance.
*/ */
virtual std::shared_ptr<BaseVersionList> versionList() const; virtual std::shared_ptr<BaseVersionList> versionList() const = 0;
/*! /*!
* \brief Gets this instance's settings object. * \brief Gets this instance's settings object.
@ -164,8 +142,8 @@ public:
/// returns a valid update task /// returns a valid update task
virtual std::shared_ptr<Task> doUpdate() = 0; virtual std::shared_ptr<Task> doUpdate() = 0;
/// returns a valid minecraft process, ready for launch with the given account. /// returns a valid process, ready for launch with the given account.
virtual bool prepareForLaunch(AuthSessionPtr account, QString & launchScript) = 0; virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) = 0;
/// do any necessary cleanups after the instance finishes. also runs before /// do any necessary cleanups after the instance finishes. also runs before
/// 'prepareForLaunch' /// 'prepareForLaunch'

View File

@ -14,28 +14,37 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include "MultiMC.h" #include "logic/BaseProcess.h"
#include "BuildConfig.h" #include "logger/QsLog.h"
#include "MinecraftProcess.h"
#include <QDataStream>
#include <QFile>
#include <QDir> #include <QDir>
#include <QProcessEnvironment> #include <QEventLoop>
#include <QRegularExpression>
#include <QStandardPaths>
#include "BaseInstance.h" 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;
}
#include "osutils.h" BaseProcess::BaseProcess(InstancePtr instance): QProcess(), m_instance(instance)
#include "pathutils.h" {
#include "cmdutils.h" }
#define IBUS "@im=ibus" void BaseProcess::init()
// constructor
MinecraftProcess::MinecraftProcess(InstancePtr inst) : m_instance(inst)
{ {
connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
SLOT(finish(int, QProcess::ExitStatus))); SLOT(finish(int, QProcess::ExitStatus)));
@ -113,114 +122,31 @@ MinecraftProcess::MinecraftProcess(InstancePtr inst) : m_instance(inst)
if (m_instance->settings().get("LogPrePostOutput").toBool()) if (m_instance->settings().get("LogPrePostOutput").toBool())
{ {
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this, connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this,
&MinecraftProcess::on_prepost_stdErr); &BaseProcess::on_prepost_stdErr);
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this, connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this,
&MinecraftProcess::on_prepost_stdOut); &BaseProcess::on_prepost_stdOut);
} }
// a process has been constructed for the instance. It is running from MultiMC POV // a process has been constructed for the instance. It is running from MultiMC POV
m_instance->setRunning(true); m_instance->setRunning(true);
} }
void MinecraftProcess::setWorkdir(QString path)
void BaseProcess::setWorkdir(QString path)
{ {
QDir mcDir(path); QDir mcDir(path);
this->setWorkingDirectory(mcDir.absolutePath()); this->setWorkingDirectory(mcDir.absolutePath());
m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath()); m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath());
} }
QString MinecraftProcess::censorPrivateInfo(QString in) void BaseProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel,
{
if (!m_session)
return in;
if (m_session->session != "-")
in.replace(m_session->session, "<SESSION ID>");
in.replace(m_session->access_token, "<ACCESS TOKEN>");
in.replace(m_session->client_token, "<CLIENT TOKEN>");
in.replace(m_session->uuid, "<PROFILE ID>");
in.replace(m_session->player_name, "<PROFILE NAME>");
auto i = m_session->u.properties.begin();
while (i != m_session->u.properties.end())
{
in.replace(i.value(), "<" + i.key().toUpper() + ">");
++i;
}
return in;
}
// console window
MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLevel::Enum level)
{
QRegularExpression re("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
auto match = re.match(line);
if(match.hasMatch())
{
// New style logs from log4j
QString timestamp = match.captured("timestamp");
QString levelStr = match.captured("level");
if(levelStr == "INFO")
level = MessageLevel::Message;
if(levelStr == "WARN")
level = MessageLevel::Warning;
if(levelStr == "ERROR")
level = MessageLevel::Error;
if(levelStr == "FATAL")
level = MessageLevel::Fatal;
if(levelStr == "TRACE" || levelStr == "DEBUG")
level = MessageLevel::Debug;
}
else
{
// Old style forge logs
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
line.contains("[FINER]") || line.contains("[FINEST]"))
level = MessageLevel::Message;
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
level = MessageLevel::Error;
if (line.contains("[WARNING]"))
level = MessageLevel::Warning;
if (line.contains("[DEBUG]"))
level = MessageLevel::Debug;
}
if (line.contains("overwriting existing"))
return MessageLevel::Fatal;
if (line.contains("Exception in thread") || line.contains(QRegularExpression("\\s+at ")))
return MessageLevel::Error;
return level;
}
MessageLevel::Enum MinecraftProcess::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;
}
void MinecraftProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel,
bool guessLevel, bool censor) bool guessLevel, bool censor)
{ {
for (int i = 0; i < lines.size(); ++i) for (int i = 0; i < lines.size(); ++i)
logOutput(lines[i], defaultLevel, guessLevel, censor); logOutput(lines[i], defaultLevel, guessLevel, censor);
} }
void MinecraftProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel, void BaseProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel,
bool censor) bool censor)
{ {
MessageLevel::Enum level = defaultLevel; MessageLevel::Enum level = defaultLevel;
@ -235,7 +161,7 @@ void MinecraftProcess::logOutput(QString line, MessageLevel::Enum defaultLevel,
int endmark = line.indexOf("]!"); int endmark = line.indexOf("]!");
if (line.startsWith("!![") && endmark != -1) if (line.startsWith("!![") && endmark != -1)
{ {
level = getLevel(line.left(endmark).mid(3)); level = MessageLevel::getLevel(line.left(endmark).mid(3));
line = line.mid(endmark + 2); line = line.mid(endmark + 2);
} }
// Guess level // Guess level
@ -248,7 +174,7 @@ void MinecraftProcess::logOutput(QString line, MessageLevel::Enum defaultLevel,
emit log(line, level); emit log(line, level);
} }
void MinecraftProcess::on_stdErr() void BaseProcess::on_stdErr()
{ {
QByteArray data = readAllStandardError(); QByteArray data = readAllStandardError();
QString str = m_err_leftover + QString::fromLocal8Bit(data); QString str = m_err_leftover + QString::fromLocal8Bit(data);
@ -260,7 +186,7 @@ void MinecraftProcess::on_stdErr()
logOutput(lines, MessageLevel::Error); logOutput(lines, MessageLevel::Error);
} }
void MinecraftProcess::on_stdOut() void BaseProcess::on_stdOut()
{ {
QByteArray data = readAllStandardOutput(); QByteArray data = readAllStandardOutput();
QString str = m_out_leftover + QString::fromLocal8Bit(data); QString str = m_out_leftover + QString::fromLocal8Bit(data);
@ -272,7 +198,7 @@ void MinecraftProcess::on_stdOut()
logOutput(lines); logOutput(lines);
} }
void MinecraftProcess::on_prepost_stdErr() void BaseProcess::on_prepost_stdErr()
{ {
QByteArray data = m_prepostlaunchprocess.readAllStandardError(); QByteArray data = m_prepostlaunchprocess.readAllStandardError();
QString str = m_err_leftover + QString::fromLocal8Bit(data); QString str = m_err_leftover + QString::fromLocal8Bit(data);
@ -284,7 +210,7 @@ void MinecraftProcess::on_prepost_stdErr()
logOutput(lines, MessageLevel::PrePost, false, false); logOutput(lines, MessageLevel::PrePost, false, false);
} }
void MinecraftProcess::on_prepost_stdOut() void BaseProcess::on_prepost_stdOut()
{ {
QByteArray data = m_prepostlaunchprocess.readAllStandardOutput(); QByteArray data = m_prepostlaunchprocess.readAllStandardOutput();
QString str = m_out_leftover + QString::fromLocal8Bit(data); QString str = m_out_leftover + QString::fromLocal8Bit(data);
@ -297,7 +223,7 @@ void MinecraftProcess::on_prepost_stdOut()
} }
// exit handler // exit handler
void MinecraftProcess::finish(int code, ExitStatus status) void BaseProcess::finish(int code, ExitStatus status)
{ {
// Flush console window // Flush console window
if (!m_err_leftover.isEmpty()) if (!m_err_leftover.isEmpty())
@ -316,18 +242,18 @@ void MinecraftProcess::finish(int code, ExitStatus status)
if (status == NormalExit) if (status == NormalExit)
{ {
//: Message displayed on instance exit //: Message displayed on instance exit
emit log(tr("Minecraft exited with exitcode %1.").arg(code)); emit log(tr("Game exited with exitcode %1.").arg(code));
} }
else else
{ {
//: Message displayed on instance crashed //: Message displayed on instance crashed
emit log(tr("Minecraft crashed with exitcode %1.").arg(code)); emit log(tr("Game crashed with exitcode %1.").arg(code));
} }
} }
else else
{ {
//: Message displayed after the instance exits due to kill request //: Message displayed after the instance exits due to kill request
emit log(tr("Minecraft was killed by user."), MessageLevel::Error); emit log(tr("Game was killed by user."), MessageLevel::Error);
} }
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
@ -340,7 +266,7 @@ void MinecraftProcess::finish(int code, ExitStatus status)
emit ended(m_instance, code, status); emit ended(m_instance, code, status);
} }
void MinecraftProcess::killMinecraft() void BaseProcess::killProcess()
{ {
killed = true; killed = true;
if (m_prepostlaunchprocess.state() == QProcess::Running) if (m_prepostlaunchprocess.state() == QProcess::Running)
@ -353,7 +279,7 @@ void MinecraftProcess::killMinecraft()
} }
} }
bool MinecraftProcess::preLaunch() bool BaseProcess::preLaunch()
{ {
QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString(); QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString();
if (!prelaunch_cmd.isEmpty()) if (!prelaunch_cmd.isEmpty())
@ -398,7 +324,7 @@ bool MinecraftProcess::preLaunch()
} }
return true; return true;
} }
bool MinecraftProcess::postLaunch() bool BaseProcess::postLaunch()
{ {
QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString(); QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
if (!postlaunch_cmd.isEmpty()) if (!postlaunch_cmd.isEmpty())
@ -439,7 +365,7 @@ bool MinecraftProcess::postLaunch()
return true; return true;
} }
bool MinecraftProcess::waitForPrePost() bool BaseProcess::waitForPrePost()
{ {
if (!m_prepostlaunchprocess.waitForStarted()) if (!m_prepostlaunchprocess.waitForStarted())
return false; return false;
@ -457,18 +383,7 @@ bool MinecraftProcess::waitForPrePost()
return ret == 0; return ret == 0;
} }
QMap<QString, QString> MinecraftProcess::getVariables() const QString BaseProcess::substituteVariables(const QString &cmd) const
{
QMap<QString, QString> out;
out.insert("INST_NAME", m_instance->name());
out.insert("INST_ID", m_instance->id());
out.insert("INST_DIR", QDir(m_instance->instanceRoot()).absolutePath());
out.insert("INST_MC_DIR", QDir(m_instance->minecraftRoot()).absolutePath());
out.insert("INST_JAVA", m_instance->settings().get("JavaPath").toString());
out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
return out;
}
QString MinecraftProcess::substituteVariables(const QString &cmd) const
{ {
QString out = cmd; QString out = cmd;
auto variables = getVariables(); auto variables = getVariables();
@ -483,96 +398,3 @@ QString MinecraftProcess::substituteVariables(const QString &cmd) const
} }
return out; return out;
} }
QStringList MinecraftProcess::javaArguments() const
{
QStringList args;
// custom args go first. we want to override them if we have our own here.
args.append(m_instance->extraArguments());
// OSX dock icon and name
#ifdef OSX
args << "-Xdock:icon=icon.png";
args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
#endif
// HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
#ifdef Q_OS_WIN32
args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
"minecraft.exe.heapdump");
#endif
args << QString("-Xms%1m").arg(m_instance->settings().get("MinMemAlloc").toInt());
args << QString("-Xmx%1m").arg(m_instance->settings().get("MaxMemAlloc").toInt());
auto permgen = m_instance->settings().get("PermGen").toInt();
if (permgen != 64)
{
args << QString("-XX:PermSize=%1m").arg(permgen);
}
args << "-Duser.language=en";
if (!m_nativeFolder.isEmpty())
args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
return args;
}
void MinecraftProcess::arm()
{
emit log("MultiMC version: " + BuildConfig.printableVersionString() + "\n\n");
emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
if (!preLaunch())
{
emit ended(m_instance, 1, QProcess::CrashExit);
return;
}
m_instance->setLastLaunch();
QStringList args = javaArguments();
QString JavaPath = m_instance->settings().get("JavaPath").toString();
emit log("Java path is:\n" + JavaPath + "\n\n");
QString allArgs = args.join(", ");
emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n");
auto realJavaPath = QStandardPaths::findExecutable(JavaPath);
if (realJavaPath.isEmpty())
{
emit log(tr("The java binary \"%1\" couldn't be found. You may have to set up java "
"if Minecraft fails to launch.").arg(JavaPath),
MessageLevel::Warning);
}
// instantiate the launcher part
start(JavaPath, args);
if (!waitForStarted())
{
//: Error message displayed if instace can't start
emit log(tr("Could not launch minecraft!"), MessageLevel::Error);
m_instance->cleanupAfterRun();
emit launch_failed(m_instance);
// not running, failed
m_instance->setRunning(false);
return;
}
// send the launch script to the launcher part
QByteArray bytes = launchScript.toUtf8();
writeData(bytes.constData(), bytes.length());
}
void MinecraftProcess::launch()
{
QString launchString("launch\n");
QByteArray bytes = launchString.toUtf8();
writeData(bytes.constData(), bytes.length());
}
void MinecraftProcess::abort()
{
QString launchString("abort\n");
QByteArray bytes = launchString.toUtf8();
writeData(bytes.constData(), bytes.length());
}

View File

@ -16,9 +16,7 @@
*/ */
#pragma once #pragma once
#include <QProcess> #include <QProcess>
#include <QString>
#include "BaseInstance.h" #include "BaseInstance.h"
/** /**
@ -38,41 +36,18 @@ enum Enum
Fatal, /**< Fatal Errors */ Fatal, /**< Fatal Errors */
PrePost, /**< Pre/Post Launch command output */ PrePost, /**< Pre/Post Launch command output */
}; };
MessageLevel::Enum getLevel(const QString &levelName);
} }
/** class BaseProcess: public QProcess
* @file data/minecraftprocess.h
* @brief The MinecraftProcess class
*/
class MinecraftProcess : public QProcess
{ {
Q_OBJECT Q_OBJECT
public: protected:
/** explicit BaseProcess(InstancePtr instance);
* @brief MinecraftProcess constructor void init();
* @param inst the Instance pointer to launch
*/
MinecraftProcess(InstancePtr inst);
virtual ~MinecraftProcess() public: /* methods */
{ virtual ~BaseProcess() {};
};
/**
* @brief start the launcher part with the provided launch script
*/
void arm();
/**
* @brief launch the armed instance!
*/
void launch();
/**
* @brief abort launch!
*/
void abort();
InstancePtr instance() InstancePtr instance()
{ {
@ -81,26 +56,36 @@ public:
void setWorkdir(QString path); void setWorkdir(QString path);
void setLaunchScript(QString script) void killProcess();
{
launchScript = script;
}
void setNativeFolder(QString natives) /**
{ * @brief prepare the process for launch (for multi-stage launch)
m_nativeFolder = natives; */
} virtual void arm() = 0;
void killMinecraft(); /**
* @brief launch the armed instance
*/
virtual void launch() = 0;
inline void setLogin(AuthSessionPtr session) /**
{ * @brief abort launch
m_session = session; */
} virtual void abort() = 0;
protected: /* methods */
bool preLaunch();
bool postLaunch();
bool waitForPrePost();
QString substituteVariables(const QString &cmd) const;
virtual QMap<QString, QString> getVariables() const = 0;
virtual QString censorPrivateInfo(QString in) = 0;
virtual MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel) = 0;
signals: signals:
/** /**
* @brief emitted when Minecraft immediately fails to run * @brief emitted when the Process immediately fails to run
*/ */
void launch_failed(InstancePtr); void launch_failed(InstancePtr);
@ -115,7 +100,7 @@ signals:
void postlaunch_failed(InstancePtr, int code, QProcess::ExitStatus status); void postlaunch_failed(InstancePtr, int code, QProcess::ExitStatus status);
/** /**
* @brief emitted when mc has finished and the PostLaunchCommand was run * @brief emitted when the process has finished and the PostLaunchCommand was run
*/ */
void ended(InstancePtr, int code, QProcess::ExitStatus status); void ended(InstancePtr, int code, QProcess::ExitStatus status);
@ -126,26 +111,7 @@ signals:
*/ */
void log(QString text, MessageLevel::Enum level = MessageLevel::MultiMC); void log(QString text, MessageLevel::Enum level = MessageLevel::MultiMC);
protected: protected slots:
InstancePtr m_instance;
QString m_err_leftover;
QString m_out_leftover;
QProcess m_prepostlaunchprocess;
bool killed = false;
AuthSessionPtr m_session;
QString launchScript;
QString m_nativeFolder;
bool preLaunch();
bool postLaunch();
bool waitForPrePost();
QMap<QString, QString> getVariables() const;
QString substituteVariables(const QString &cmd) const;
QStringList javaArguments() const;
protected
slots:
void finish(int, QProcess::ExitStatus status); void finish(int, QProcess::ExitStatus status);
void on_stdErr(); void on_stdErr();
void on_stdOut(); void on_stdOut();
@ -158,8 +124,10 @@ slots:
MessageLevel::Enum defaultLevel = MessageLevel::Message, MessageLevel::Enum defaultLevel = MessageLevel::Message,
bool guessLevel = true, bool censor = true); bool guessLevel = true, bool censor = true);
private: protected:
QString censorPrivateInfo(QString in); InstancePtr m_instance;
MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel); QString m_err_leftover;
MessageLevel::Enum getLevel(const QString &levelName); QString m_out_leftover;
QProcess m_prepostlaunchprocess;
bool killed = false;
}; };

View File

@ -27,12 +27,13 @@
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "logic/LegacyInstance.h" #include "logic/LegacyInstance.h"
#include "logic/LegacyFTBInstance.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/OneSixFTBInstance.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/BaseVersion.h" #include "logic/BaseVersion.h"
#include "logic/minecraft/MinecraftVersion.h" #include "logic/minecraft/MinecraftVersion.h"
#include "logic/ftb/LegacyFTBInstance.h"
#include "logic/ftb/OneSixFTBInstance.h"
#include "logic/ftb/FTBVersion.h"
InstanceFactory InstanceFactory::loader; InstanceFactory InstanceFactory::loader;
@ -74,8 +75,8 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(InstancePtr &inst,
return NoLoadError; return NoLoadError;
} }
InstanceFactory::InstCreateError InstanceFactory::createInstance(InstancePtr &inst, BaseVersionPtr version, InstanceFactory::InstCreateError
const QString &instDir, const InstanceFactory::InstType type) InstanceFactory::createInstance(InstancePtr &inst, BaseVersionPtr version, const QString &instDir)
{ {
QDir rootDir(instDir); QDir rootDir(instDir);
@ -85,8 +86,8 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(InstancePtr &in
QLOG_ERROR() << "Can't create instance folder" << instDir; QLOG_ERROR() << "Can't create instance folder" << instDir;
return InstanceFactory::CantCreateDir; return InstanceFactory::CantCreateDir;
} }
auto mcVer = std::dynamic_pointer_cast<MinecraftVersion>(version);
if (!mcVer) if (!version)
{ {
QLOG_ERROR() << "Can't create instance for non-existing MC version"; QLOG_ERROR() << "Can't create instance for non-existing MC version";
return InstanceFactory::NoSuchVersion; return InstanceFactory::NoSuchVersion;
@ -95,39 +96,39 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(InstancePtr &in
auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
m_settings->registerSetting("InstanceType", "Legacy"); m_settings->registerSetting("InstanceType", "Legacy");
if (type == NormalInst) auto minecraftVersion = std::dynamic_pointer_cast<MinecraftVersion>(version);
if(minecraftVersion)
{ {
auto mcVer = std::dynamic_pointer_cast<MinecraftVersion>(version);
m_settings->set("InstanceType", "OneSix"); m_settings->set("InstanceType", "OneSix");
inst.reset(new OneSixInstance(instDir, m_settings)); inst.reset(new OneSixInstance(instDir, m_settings));
inst->setIntendedVersionId(version->descriptor()); inst->setIntendedVersionId(version->descriptor());
inst->init();
return InstanceFactory::NoCreateError;
} }
else if (type == FTBInstance) auto ftbVersion = std::dynamic_pointer_cast<FTBVersion>(version);
if(ftbVersion)
{ {
if(mcVer->usesLegacyLauncher()) auto mcversion = ftbVersion->getMinecraftVersion();
if (mcversion->usesLegacyLauncher())
{ {
m_settings->set("InstanceType", "LegacyFTB"); m_settings->set("InstanceType", "LegacyFTB");
inst.reset(new LegacyFTBInstance(instDir, m_settings)); inst.reset(new LegacyFTBInstance(instDir, m_settings));
inst->setIntendedVersionId(version->descriptor()); inst->setIntendedVersionId(mcversion->descriptor());
} }
else else
{ {
m_settings->set("InstanceType", "OneSixFTB"); m_settings->set("InstanceType", "OneSixFTB");
inst.reset(new OneSixFTBInstance(instDir, m_settings)); inst.reset(new OneSixFTBInstance(instDir, m_settings));
inst->setIntendedVersionId(version->descriptor()); inst->setIntendedVersionId(mcversion->descriptor());
inst->init();
} }
return InstanceFactory::NoCreateError;
} }
else
{
delete m_settings; delete m_settings;
return InstanceFactory::NoSuchVersion; return InstanceFactory::NoSuchVersion;
} }
inst->init();
// FIXME: really, how do you even know?
return InstanceFactory::NoCreateError;
}
InstanceFactory::InstCreateError InstanceFactory::copyInstance(InstancePtr &newInstance, InstanceFactory::InstCreateError InstanceFactory::copyInstance(InstancePtr &newInstance,
InstancePtr &oldInstance, InstancePtr &oldInstance,
const QString &instDir) const QString &instDir)

View File

@ -56,12 +56,6 @@ public:
CantCreateDir CantCreateDir
}; };
enum InstType
{
NormalInst,
FTBInstance
};
/*! /*!
* \brief Creates a stub instance * \brief Creates a stub instance
* *
@ -74,7 +68,7 @@ public:
* - CantCreateDir if the given instance directory cannot be created. * - CantCreateDir if the given instance directory cannot be created.
*/ */
InstCreateError createInstance(InstancePtr &inst, BaseVersionPtr version, InstCreateError createInstance(InstancePtr &inst, BaseVersionPtr version,
const QString &instDir, const InstType type = NormalInst); const QString &instDir);
/*! /*!
* \brief Creates a copy of an existing instance with a new name * \brief Creates a copy of an existing instance with a new name

View File

@ -32,6 +32,7 @@
#include "logic/minecraft/MinecraftVersionList.h" #include "logic/minecraft/MinecraftVersionList.h"
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "logic/InstanceFactory.h" #include "logic/InstanceFactory.h"
#include "ftb/FTBVersion.h"
#include "logger/QsLog.h" #include "logger/QsLog.h"
#include "gui/groupview/GroupView.h" #include "gui/groupview/GroupView.h"
@ -403,16 +404,16 @@ void InstanceList::loadFTBInstances(QMap<QString, QString> &groupMap,
QLOG_INFO() << "Converting " << record.name << " as new."; QLOG_INFO() << "Converting " << record.name << " as new.";
InstancePtr instPtr; InstancePtr instPtr;
auto &factory = InstanceFactory::get(); auto &factory = InstanceFactory::get();
auto version = MMC->minecraftlist()->findVersion(record.mcVersion); auto mcVersion = std::dynamic_pointer_cast<MinecraftVersion>(MMC->minecraftlist()->findVersion(record.mcVersion));
if (!version) if (!mcVersion)
{ {
QLOG_ERROR() << "Can't load instance " << record.instanceDir QLOG_ERROR() << "Can't load instance " << record.instanceDir
<< " because minecraft version " << record.mcVersion << " because minecraft version " << record.mcVersion
<< " can't be resolved."; << " can't be resolved.";
continue; continue;
} }
auto error = factory.createInstance(instPtr, version, record.instanceDir, auto ftbVersion = std::make_shared<FTBVersion>(mcVersion);
InstanceFactory::FTBInstance); auto error = factory.createInstance(instPtr, ftbVersion, record.instanceDir);
if (!instPtr || error != InstanceFactory::NoCreateError) if (!instPtr || error != InstanceFactory::NoCreateError)
continue; continue;

View File

@ -19,7 +19,6 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSet> #include <QSet>
#include <gui/groupview/GroupedProxyModel.h> #include <gui/groupview/GroupedProxyModel.h>
#include <QIcon>
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
@ -55,7 +54,7 @@ private:
QSet<FTBRecord> discoverFTBInstances(); QSet<FTBRecord> discoverFTBInstances();
void loadFTBInstances(QMap<QString, QString> &groupMap, QList<InstancePtr> & tempList); void loadFTBInstances(QMap<QString, QString> &groupMap, QList<InstancePtr> & tempList);
private public
slots: slots:
void saveGroupList(); void saveGroupList();

View File

@ -30,7 +30,6 @@ bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
continue; continue;
} }
contained.insert(filename); contained.insert(filename);
QLOG_INFO() << "Adding file " << filename << " from " << from.fileName();
if (!fileInsideMod.open(QIODevice::ReadOnly)) if (!fileInsideMod.open(QIODevice::ReadOnly))
{ {
@ -103,8 +102,6 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<M
return false; return false;
} }
addedFiles.insert(filename.fileName()); addedFiles.insert(filename.fileName());
QLOG_INFO() << "Adding file " << filename.fileName() << " from "
<< filename.absoluteFilePath();
} }
else if (mod.type() == Mod::MOD_FOLDER) else if (mod.type() == Mod::MOD_FOLDER)
{ {

View File

@ -24,9 +24,9 @@
#include "LegacyInstance.h" #include "LegacyInstance.h"
#include "logic/MinecraftProcess.h"
#include "logic/LegacyUpdate.h" #include "logic/LegacyUpdate.h"
#include "logic/icons/IconList.h" #include "logic/icons/IconList.h"
#include "logic/minecraft/MinecraftProcess.h"
#include "gui/pages/LegacyUpgradePage.h" #include "gui/pages/LegacyUpgradePage.h"
#include "gui/pages/ModFolderPage.h" #include "gui/pages/ModFolderPage.h"
#include "gui/pages/LegacyJarModPage.h" #include "gui/pages/LegacyJarModPage.h"
@ -36,7 +36,7 @@
#include <gui/pages/ScreenshotsPage.h> #include <gui/pages/ScreenshotsPage.h>
LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: BaseInstance(rootDir, settings, parent) : MinecraftInstance(rootDir, settings, parent)
{ {
settings->registerSetting("NeedsRebuild", true); settings->registerSetting("NeedsRebuild", true);
settings->registerSetting("ShouldUpdate", false); settings->registerSetting("ShouldUpdate", false);
@ -66,7 +66,7 @@ QList<BasePage *> LegacyInstance::getPages()
"Loader-mods")); "Loader-mods"));
values.append(new TexturePackPage(this)); values.append(new TexturePackPage(this));
values.append(new NotesPage(this)); values.append(new NotesPage(this));
values.append(new ScreenshotsPage(this)); values.append(new ScreenshotsPage(PathCombine(minecraftRoot(), "screenshots")));
values.append(new InstanceSettingsPage(this)); values.append(new InstanceSettingsPage(this));
return values; return values;
} }
@ -124,8 +124,9 @@ std::shared_ptr<Task> LegacyInstance::doUpdate()
return std::shared_ptr<Task>(new LegacyUpdate(this, this)); return std::shared_ptr<Task>(new LegacyUpdate(this, this));
} }
bool LegacyInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScript) BaseProcess *LegacyInstance::prepareForLaunch(AuthSessionPtr account)
{ {
QString launchScript;
QIcon icon = MMC->icons()->getIcon(iconKey()); QIcon icon = MMC->icons()->getIcon(iconKey());
auto pixmap = icon.pixmap(128, 128); auto pixmap = icon.pixmap(128, 128);
pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
@ -150,7 +151,11 @@ bool LegacyInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScr
launchScript += "lwjgl " + lwjgl + "\n"; launchScript += "lwjgl " + lwjgl + "\n";
launchScript += "launcher legacy\n"; launchScript += "launcher legacy\n";
} }
return true; auto process = MinecraftProcess::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr()));
process->setLaunchScript(launchScript);
process->setWorkdir(minecraftRoot());
process->setLogin(account);
return process;
} }
void LegacyInstance::cleanupAfterRun() void LegacyInstance::cleanupAfterRun()
@ -158,7 +163,7 @@ void LegacyInstance::cleanupAfterRun()
// FIXME: delete the launcher and icons and whatnot. // FIXME: delete the launcher and icons and whatnot.
} }
std::shared_ptr<ModList> LegacyInstance::coreModList() std::shared_ptr<ModList> LegacyInstance::coreModList() const
{ {
if (!core_mod_list) if (!core_mod_list)
{ {
@ -168,7 +173,7 @@ std::shared_ptr<ModList> LegacyInstance::coreModList()
return core_mod_list; return core_mod_list;
} }
std::shared_ptr<ModList> LegacyInstance::jarModList() std::shared_ptr<ModList> LegacyInstance::jarModList() const
{ {
if (!jar_mod_list) if (!jar_mod_list)
{ {
@ -180,13 +185,18 @@ std::shared_ptr<ModList> LegacyInstance::jarModList()
return jar_mod_list; return jar_mod_list;
} }
QList<Mod> LegacyInstance::getJarMods() const
{
return jarModList()->allMods();
}
void LegacyInstance::jarModsChanged() void LegacyInstance::jarModsChanged()
{ {
QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt."; QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt.";
setShouldRebuild(true); setShouldRebuild(true);
} }
std::shared_ptr<ModList> LegacyInstance::loaderModList() std::shared_ptr<ModList> LegacyInstance::loaderModList() const
{ {
if (!loader_mod_list) if (!loader_mod_list)
{ {
@ -196,7 +206,7 @@ std::shared_ptr<ModList> LegacyInstance::loaderModList()
return loader_mod_list; return loader_mod_list;
} }
std::shared_ptr<ModList> LegacyInstance::texturePackList() std::shared_ptr<ModList> LegacyInstance::texturePackList() const
{ {
if (!texture_pack_list) if (!texture_pack_list)
{ {

View File

@ -15,13 +15,13 @@
#pragma once #pragma once
#include "BaseInstance.h" #include "logic/minecraft/MinecraftInstance.h"
#include "gui/pages/BasePageProvider.h" #include "gui/pages/BasePageProvider.h"
class ModList; class ModList;
class Task; class Task;
class LegacyInstance : public BaseInstance, public BasePageProvider class LegacyInstance : public MinecraftInstance, public BasePageProvider
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -29,6 +29,8 @@ public:
explicit LegacyInstance(const QString &rootDir, SettingsObject *settings, explicit LegacyInstance(const QString &rootDir, SettingsObject *settings,
QObject *parent = 0); QObject *parent = 0);
virtual void init() {};
/// Path to the instance's minecraft.jar /// Path to the instance's minecraft.jar
QString runnableJar() const; QString runnableJar() const;
@ -40,10 +42,11 @@ public:
virtual QString dialogTitle(); virtual QString dialogTitle();
////// Mod Lists ////// ////// Mod Lists //////
std::shared_ptr<ModList> jarModList(); std::shared_ptr<ModList> jarModList() const ;
std::shared_ptr<ModList> coreModList(); virtual QList< Mod > getJarMods() const override;
std::shared_ptr<ModList> loaderModList(); std::shared_ptr<ModList> coreModList() const;
std::shared_ptr<ModList> texturePackList(); std::shared_ptr<ModList> loaderModList() const;
std::shared_ptr<ModList> texturePackList() const override;
////// Directories ////// ////// Directories //////
QString libDir() const; QString libDir() const;
@ -94,12 +97,6 @@ public:
virtual QString intendedVersionId() const override; virtual QString intendedVersionId() const override;
virtual bool setIntendedVersionId(QString version) override; virtual bool setIntendedVersionId(QString version) override;
// the `version' of Legacy instances is defined by the launcher code.
// in contrast with OneSix, where `version' is described in a json file
virtual bool versionIsCustom() override
{
return false;
}
virtual QSet<QString> traits() virtual QSet<QString> traits()
{ {
@ -110,16 +107,16 @@ public:
virtual void setShouldUpdate(bool val) override; virtual void setShouldUpdate(bool val) override;
virtual std::shared_ptr<Task> doUpdate() override; virtual std::shared_ptr<Task> doUpdate() override;
virtual bool prepareForLaunch(AuthSessionPtr account, QString & launchScript) override; virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;
virtual QString getStatusbarDescription() override; virtual QString getStatusbarDescription() override;
protected: protected:
std::shared_ptr<ModList> jar_mod_list; mutable std::shared_ptr<ModList> jar_mod_list;
std::shared_ptr<ModList> core_mod_list; mutable std::shared_ptr<ModList> core_mod_list;
std::shared_ptr<ModList> loader_mod_list; mutable std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> texture_pack_list; mutable std::shared_ptr<ModList> texture_pack_list;
protected protected
slots: slots:

View File

@ -409,7 +409,7 @@ void LegacyUpdate::ModTheJar()
} }
// Get the mod list // Get the mod list
auto modList = inst->jarModList(); auto modList = inst->getJarMods();
QFileInfo runnableJar(inst->runnableJar()); QFileInfo runnableJar(inst->runnableJar());
QFileInfo baseJar(inst->baseJar()); QFileInfo baseJar(inst->baseJar());
@ -421,7 +421,7 @@ void LegacyUpdate::ModTheJar()
// yes, this can happen if the instance only has the runnable jar and not the base jar // yes, this can happen if the instance only has the runnable jar and not the base jar
// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe // it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
// because that's not something mmc4 guarantees // because that's not something mmc4 guarantees
if (runnableJar.isFile() && !baseJar.exists() && modList->empty()) if (runnableJar.isFile() && !baseJar.exists() && modList.empty())
{ {
inst->setShouldRebuild(false); inst->setShouldRebuild(false);
emitSucceeded(); emitSucceeded();
@ -452,14 +452,12 @@ void LegacyUpdate::ModTheJar()
return; return;
} }
// TaskStep(); // STEP 1
setStatus(tr("Installing mods: Opening minecraft.jar ...")); setStatus(tr("Installing mods: Opening minecraft.jar ..."));
const auto & mods = modList->allMods();
QString outputJarPath = runnableJar.filePath(); QString outputJarPath = runnableJar.filePath();
QString inputJarPath = baseJar.filePath(); QString inputJarPath = baseJar.filePath();
if(!JarUtils::createModdedJar(inputJarPath, outputJarPath, mods)) if(!JarUtils::createModdedJar(inputJarPath, outputJarPath, modList))
{ {
emitFailed(tr("Failed to create the custom Minecraft jar file.")); emitFailed(tr("Failed to create the custom Minecraft jar file."));
return; return;

View File

@ -21,7 +21,7 @@
#include "logic/net/NetJob.h" #include "logic/net/NetJob.h"
#include "logic/tasks/Task.h" #include "logic/tasks/Task.h"
#include "logic/VersionFilterData.h" #include "logic/minecraft/VersionFilterData.h"
class MinecraftVersion; class MinecraftVersion;
class BaseInstance; class BaseInstance;

View File

@ -22,12 +22,13 @@
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/OneSixUpdate.h" #include "logic/OneSixUpdate.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "minecraft/VersionBuildError.h" #include "minecraft/VersionBuildError.h"
#include "logic/minecraft/MinecraftProcess.h"
#include "minecraft/OneSixProfileStrategy.h"
#include "logic/assets/AssetsUtils.h" #include "logic/assets/AssetsUtils.h"
#include "icons/IconList.h" #include "logic/icons/IconList.h"
#include "logic/MinecraftProcess.h"
#include "gui/pagedialog/PageDialog.h" #include "gui/pagedialog/PageDialog.h"
#include "gui/pages/VersionPage.h" #include "gui/pages/VersionPage.h"
#include "gui/pages/ModFolderPage.h" #include "gui/pages/ModFolderPage.h"
@ -39,24 +40,22 @@
#include "gui/pages/OtherLogsPage.h" #include "gui/pages/OtherLogsPage.h"
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: BaseInstance(rootDir, settings, parent) : MinecraftInstance(rootDir, settings, parent)
{ {
m_settings->registerSetting("IntendedVersion", ""); m_settings->registerSetting("IntendedVersion", "");
version.reset(new InstanceVersion(this, this));
} }
void OneSixInstance::init() void OneSixInstance::init()
{ {
try createProfile();
}
void OneSixInstance::createProfile()
{ {
reloadVersion(); m_version.reset(new MinecraftProfile(new OneSixProfileStrategy(this)));
}
catch (MMCError &e)
{
QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
}
} }
QList<BasePage *> OneSixInstance::getPages() QList<BasePage *> OneSixInstance::getPages()
{ {
QList<BasePage *> values; QList<BasePage *> values;
@ -68,9 +67,9 @@ QList<BasePage *> OneSixInstance::getPages()
values.append(new ResourcePackPage(this)); values.append(new ResourcePackPage(this));
values.append(new TexturePackPage(this)); values.append(new TexturePackPage(this));
values.append(new NotesPage(this)); values.append(new NotesPage(this));
values.append(new ScreenshotsPage(this)); values.append(new ScreenshotsPage(PathCombine(minecraftRoot(), "screenshots")));
values.append(new InstanceSettingsPage(this)); values.append(new InstanceSettingsPage(this));
values.append(new OtherLogsPage(this)); values.append(new OtherLogsPage(minecraftRoot()));
return values; return values;
} }
@ -81,7 +80,7 @@ QString OneSixInstance::dialogTitle()
QSet<QString> OneSixInstance::traits() QSet<QString> OneSixInstance::traits()
{ {
auto version = getFullVersion(); auto version = getMinecraftProfile();
if (!version) if (!version)
{ {
return {"version-incomplete"}; return {"version-incomplete"};
@ -119,70 +118,10 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result; return result;
} }
QDir OneSixInstance::reconstructAssets(std::shared_ptr<InstanceVersion> version)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects"));
QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual"));
QString indexPath = PathCombine(indexDir.path(), version->assets + ".json");
QFile indexFile(indexPath);
QDir virtualRoot(PathCombine(virtualDir.path(), version->assets));
if (!indexFile.exists())
{
QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets";
return virtualRoot;
}
QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path()
<< objectDir.path() << virtualDir.path() << virtualRoot.path();
AssetsIndex index;
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index);
if (loadAssetsIndex && index.isVirtual)
{
QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path();
for (QString map : index.objects.keys())
{
AssetObject asset_object = index.objects.value(map);
QString target_path = PathCombine(virtualRoot.path(), map);
QFile target(target_path);
QString tlk = asset_object.hash.left(2);
QString original_path =
PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash);
QFile original(original_path);
if (!original.exists())
continue;
if (!target.exists())
{
QFileInfo info(target_path);
QDir target_dir = info.dir();
// QLOG_DEBUG() << target_dir;
if (!target_dir.exists())
QDir("").mkpath(target_dir.path());
bool couldCopy = original.copy(target_path);
QLOG_DEBUG() << " Copying" << original_path << "to" << target_path
<< QString::number(couldCopy); // << original.errorString();
}
}
// TODO: Write last used time to virtualRoot/.lastused
}
return virtualRoot;
}
QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
{ {
QString args_pattern = version->minecraftArguments; QString args_pattern = m_version->minecraftArguments;
for (auto tweaker : version->tweakers) for (auto tweaker : m_version->tweakers)
{ {
args_pattern += " --tweakClass " + tweaker; args_pattern += " --tweakClass " + tweaker;
} }
@ -197,18 +136,18 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
// these do nothing and are stupid. // these do nothing and are stupid.
token_mapping["profile_name"] = name(); token_mapping["profile_name"] = name();
token_mapping["version_name"] = version->id; token_mapping["version_name"] = m_version->id;
QString absRootDir = QDir(minecraftRoot()).absolutePath(); QString absRootDir = QDir(minecraftRoot()).absolutePath();
token_mapping["game_directory"] = absRootDir; token_mapping["game_directory"] = absRootDir;
QString absAssetsDir = QDir("assets/").absolutePath(); QString absAssetsDir = QDir("assets/").absolutePath();
token_mapping["game_assets"] = reconstructAssets(version).absolutePath(); token_mapping["game_assets"] = AssetsUtils::reconstructAssets(m_version->assets).absolutePath();
token_mapping["user_properties"] = session->serializeUserProperties(); token_mapping["user_properties"] = session->serializeUserProperties();
token_mapping["user_type"] = session->user_type; token_mapping["user_type"] = session->user_type;
// 1.7.3+ assets tokens // 1.7.3+ assets tokens
token_mapping["assets_root"] = absAssetsDir; token_mapping["assets_root"] = absAssetsDir;
token_mapping["assets_index_name"] = version->assets; token_mapping["assets_index_name"] = m_version->assets;
QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
for (int i = 0; i < parts.length(); i++) for (int i = 0; i < parts.length(); i++)
@ -218,40 +157,41 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts; return parts;
} }
bool OneSixInstance::prepareForLaunch(AuthSessionPtr session, QString &launchScript) BaseProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session)
{ {
QString launchScript;
QIcon icon = MMC->icons()->getIcon(iconKey()); QIcon icon = MMC->icons()->getIcon(iconKey());
auto pixmap = icon.pixmap(128, 128); auto pixmap = icon.pixmap(128, 128);
pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
if (!version) if (!m_version)
return nullptr; return nullptr;
// libraries and class path. // libraries and class path.
{ {
auto libs = version->getActiveNormalLibs(); auto libs = m_version->getActiveNormalLibs();
for (auto lib : libs) for (auto lib : libs)
{ {
launchScript += "cp " + librariesPath().absoluteFilePath(lib->storagePath()) + "\n"; launchScript += "cp " + librariesPath().absoluteFilePath(lib->storagePath()) + "\n";
} }
if (version->hasJarMods()) auto jarMods = getJarMods();
if (!jarMods.isEmpty())
{ {
launchScript += "cp " + QDir(instanceRoot()).absoluteFilePath("temp.jar") + "\n"; launchScript += "cp " + QDir(instanceRoot()).absoluteFilePath("temp.jar") + "\n";
} }
else else
{ {
QString relpath = version->id + "/" + version->id + ".jar"; QString relpath = m_version->id + "/" + m_version->id + ".jar";
launchScript += "cp " + versionsPath().absoluteFilePath(relpath) + "\n"; launchScript += "cp " + versionsPath().absoluteFilePath(relpath) + "\n";
} }
} }
if (!version->mainClass.isEmpty()) if (!m_version->mainClass.isEmpty())
{ {
launchScript += "mainClass " + version->mainClass + "\n"; launchScript += "mainClass " + m_version->mainClass + "\n";
} }
if (!version->appletClass.isEmpty()) if (!m_version->appletClass.isEmpty())
{ {
launchScript += "appletClass " + version->appletClass + "\n"; launchScript += "appletClass " + m_version->appletClass + "\n";
} }
// generic minecraft params // generic minecraft params
@ -282,7 +222,7 @@ bool OneSixInstance::prepareForLaunch(AuthSessionPtr session, QString &launchScr
// native libraries (mostly LWJGL) // native libraries (mostly LWJGL)
{ {
QDir natives_dir(PathCombine(instanceRoot(), "natives/")); QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
for (auto native : version->getActiveNativeLibs()) for (auto native : m_version->getActiveNativeLibs())
{ {
QFileInfo finfo(PathCombine("libraries", native->storagePath())); QFileInfo finfo(PathCombine("libraries", native->storagePath()));
launchScript += "ext " + finfo.absoluteFilePath() + "\n"; launchScript += "ext " + finfo.absoluteFilePath() + "\n";
@ -291,12 +231,17 @@ bool OneSixInstance::prepareForLaunch(AuthSessionPtr session, QString &launchScr
} }
// traits. including legacyLaunch and others ;) // traits. including legacyLaunch and others ;)
for (auto trait : version->traits) for (auto trait : m_version->traits)
{ {
launchScript += "traits " + trait + "\n"; launchScript += "traits " + trait + "\n";
} }
launchScript += "launcher onesix\n"; launchScript += "launcher onesix\n";
return true;
auto process = MinecraftProcess::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr()));
process->setLaunchScript(launchScript);
process->setWorkdir(minecraftRoot());
process->setLogin(session);
return process;
} }
void OneSixInstance::cleanupAfterRun() void OneSixInstance::cleanupAfterRun()
@ -306,54 +251,68 @@ void OneSixInstance::cleanupAfterRun()
dir.removeRecursively(); dir.removeRecursively();
} }
std::shared_ptr<ModList> OneSixInstance::loaderModList() std::shared_ptr<ModList> OneSixInstance::loaderModList() const
{ {
if (!loader_mod_list) if (!m_loader_mod_list)
{ {
loader_mod_list.reset(new ModList(loaderModsDir())); m_loader_mod_list.reset(new ModList(loaderModsDir()));
} }
loader_mod_list->update(); m_loader_mod_list->update();
return loader_mod_list; return m_loader_mod_list;
} }
std::shared_ptr<ModList> OneSixInstance::coreModList() std::shared_ptr<ModList> OneSixInstance::coreModList() const
{ {
if (!core_mod_list) if (!m_core_mod_list)
{ {
core_mod_list.reset(new ModList(coreModsDir())); m_core_mod_list.reset(new ModList(coreModsDir()));
} }
core_mod_list->update(); m_core_mod_list->update();
return core_mod_list; return m_core_mod_list;
} }
std::shared_ptr<ModList> OneSixInstance::resourcePackList() std::shared_ptr<ModList> OneSixInstance::resourcePackList() const
{ {
if (!resource_pack_list) if (!m_resource_pack_list)
{ {
resource_pack_list.reset(new ModList(resourcePacksDir())); m_resource_pack_list.reset(new ModList(resourcePacksDir()));
} }
resource_pack_list->update(); m_resource_pack_list->update();
return resource_pack_list; return m_resource_pack_list;
} }
std::shared_ptr<ModList> OneSixInstance::texturePackList() std::shared_ptr<ModList> OneSixInstance::texturePackList() const
{ {
if (!texture_pack_list) if (!m_texture_pack_list)
{ {
texture_pack_list.reset(new ModList(texturePacksDir())); m_texture_pack_list.reset(new ModList(texturePacksDir()));
} }
texture_pack_list->update(); m_texture_pack_list->update();
return texture_pack_list; return m_texture_pack_list;
} }
bool OneSixInstance::setIntendedVersionId(QString version) bool OneSixInstance::setIntendedVersionId(QString version)
{ {
settings().set("IntendedVersion", version); settings().set("IntendedVersion", version);
QFile::remove(PathCombine(instanceRoot(), "version.json")); if(getMinecraftProfile())
clearVersion(); {
clearProfile();
}
return true; return true;
} }
QList< Mod > OneSixInstance::getJarMods() const
{
QList<Mod> mods;
for (auto jarmod : m_version->jarMods)
{
QString filePath = jarmodsPath().absoluteFilePath(jarmod->name);
mods.push_back(Mod(QFileInfo(filePath)));
}
return mods;
}
QString OneSixInstance::intendedVersionId() const QString OneSixInstance::intendedVersionId() const
{ {
return settings().get("IntendedVersion").toString(); return settings().get("IntendedVersion").toString();
@ -368,35 +327,16 @@ bool OneSixInstance::shouldUpdate() const
return true; return true;
} }
bool OneSixInstance::versionIsCustom()
{
if (version)
{
return !version->isVanilla();
}
return false;
}
bool OneSixInstance::versionIsFTBPack()
{
if (version)
{
return version->hasFtbPack();
}
return false;
}
QString OneSixInstance::currentVersionId() const QString OneSixInstance::currentVersionId() const
{ {
return intendedVersionId(); return intendedVersionId();
} }
void OneSixInstance::reloadVersion() void OneSixInstance::reloadProfile()
{ {
try try
{ {
version->reload(externalPatches()); m_version->reload();
unsetFlag(VersionBrokenFlag); unsetFlag(VersionBrokenFlag);
emit versionReloaded(); emit versionReloaded();
} }
@ -405,7 +345,7 @@ void OneSixInstance::reloadVersion()
} }
catch (MMCError &error) catch (MMCError &error)
{ {
version->clear(); m_version->clear();
setFlag(VersionBrokenFlag); setFlag(VersionBrokenFlag);
// TODO: rethrow to show some error message(s)? // TODO: rethrow to show some error message(s)?
emit versionReloaded(); emit versionReloaded();
@ -413,24 +353,20 @@ void OneSixInstance::reloadVersion()
} }
} }
void OneSixInstance::clearVersion() void OneSixInstance::clearProfile()
{ {
version->clear(); m_version->clear();
emit versionReloaded(); emit versionReloaded();
} }
std::shared_ptr<InstanceVersion> OneSixInstance::getFullVersion() const std::shared_ptr<MinecraftProfile> OneSixInstance::getMinecraftProfile() const
{ {
return version; return m_version;
} }
QString OneSixInstance::getStatusbarDescription() QString OneSixInstance::getStatusbarDescription()
{ {
QStringList traits; QStringList traits;
if (versionIsCustom())
{
traits.append(tr("custom"));
}
if (flags() & VersionBrokenFlag) if (flags() & VersionBrokenFlag)
{ {
traits.append(tr("broken")); traits.append(tr("broken"));
@ -461,11 +397,6 @@ QDir OneSixInstance::versionsPath() const
return QDir::current().absoluteFilePath("versions"); return QDir::current().absoluteFilePath("versions");
} }
QStringList OneSixInstance::externalPatches() const
{
return QStringList();
}
bool OneSixInstance::providesVersionFile() const bool OneSixInstance::providesVersionFile() const
{ {
return false; return false;
@ -477,7 +408,7 @@ bool OneSixInstance::reload()
{ {
try try
{ {
reloadVersion(); reloadProfile();
return true; return true;
} }
catch (...) catch (...)
@ -526,10 +457,11 @@ QString OneSixInstance::libDir() const
QStringList OneSixInstance::extraArguments() const QStringList OneSixInstance::extraArguments() const
{ {
auto list = BaseInstance::extraArguments(); auto list = BaseInstance::extraArguments();
auto version = getFullVersion(); auto version = getMinecraftProfile();
if (!version) if (!version)
return list; return list;
if (version->hasJarMods()) auto jarMods = getJarMods();
if (!jarMods.isEmpty())
{ {
list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true", list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
"-Dfml.ignorePatchDiscrepancies=true"}); "-Dfml.ignorePatchDiscrepancies=true"});

View File

@ -15,13 +15,13 @@
#pragma once #pragma once
#include "BaseInstance.h" #include "logic/minecraft/MinecraftInstance.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/ModList.h" #include "logic/ModList.h"
#include "gui/pages/BasePageProvider.h" #include "gui/pages/BasePageProvider.h"
class OneSixInstance : public BaseInstance, public BasePageProvider class OneSixInstance : public MinecraftInstance, public BasePageProvider
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -29,17 +29,19 @@ public:
QObject *parent = 0); QObject *parent = 0);
virtual ~OneSixInstance(){}; virtual ~OneSixInstance(){};
virtual void init() override; virtual void init();
////// Edit Instance Dialog stuff ////// ////// Edit Instance Dialog stuff //////
virtual QList<BasePage *> getPages(); virtual QList<BasePage *> getPages();
virtual QString dialogTitle(); virtual QString dialogTitle();
////// Mod Lists ////// ////// Mod Lists //////
std::shared_ptr<ModList> loaderModList(); std::shared_ptr<ModList> loaderModList() const;
std::shared_ptr<ModList> coreModList(); std::shared_ptr<ModList> coreModList() const;
std::shared_ptr<ModList> resourcePackList() override; std::shared_ptr<ModList> resourcePackList() const override;
std::shared_ptr<ModList> texturePackList() override; std::shared_ptr<ModList> texturePackList() const override;
virtual QList<Mod> getJarMods() const override;
virtual void createProfile();
virtual QSet<QString> traits(); virtual QSet<QString> traits();
@ -53,7 +55,7 @@ public:
virtual QString instanceConfigFolder() const override; virtual QString instanceConfigFolder() const override;
virtual std::shared_ptr<Task> doUpdate() override; virtual std::shared_ptr<Task> doUpdate() override;
virtual bool prepareForLaunch(AuthSessionPtr account, QString & launchScript) override; virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;
@ -66,30 +68,23 @@ public:
virtual void setShouldUpdate(bool val) override; virtual void setShouldUpdate(bool val) override;
/** /**
* reload the full version json files. * reload the profile, including version json files.
* *
* throws various exceptions :3 * throws various exceptions :3
*/ */
void reloadVersion(); void reloadProfile();
/// clears all version information in preparation for an update /// clears all version information in preparation for an update
void clearVersion(); void clearProfile();
/// get the current full version info /// get the current full version info
std::shared_ptr<InstanceVersion> getFullVersion() const; std::shared_ptr<MinecraftProfile> getMinecraftProfile() const;
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
/// does this instance have an FTB pack patch inside?
bool versionIsFTBPack();
virtual QString getStatusbarDescription() override; virtual QString getStatusbarDescription() override;
virtual QDir jarmodsPath() const; virtual QDir jarmodsPath() const;
virtual QDir librariesPath() const; virtual QDir librariesPath() const;
virtual QDir versionsPath() const; virtual QDir versionsPath() const;
virtual QStringList externalPatches() const;
virtual bool providesVersionFile() const; virtual bool providesVersionFile() const;
bool reload() override; bool reload() override;
@ -103,15 +98,13 @@ signals:
private: private:
QStringList processMinecraftArgs(AuthSessionPtr account); QStringList processMinecraftArgs(AuthSessionPtr account);
QDir reconstructAssets(std::shared_ptr<InstanceVersion> version);
protected: protected:
std::shared_ptr<InstanceVersion> version; std::shared_ptr<MinecraftProfile> m_version;
std::shared_ptr<ModList> jar_mod_list; mutable std::shared_ptr<ModList> m_loader_mod_list;
std::shared_ptr<ModList> loader_mod_list; mutable std::shared_ptr<ModList> m_core_mod_list;
std::shared_ptr<ModList> core_mod_list; mutable std::shared_ptr<ModList> m_resource_pack_list;
std::shared_ptr<ModList> resource_pack_list; mutable std::shared_ptr<ModList> m_texture_pack_list;
std::shared_ptr<ModList> texture_pack_list;
}; };
Q_DECLARE_METATYPE(std::shared_ptr<OneSixInstance>) Q_DECLARE_METATYPE(std::shared_ptr<OneSixInstance>)

View File

@ -27,7 +27,7 @@
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "logic/minecraft/MinecraftVersionList.h" #include "logic/minecraft/MinecraftVersionList.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/OneSixLibrary.h" #include "logic/minecraft/OneSixLibrary.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/forge/ForgeMirrors.h" #include "logic/forge/ForgeMirrors.h"
@ -88,7 +88,7 @@ void OneSixUpdate::assetIndexStart()
{ {
setStatus(tr("Updating assets index...")); setStatus(tr("Updating assets index..."));
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
std::shared_ptr<InstanceVersion> version = inst->getFullVersion(); std::shared_ptr<MinecraftProfile> version = inst->getMinecraftProfile();
QString assetName = version->assets; QString assetName = version->assets;
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
QString localPath = assetName + ".json"; QString localPath = assetName + ".json";
@ -112,7 +112,7 @@ void OneSixUpdate::assetIndexFinished()
AssetsIndex index; AssetsIndex index;
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
std::shared_ptr<InstanceVersion> version = inst->getFullVersion(); std::shared_ptr<MinecraftProfile> version = inst->getMinecraftProfile();
QString assetName = version->assets; QString assetName = version->assets;
QString asset_fname = "assets/indexes/" + assetName + ".json"; QString asset_fname = "assets/indexes/" + assetName + ".json";
@ -177,7 +177,7 @@ void OneSixUpdate::jarlibStart()
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
try try
{ {
inst->reloadVersion(); inst->reloadProfile();
} }
catch (MMCError &e) catch (MMCError &e)
{ {
@ -191,7 +191,7 @@ void OneSixUpdate::jarlibStart()
} }
// Build a list of URLs that will need to be downloaded. // Build a list of URLs that will need to be downloaded.
std::shared_ptr<InstanceVersion> version = inst->getFullVersion(); std::shared_ptr<MinecraftProfile> version = inst->getMinecraftProfile();
// minecraft.jar for this version // minecraft.jar for this version
{ {
QString version_id = version->id; QString version_id = version->id;
@ -290,7 +290,7 @@ void OneSixUpdate::jarlibStart()
void OneSixUpdate::jarlibFinished() void OneSixUpdate::jarlibFinished()
{ {
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
std::shared_ptr<InstanceVersion> version = inst->getFullVersion(); std::shared_ptr<MinecraftProfile> version = inst->getMinecraftProfile();
// nuke obsolete stripped jar(s) if needed // nuke obsolete stripped jar(s) if needed
QString version_id = version->id; QString version_id = version->id;
@ -311,22 +311,16 @@ void OneSixUpdate::jarlibFinished()
} }
} }
// create stripped jar, if needed // create temporary modded jar, if needed
if (version->hasJarMods()) auto jarMods = inst->getJarMods();
if(jarMods.size())
{ {
auto sourceJarPath = m_inst->versionsPath().absoluteFilePath(version->id + "/" + version->id + ".jar"); auto sourceJarPath = m_inst->versionsPath().absoluteFilePath(version->id + "/" + version->id + ".jar");
QString localPath = version_id + "/" + version_id + ".jar"; QString localPath = version_id + "/" + version_id + ".jar";
auto metacache = MMC->metacache(); auto metacache = MMC->metacache();
auto entry = metacache->resolveEntry("versions", localPath); auto entry = metacache->resolveEntry("versions", localPath);
QString fullJarPath = entry->getFullPath(); QString fullJarPath = entry->getFullPath();
//FIXME: remove need to convert to different objects here if(!JarUtils::createModdedJar(sourceJarPath, finalJarPath, jarMods))
QList<Mod> mods;
for (auto jarmod : version->jarMods)
{
QString filePath = m_inst->jarmodsPath().absoluteFilePath(jarmod->name);
mods.push_back(Mod(QFileInfo(filePath)));
}
if(!JarUtils::createModdedJar(sourceJarPath, finalJarPath, mods))
{ {
emitFailed(tr("Failed to create the custom Minecraft jar file.")); emitFailed(tr("Failed to create the custom Minecraft jar file."));
return; return;
@ -354,7 +348,7 @@ void OneSixUpdate::fmllibsStart()
{ {
// Get the mod list // Get the mod list
OneSixInstance *inst = (OneSixInstance *)m_inst; OneSixInstance *inst = (OneSixInstance *)m_inst;
std::shared_ptr<InstanceVersion> fullversion = inst->getFullVersion(); std::shared_ptr<MinecraftProfile> fullversion = inst->getMinecraftProfile();
bool forge_present = false; bool forge_present = false;
QString version = inst->intendedVersionId(); QString version = inst->intendedVersionId();

View File

@ -21,7 +21,7 @@
#include "logic/net/NetJob.h" #include "logic/net/NetJob.h"
#include "logic/tasks/Task.h" #include "logic/tasks/Task.h"
#include "logic/VersionFilterData.h" #include "logic/minecraft/VersionFilterData.h"
#include <quazip.h> #include <quazip.h>
class MinecraftVersion; class MinecraftVersion;

View File

@ -5,8 +5,6 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QDirIterator> #include <QDirIterator>
#include <QCryptographicHash> #include <QCryptographicHash>
#include "gui/dialogs/CustomMessageBox.h"
#include <QDesktopServices>
AssetsMigrateTask::AssetsMigrateTask(int expected, QObject *parent) AssetsMigrateTask::AssetsMigrateTask(int expected, QObject *parent)
: Task(parent) : Task(parent)

View File

@ -22,6 +22,7 @@
#include "AssetsUtils.h" #include "AssetsUtils.h"
#include "MultiMC.h" #include "MultiMC.h"
#include <pathutils.h>
namespace AssetsUtils namespace AssetsUtils
{ {
@ -151,4 +152,65 @@ bool loadAssetsIndexJson(QString path, AssetsIndex *index)
return true; return true;
} }
QDir reconstructAssets(QString assetsId)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects"));
QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual"));
QString indexPath = PathCombine(indexDir.path(), assetsId + ".json");
QFile indexFile(indexPath);
QDir virtualRoot(PathCombine(virtualDir.path(), assetsId));
if (!indexFile.exists())
{
QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets";
return virtualRoot;
}
QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path()
<< objectDir.path() << virtualDir.path() << virtualRoot.path();
AssetsIndex index;
bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index);
if (loadAssetsIndex && index.isVirtual)
{
QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path();
for (QString map : index.objects.keys())
{
AssetObject asset_object = index.objects.value(map);
QString target_path = PathCombine(virtualRoot.path(), map);
QFile target(target_path);
QString tlk = asset_object.hash.left(2);
QString original_path =
PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash);
QFile original(original_path);
if (!original.exists())
continue;
if (!target.exists())
{
QFileInfo info(target_path);
QDir target_dir = info.dir();
// QLOG_DEBUG() << target_dir;
if (!target_dir.exists())
QDir("").mkpath(target_dir.path());
bool couldCopy = original.copy(target_path);
QLOG_DEBUG() << " Copying" << original_path << "to" << target_path
<< QString::number(couldCopy); // << original.errorString();
}
}
// TODO: Write last used time to virtualRoot/.lastused
}
return virtualRoot;
}
} }

View File

@ -34,4 +34,6 @@ namespace AssetsUtils
{ {
bool loadAssetsIndexJson(QString file, AssetsIndex* index); bool loadAssetsIndexJson(QString file, AssetsIndex* index);
int findLegacyAssets(); int findLegacyAssets();
/// Reconstruct a virtual assets folder for the given assets ID and return the folder
QDir reconstructAssets(QString assetsId);
} }

View File

@ -14,14 +14,13 @@
*/ */
#include "ForgeInstaller.h" #include "ForgeInstaller.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/OneSixLibrary.h" #include "logic/minecraft/OneSixLibrary.h"
#include "logic/net/HttpMetaCache.h" #include "logic/net/HttpMetaCache.h"
#include "logic/tasks/Task.h" #include "logic/tasks/Task.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/forge/ForgeVersionList.h" #include "logic/forge/ForgeVersionList.h"
#include "logic/VersionFilterData.h" #include "logic/minecraft/VersionFilterData.h"
#include "gui/dialogs/ProgressDialog.h"
#include <quazip.h> #include <quazip.h>
#include <quazipfile.h> #include <quazipfile.h>
@ -41,7 +40,7 @@ ForgeInstaller::ForgeInstaller() : BaseInstaller()
void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl) void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl)
{ {
std::shared_ptr<InstanceVersion> newVersion; std::shared_ptr<MinecraftProfile> newVersion;
m_universal_url = universalUrl; m_universal_url = universalUrl;
QuaZip zip(filename); QuaZip zip(filename);
@ -74,7 +73,7 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr
// read the forge version info // read the forge version info
{ {
newVersion = InstanceVersion::fromJson(versionInfoVal.toObject()); newVersion = MinecraftProfile::fromJson(versionInfoVal.toObject());
if (!newVersion) if (!newVersion)
return; return;
} }
@ -116,7 +115,7 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr
file.close(); file.close();
m_forge_json = newVersion; m_forge_json = newVersion;
realVersionId = m_forge_json->id = installObj.value("minecraft").toString(); m_forge_json->id = installObj.value("minecraft").toString();
} }
bool ForgeInstaller::add(OneSixInstance *to) bool ForgeInstaller::add(OneSixInstance *to)
@ -194,7 +193,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
bool found = false; bool found = false;
bool equals = false; bool equals = false;
// find an entry that matches this one // find an entry that matches this one
for (auto tolib : to->getFullVersion()->vanillaLibraries) for (auto tolib : to->getMinecraftProfile()->vanillaLibraries)
{ {
if (tolib->artifactId() != libName) if (tolib->artifactId() != libName)
continue; continue;
@ -237,7 +236,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
match = expression.match(args); match = expression.match(args);
} }
} }
if (!args.isEmpty() && args != to->getFullVersion()->vanillaMinecraftArguments) if (!args.isEmpty() && args != to->getMinecraftProfile()->vanillaMinecraftArguments)
{ {
obj.insert("minecraftArguments", args); obj.insert("minecraftArguments", args);
} }
@ -246,7 +245,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
obj.insert("+tweakers", QJsonArray::fromStringList(tweakers)); obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
} }
if (!m_forge_json->processArguments.isEmpty() && if (!m_forge_json->processArguments.isEmpty() &&
m_forge_json->processArguments != to->getFullVersion()->vanillaProcessArguments) m_forge_json->processArguments != to->getMinecraftProfile()->vanillaProcessArguments)
{ {
obj.insert("processArguments", m_forge_json->processArguments); obj.insert("processArguments", m_forge_json->processArguments);
} }
@ -308,7 +307,7 @@ bool ForgeInstaller::addLegacy(OneSixInstance *to)
traitsPlus.append(QString("legacyFML")); traitsPlus.append(QString("legacyFML"));
obj.insert("+traits", traitsPlus); obj.insert("+traits", traitsPlus);
} }
auto fullversion = to->getFullVersion(); auto fullversion = to->getMinecraftProfile();
fullversion->remove("net.minecraftforge"); fullversion->remove("net.minecraftforge");
QFile file(filename(to->instanceRoot())); QFile file(filename(to->instanceRoot()));
@ -409,7 +408,7 @@ protected:
{ {
try try
{ {
m_instance->reloadVersion(); m_instance->reloadProfile();
emitSucceeded(); emitSucceeded();
} }
catch (MMCError &e) catch (MMCError &e)

View File

@ -20,7 +20,7 @@
#include <QString> #include <QString>
#include <memory> #include <memory>
class InstanceVersion; class MinecraftProfile;
class ForgeInstallTask; class ForgeInstallTask;
struct ForgeVersion; struct ForgeVersion;
@ -40,12 +40,11 @@ protected:
private: private:
// the parsed version json, read from the installer // the parsed version json, read from the installer
std::shared_ptr<InstanceVersion> m_forge_json; std::shared_ptr<MinecraftProfile> m_forge_json;
// the actual forge version // the actual forge version
std::shared_ptr<ForgeVersion> m_forge_version; std::shared_ptr<ForgeVersion> m_forge_version;
QString internalPath; QString internalPath;
QString finalPath; QString finalPath;
QString realVersionId;
QString m_forgeVersionString; QString m_forgeVersionString;
QString m_universal_url; QString m_universal_url;
}; };

View File

@ -1,5 +1,5 @@
#include "ForgeVersion.h" #include "ForgeVersion.h"
#include "logic/VersionFilterData.h" #include "logic/minecraft/VersionFilterData.h"
#include <QObject> #include <QObject>
QString ForgeVersion::name() QString ForgeVersion::name()

32
logic/ftb/FTBVersion.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <logic/minecraft/MinecraftVersion.h>
class FTBVersion : public BaseVersion
{
public:
FTBVersion(MinecraftVersionPtr parent) : m_version(parent){};
public:
virtual QString descriptor() override
{
return m_version->descriptor();
}
virtual QString name() override
{
return m_version->name();
}
virtual QString typeString() const override
{
return m_version->typeString();
}
MinecraftVersionPtr getMinecraftVersion()
{
return m_version;
}
private:
MinecraftVersionPtr m_version;
};

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "LegacyInstance.h" #include "logic/LegacyInstance.h"
class LegacyFTBInstance : public LegacyInstance class LegacyFTBInstance : public LegacyInstance
{ {

View File

@ -1,11 +1,11 @@
#include "OneSixFTBInstance.h" #include "OneSixFTBInstance.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/OneSixLibrary.h" #include "logic/minecraft/OneSixLibrary.h"
#include "logic/minecraft/VersionBuilder.h" #include "logic/minecraft/VersionBuilder.h"
#include "tasks/SequentialTask.h" #include "logic/tasks/SequentialTask.h"
#include "forge/ForgeInstaller.h" #include "logic/forge/ForgeInstaller.h"
#include "forge/ForgeVersionList.h" #include "logic/forge/ForgeVersionList.h"
#include "MultiMC.h" #include "MultiMC.h"
#include "pathutils.h" #include "pathutils.h"
@ -109,11 +109,13 @@ QDir OneSixFTBInstance::versionsPath() const
return QDir(MMC->settings()->get("FTBRoot").toString() + "/versions"); return QDir(MMC->settings()->get("FTBRoot").toString() + "/versions");
} }
/*
QStringList OneSixFTBInstance::externalPatches() const QStringList OneSixFTBInstance::externalPatches() const
{ {
return QStringList() << versionsPath().absoluteFilePath(intendedVersionId() + "/" + intendedVersionId() + ".json") return QStringList() << versionsPath().absoluteFilePath(intendedVersionId() + "/" + intendedVersionId() + ".json")
<< minecraftRoot() + "/pack.json"; << minecraftRoot() + "/pack.json";
} }
*/
bool OneSixFTBInstance::providesVersionFile() const bool OneSixFTBInstance::providesVersionFile() const
{ {

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "OneSixInstance.h" #include "logic/OneSixInstance.h"
class OneSixLibrary; class OneSixLibrary;
@ -22,7 +22,6 @@ public:
QDir librariesPath() const override; QDir librariesPath() const override;
QDir versionsPath() const override; QDir versionsPath() const override;
QStringList externalPatches() const override;
bool providesVersionFile() const override; bool providesVersionFile() const override;
private: private:

View File

@ -20,7 +20,7 @@
#include "logger/QsLog.h" #include "logger/QsLog.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/OneSixLibrary.h" #include "logic/minecraft/OneSixLibrary.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "MultiMC.h" #include "MultiMC.h"
@ -116,7 +116,7 @@ protected:
{ {
try try
{ {
m_instance->reloadVersion(); m_instance->reloadProfile();
emitSucceeded(); emitSucceeded();
} }
catch (MMCError &e) catch (MMCError &e)

View File

@ -11,30 +11,6 @@ JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
"contains a jarmod that doesn't have a 'name' field"); "contains a jarmod that doesn't have a 'name' field");
} }
out->name = libObj.value("name").toString(); out->name = libObj.value("name").toString();
auto readString = [libObj, filename](const QString & key, QString & variable)
{
if (libObj.contains(key))
{
QJsonValue val = libObj.value(key);
if (!val.isString())
{
QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
}
else
{
variable = val.toString();
}
}
};
readString("url", out->baseurl);
readString("MMC-hint", out->hint);
readString("MMC-absoluteUrl", out->absoluteUrl);
if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty())
{
out->absoluteUrl = out->baseurl + out->name;
}
return out; return out;
} }
@ -42,15 +18,5 @@ QJsonObject Jarmod::toJson()
{ {
QJsonObject out; QJsonObject out;
writeString(out, "name", name); writeString(out, "name", name);
writeString(out, "url", baseurl);
writeString(out, "MMC-absoluteUrl", absoluteUrl);
writeString(out, "MMC-hint", hint);
return out; return out;
} }
QString Jarmod::url()
{
if(!absoluteUrl.isEmpty())
return absoluteUrl;
else return baseurl + name;
}

View File

@ -9,10 +9,6 @@ class Jarmod
public: /* methods */ public: /* methods */
static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename); static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename);
QJsonObject toJson(); QJsonObject toJson();
QString url();
public: /* data */ public: /* data */
QString name; QString name;
QString baseurl;
QString hint;
QString absoluteUrl;
}; };

View File

@ -0,0 +1,58 @@
#include "MinecraftInstance.h"
#include "MultiMC.h"
#include "logic/settings/SettingsObject.h"
#include <pathutils.h>
#include "logic/minecraft/MinecraftVersionList.h"
MinecraftInstance::MinecraftInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: BaseInstance(rootDir, settings, parent)
{
auto globalSettings = MMC->settings();
// Java Settings
m_settings->registerSetting("OverrideJava", false);
m_settings->registerSetting("OverrideJavaLocation", false);
m_settings->registerSetting("OverrideJavaArgs", false);
m_settings->registerOverride(globalSettings->getSetting("JavaPath"));
m_settings->registerOverride(globalSettings->getSetting("JvmArgs"));
// Custom Commands
m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"));
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"));
// Window Size
m_settings->registerSetting("OverrideWindow", false);
m_settings->registerOverride(globalSettings->getSetting("LaunchMaximized"));
m_settings->registerOverride(globalSettings->getSetting("MinecraftWinWidth"));
m_settings->registerOverride(globalSettings->getSetting("MinecraftWinHeight"));
// Memory
m_settings->registerSetting("OverrideMemory", false);
m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"));
m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"));
m_settings->registerOverride(globalSettings->getSetting("PermGen"));
// Console
m_settings->registerSetting("OverrideConsole", false);
m_settings->registerOverride(globalSettings->getSetting("ShowConsole"));
m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"));
m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"));
}
QString MinecraftInstance::minecraftRoot() const
{
QFileInfo mcDir(PathCombine(instanceRoot(), "minecraft"));
QFileInfo dotMCDir(PathCombine(instanceRoot(), ".minecraft"));
if (dotMCDir.exists() && !mcDir.exists())
return dotMCDir.filePath();
else
return mcDir.filePath();
}
std::shared_ptr< BaseVersionList > MinecraftInstance::versionList() const
{
return std::dynamic_pointer_cast<BaseVersionList>(MMC->minecraftlist());
}

View File

@ -0,0 +1,30 @@
#pragma once
#include "logic/BaseInstance.h"
class MinecraftInstance: public BaseInstance
{
public:
MinecraftInstance(const QString& rootDir, SettingsObject* settings, QObject* parent = 0);
virtual ~MinecraftInstance() {};
/// Path to the instance's minecraft directory.
QString minecraftRoot() const;
////// Mod Lists //////
virtual std::shared_ptr<ModList> resourcePackList() const
{
return nullptr;
}
virtual std::shared_ptr<ModList> texturePackList() const
{
return nullptr;
}
/// get all jar mods applicable to this instance's jar
virtual QList<Mod> getJarMods() const
{
return QList<Mod>();
}
virtual std::shared_ptr< BaseVersionList > versionList() const;
};
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;

View File

@ -0,0 +1,216 @@
/* Copyright 2013-2014 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.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
*
* 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 "MultiMC.h"
#include "BuildConfig.h"
#include "logic/minecraft/MinecraftProcess.h"
#include "logic/BaseInstance.h"
#include <QDataStream>
#include <QFile>
#include <QDir>
#include <QProcessEnvironment>
#include <QRegularExpression>
#include <QStandardPaths>
#include "osutils.h"
#include "pathutils.h"
#include "cmdutils.h"
#define IBUS "@im=ibus"
// constructor
MinecraftProcess::MinecraftProcess(MinecraftInstancePtr inst) : BaseProcess(inst)
{
}
MinecraftProcess* MinecraftProcess::create(MinecraftInstancePtr inst)
{
auto proc = new MinecraftProcess(inst);
proc->init();
return proc;
}
QString MinecraftProcess::censorPrivateInfo(QString in)
{
if (!m_session)
return in;
if (m_session->session != "-")
in.replace(m_session->session, "<SESSION ID>");
in.replace(m_session->access_token, "<ACCESS TOKEN>");
in.replace(m_session->client_token, "<CLIENT TOKEN>");
in.replace(m_session->uuid, "<PROFILE ID>");
in.replace(m_session->player_name, "<PROFILE NAME>");
auto i = m_session->u.properties.begin();
while (i != m_session->u.properties.end())
{
in.replace(i.value(), "<" + i.key().toUpper() + ">");
++i;
}
return in;
}
// console window
MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLevel::Enum level)
{
QRegularExpression re("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]");
auto match = re.match(line);
if(match.hasMatch())
{
// New style logs from log4j
QString timestamp = match.captured("timestamp");
QString levelStr = match.captured("level");
if(levelStr == "INFO")
level = MessageLevel::Message;
if(levelStr == "WARN")
level = MessageLevel::Warning;
if(levelStr == "ERROR")
level = MessageLevel::Error;
if(levelStr == "FATAL")
level = MessageLevel::Fatal;
if(levelStr == "TRACE" || levelStr == "DEBUG")
level = MessageLevel::Debug;
}
else
{
// Old style forge logs
if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") ||
line.contains("[FINER]") || line.contains("[FINEST]"))
level = MessageLevel::Message;
if (line.contains("[SEVERE]") || line.contains("[STDERR]"))
level = MessageLevel::Error;
if (line.contains("[WARNING]"))
level = MessageLevel::Warning;
if (line.contains("[DEBUG]"))
level = MessageLevel::Debug;
}
if (line.contains("overwriting existing"))
return MessageLevel::Fatal;
if (line.contains("Exception in thread") || line.contains(QRegularExpression("\\s+at ")))
return MessageLevel::Error;
return level;
}
QMap<QString, QString> MinecraftProcess::getVariables() const
{
auto mcInstance = std::dynamic_pointer_cast<MinecraftInstance>(m_instance);
QMap<QString, QString> out;
out.insert("INST_NAME", mcInstance->name());
out.insert("INST_ID", mcInstance->id());
out.insert("INST_DIR", QDir(mcInstance->instanceRoot()).absolutePath());
out.insert("INST_MC_DIR", QDir(mcInstance->minecraftRoot()).absolutePath());
out.insert("INST_JAVA", mcInstance->settings().get("JavaPath").toString());
out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
return out;
}
QStringList MinecraftProcess::javaArguments() const
{
QStringList args;
// custom args go first. we want to override them if we have our own here.
args.append(m_instance->extraArguments());
// OSX dock icon and name
#ifdef OSX
args << "-Xdock:icon=icon.png";
args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
#endif
// HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
#ifdef Q_OS_WIN32
args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
"minecraft.exe.heapdump");
#endif
args << QString("-Xms%1m").arg(m_instance->settings().get("MinMemAlloc").toInt());
args << QString("-Xmx%1m").arg(m_instance->settings().get("MaxMemAlloc").toInt());
auto permgen = m_instance->settings().get("PermGen").toInt();
if (permgen != 64)
{
args << QString("-XX:PermSize=%1m").arg(permgen);
}
args << "-Duser.language=en";
if (!m_nativeFolder.isEmpty())
args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
return args;
}
void MinecraftProcess::arm()
{
emit log("MultiMC version: " + BuildConfig.printableVersionString() + "\n\n");
emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
if (!preLaunch())
{
emit ended(m_instance, 1, QProcess::CrashExit);
return;
}
m_instance->setLastLaunch();
QStringList args = javaArguments();
QString JavaPath = m_instance->settings().get("JavaPath").toString();
emit log("Java path is:\n" + JavaPath + "\n\n");
QString allArgs = args.join(", ");
emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n");
auto realJavaPath = QStandardPaths::findExecutable(JavaPath);
if (realJavaPath.isEmpty())
{
emit log(tr("The java binary \"%1\" couldn't be found. You may have to set up java "
"if Minecraft fails to launch.").arg(JavaPath),
MessageLevel::Warning);
}
// instantiate the launcher part
start(JavaPath, args);
if (!waitForStarted())
{
//: Error message displayed if instace can't start
emit log(tr("Could not launch minecraft!"), MessageLevel::Error);
m_instance->cleanupAfterRun();
emit launch_failed(m_instance);
// not running, failed
m_instance->setRunning(false);
return;
}
// send the launch script to the launcher part
QByteArray bytes = launchScript.toUtf8();
writeData(bytes.constData(), bytes.length());
}
void MinecraftProcess::launch()
{
QString launchString("launch\n");
QByteArray bytes = launchString.toUtf8();
writeData(bytes.constData(), bytes.length());
}
void MinecraftProcess::abort()
{
QString launchString("abort\n");
QByteArray bytes = launchString.toUtf8();
writeData(bytes.constData(), bytes.length());
}

View File

@ -0,0 +1,77 @@
/* Copyright 2013-2014 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.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
*
* 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 <QString>
#include "logic/minecraft/MinecraftInstance.h"
#include "logic/BaseProcess.h"
/**
* The MinecraftProcess class
*/
class MinecraftProcess : public BaseProcess
{
Q_OBJECT
protected:
MinecraftProcess(MinecraftInstancePtr inst);
public:
static MinecraftProcess *create(MinecraftInstancePtr inst);
virtual ~MinecraftProcess(){};
/**
* @brief start the launcher part with the provided launch script
*/
void arm() override;
/**
* @brief launch the armed instance!
*/
void launch() override;
/**
* @brief abort launch!
*/
void abort() override;
void setLaunchScript(QString script)
{
launchScript = script;
}
void setNativeFolder(QString natives)
{
m_nativeFolder = natives;
}
inline void setLogin(AuthSessionPtr session)
{
m_session = session;
}
protected:
AuthSessionPtr m_session;
QString launchScript;
QString m_nativeFolder;
virtual QMap<QString, QString> getVariables() const override;
QStringList javaArguments() const;
virtual QString censorPrivateInfo(QString in) override;
virtual MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel) override;
};

View File

@ -15,31 +15,50 @@
#include <QFile> #include <QFile>
#include <QDir> #include <QDir>
#include <QUuid>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <pathutils.h> #include <pathutils.h>
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/VersionBuilder.h" #include "logic/minecraft/VersionBuilder.h"
#include "ProfileUtils.h"
#include "NullProfileStrategy.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
InstanceVersion::InstanceVersion(OneSixInstance *instance, QObject *parent) MinecraftProfile::MinecraftProfile(ProfileStrategy *strategy)
: QAbstractListModel(parent), m_instance(instance) : QAbstractListModel()
{ {
setStrategy(strategy);
clear(); clear();
} }
void InstanceVersion::reload(const QStringList &external) void MinecraftProfile::setStrategy(ProfileStrategy* strategy)
{
Q_ASSERT(strategy != nullptr);
if(m_strategy != nullptr)
{
delete m_strategy;
m_strategy = nullptr;
}
m_strategy = strategy;
m_strategy->profile = this;
}
ProfileStrategy* MinecraftProfile::strategy()
{
return m_strategy;
}
void MinecraftProfile::reload()
{ {
m_externalPatches = external;
beginResetModel(); beginResetModel();
VersionBuilder::build(this, m_instance, m_externalPatches); m_strategy->load();
reapply(true); reapply();
endResetModel(); endResetModel();
} }
void InstanceVersion::clear() void MinecraftProfile::clear()
{ {
id.clear(); id.clear();
m_updateTimeString.clear(); m_updateTimeString.clear();
@ -59,44 +78,45 @@ void InstanceVersion::clear()
traits.clear(); traits.clear();
} }
bool InstanceVersion::canRemove(const int index) const void MinecraftProfile::clearPatches()
{
beginResetModel();
VersionPatches.clear();
endResetModel();
}
void MinecraftProfile::appendPatch(ProfilePatchPtr patch)
{
int index = VersionPatches.size();
beginInsertRows(QModelIndex(), index, index);
VersionPatches.append(patch);
endInsertRows();
}
bool MinecraftProfile::canRemove(const int index) const
{ {
return VersionPatches.at(index)->isMoveable(); return VersionPatches.at(index)->isMoveable();
} }
bool InstanceVersion::preremove(VersionPatchPtr patch) bool MinecraftProfile::remove(const int index)
{
bool ok = true;
for(auto & jarmod: patch->getJarMods())
{
QString fullpath =PathCombine(m_instance->jarModsDir(), jarmod->name);
QFileInfo finfo (fullpath);
if(finfo.exists())
ok &= QFile::remove(fullpath);
}
return ok;
}
bool InstanceVersion::remove(const int index)
{ {
if (!canRemove(index)) if (!canRemove(index))
return false; return false;
if(!preremove(VersionPatches[index]))
if(!m_strategy->removePatch(VersionPatches.at(index)))
{ {
return false; return false;
} }
auto toDelete = VersionPatches.at(index)->getPatchFilename();
if(!QFile::remove(toDelete))
return false;
beginRemoveRows(QModelIndex(), index, index); beginRemoveRows(QModelIndex(), index, index);
VersionPatches.removeAt(index); VersionPatches.removeAt(index);
endRemoveRows(); endRemoveRows();
reapply(true); reapply();
saveCurrentOrder(); saveCurrentOrder();
return true; return true;
} }
bool InstanceVersion::remove(const QString id) bool MinecraftProfile::remove(const QString id)
{ {
int i = 0; int i = 0;
for (auto patch : VersionPatches) for (auto patch : VersionPatches)
@ -110,7 +130,7 @@ bool InstanceVersion::remove(const QString id)
return false; return false;
} }
QString InstanceVersion::versionFileId(const int index) const QString MinecraftProfile::versionFileId(const int index) const
{ {
if (index < 0 || index >= VersionPatches.size()) if (index < 0 || index >= VersionPatches.size())
{ {
@ -119,7 +139,7 @@ QString InstanceVersion::versionFileId(const int index) const
return VersionPatches.at(index)->getPatchID(); return VersionPatches.at(index)->getPatchID();
} }
VersionPatchPtr InstanceVersion::versionPatch(const QString &id) ProfilePatchPtr MinecraftProfile::versionPatch(const QString &id)
{ {
for (auto file : VersionPatches) for (auto file : VersionPatches)
{ {
@ -131,67 +151,27 @@ VersionPatchPtr InstanceVersion::versionPatch(const QString &id)
return 0; return 0;
} }
VersionPatchPtr InstanceVersion::versionPatch(int index) ProfilePatchPtr MinecraftProfile::versionPatch(int index)
{ {
if(index < 0 || index >= VersionPatches.size()) if(index < 0 || index >= VersionPatches.size())
return 0; return 0;
return VersionPatches[index]; return VersionPatches[index];
} }
bool MinecraftProfile::isVanilla()
bool InstanceVersion::hasJarMods()
{ {
return !jarMods.isEmpty();
}
bool InstanceVersion::hasFtbPack()
{
return versionPatch("org.multimc.ftb.pack.json") != nullptr;
}
bool InstanceVersion::removeFtbPack()
{
return remove("org.multimc.ftb.pack.json");
}
bool InstanceVersion::isVanilla()
{
QDir patches(PathCombine(m_instance->instanceRoot(), "patches/"));
for(auto patchptr: VersionPatches) for(auto patchptr: VersionPatches)
{ {
if(patchptr->isCustom()) if(patchptr->isCustom())
return false; return false;
} }
if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
return false;
if(QFile::exists(PathCombine(m_instance->instanceRoot(), "version.json")))
return false;
return true; return true;
} }
bool InstanceVersion::revertToVanilla() bool MinecraftProfile::revertToVanilla()
{ {
/*
beginResetModel(); beginResetModel();
// remove custom.json, if present
QString customPath = PathCombine(m_instance->instanceRoot(), "custom.json");
if(QFile::exists(customPath))
{
if(!QFile::remove(customPath))
{
endResetModel();
return false;
}
}
// remove version.json, if present
QString versionPath = PathCombine(m_instance->instanceRoot(), "version.json");
if(QFile::exists(versionPath))
{
if(!QFile::remove(versionPath))
{
endResetModel();
return false;
}
}
// remove patches, if present // remove patches, if present
auto it = VersionPatches.begin(); auto it = VersionPatches.begin();
while (it != VersionPatches.end()) while (it != VersionPatches.end())
@ -215,49 +195,15 @@ bool InstanceVersion::revertToVanilla()
else else
it++; it++;
} }
reapply(true); reapply();
endResetModel(); endResetModel();
saveCurrentOrder(); saveCurrentOrder();
return true; return true;
} */
bool InstanceVersion::hasDeprecatedVersionFiles()
{
if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
return true;
if(QFile::exists(PathCombine(m_instance->instanceRoot(), "version.json")))
return true;
return false; return false;
} }
bool InstanceVersion::removeDeprecatedVersionFiles() QList<std::shared_ptr<OneSixLibrary> > MinecraftProfile::getActiveNormalLibs()
{
beginResetModel();
// remove custom.json, if present
QString customPath = PathCombine(m_instance->instanceRoot(), "custom.json");
if(QFile::exists(customPath))
{
if(!QFile::remove(customPath))
{
endResetModel();
return false;
}
}
// remove version.json, if present
QString versionPath = PathCombine(m_instance->instanceRoot(), "version.json");
if(QFile::exists(versionPath))
{
if(!QFile::remove(versionPath))
{
endResetModel();
return false;
}
}
endResetModel();
return true;
}
QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::getActiveNormalLibs()
{ {
QList<std::shared_ptr<OneSixLibrary> > output; QList<std::shared_ptr<OneSixLibrary> > output;
for (auto lib : libraries) for (auto lib : libraries)
@ -277,7 +223,8 @@ QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::getActiveNormalLibs()
} }
return output; return output;
} }
QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::getActiveNativeLibs()
QList<std::shared_ptr<OneSixLibrary> > MinecraftProfile::getActiveNativeLibs()
{ {
QList<std::shared_ptr<OneSixLibrary> > output; QList<std::shared_ptr<OneSixLibrary> > output;
for (auto lib : libraries) for (auto lib : libraries)
@ -290,9 +237,9 @@ QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::getActiveNativeLibs()
return output; return output;
} }
std::shared_ptr<InstanceVersion> InstanceVersion::fromJson(const QJsonObject &obj) std::shared_ptr<MinecraftProfile> MinecraftProfile::fromJson(const QJsonObject &obj)
{ {
std::shared_ptr<InstanceVersion> version(new InstanceVersion(0)); std::shared_ptr<MinecraftProfile> version(new MinecraftProfile(new NullProfileStrategy()));
try try
{ {
VersionBuilder::readJsonAndApplyToVersion(version.get(), obj); VersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
@ -304,7 +251,7 @@ std::shared_ptr<InstanceVersion> InstanceVersion::fromJson(const QJsonObject &ob
return version; return version;
} }
QVariant InstanceVersion::data(const QModelIndex &index, int role) const QVariant MinecraftProfile::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
@ -329,7 +276,7 @@ QVariant InstanceVersion::data(const QModelIndex &index, int role) const
} }
return QVariant(); return QVariant();
} }
QVariant InstanceVersion::headerData(int section, Qt::Orientation orientation, int role) const QVariant MinecraftProfile::headerData(int section, Qt::Orientation orientation, int role) const
{ {
if (orientation == Qt::Horizontal) if (orientation == Qt::Horizontal)
{ {
@ -348,36 +295,36 @@ QVariant InstanceVersion::headerData(int section, Qt::Orientation orientation, i
} }
return QVariant(); return QVariant();
} }
Qt::ItemFlags InstanceVersion::flags(const QModelIndex &index) const Qt::ItemFlags MinecraftProfile::flags(const QModelIndex &index) const
{ {
if (!index.isValid()) if (!index.isValid())
return Qt::NoItemFlags; return Qt::NoItemFlags;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
} }
int InstanceVersion::rowCount(const QModelIndex &parent) const int MinecraftProfile::rowCount(const QModelIndex &parent) const
{ {
return VersionPatches.size(); return VersionPatches.size();
} }
int InstanceVersion::columnCount(const QModelIndex &parent) const int MinecraftProfile::columnCount(const QModelIndex &parent) const
{ {
return 2; return 2;
} }
void InstanceVersion::saveCurrentOrder() const void MinecraftProfile::saveCurrentOrder() const
{ {
PatchOrder order; ProfileUtils::PatchOrder order;
for(auto item: VersionPatches) for(auto item: VersionPatches)
{ {
if(!item->isMoveable()) if(!item->isMoveable())
continue; continue;
order.append(item->getPatchID()); order.append(item->getPatchID());
} }
VersionBuilder::writeOverrideOrders(m_instance, order); m_strategy->saveOrder(order);
} }
void InstanceVersion::move(const int index, const MoveDirection direction) void MinecraftProfile::move(const int index, const MoveDirection direction)
{ {
int theirIndex; int theirIndex;
if (direction == MoveUp) if (direction == MoveUp)
@ -412,13 +359,13 @@ void InstanceVersion::move(const int index, const MoveDirection direction)
saveCurrentOrder(); saveCurrentOrder();
reapply(); reapply();
} }
void InstanceVersion::resetOrder() void MinecraftProfile::resetOrder()
{ {
QDir(m_instance->instanceRoot()).remove("order.json"); m_strategy->resetOrder();
reload(m_externalPatches); reload();
} }
void InstanceVersion::reapply(const bool alreadyReseting) void MinecraftProfile::reapply()
{ {
clear(); clear();
for(auto file: VersionPatches) for(auto file: VersionPatches)
@ -428,7 +375,7 @@ void InstanceVersion::reapply(const bool alreadyReseting)
finalize(); finalize();
} }
void InstanceVersion::finalize() void MinecraftProfile::finalize()
{ {
// HACK: deny april fools. my head hurts enough already. // HACK: deny april fools. my head hurts enough already.
QDate now = QDate::currentDate(); QDate now = QDate::currentDate();
@ -465,78 +412,15 @@ void InstanceVersion::finalize()
finalizeArguments(minecraftArguments, processArguments); finalizeArguments(minecraftArguments, processArguments);
} }
void InstanceVersion::installJarMods(QStringList selectedFiles) void MinecraftProfile::installJarMods(QStringList selectedFiles)
{ {
for(auto filename: selectedFiles) m_strategy->installJarMods(selectedFiles);
{
installJarModByFilename(filename);
}
} }
void InstanceVersion::installJarModByFilename(QString filepath) /*
{ * TODO: get rid of this. Get rid of all order numbers.
QString patchDir = PathCombine(m_instance->instanceRoot(), "patches"); */
if(!ensureFolderPathExists(patchDir)) int MinecraftProfile::getFreeOrderNumber()
{
// THROW...
return;
}
if (!ensureFolderPathExists(m_instance->jarModsDir()))
{
// THROW...
return;
}
QFileInfo sourceInfo(filepath);
auto uuid = QUuid::createUuid();
QString id = uuid.toString().remove('{').remove('}');
QString target_filename = id + ".jar";
QString target_id = "org.multimc.jarmod." + id;
QString target_name = sourceInfo.completeBaseName() + " (jar mod)";
QString finalPath = PathCombine(m_instance->jarModsDir(), target_filename);
QFileInfo targetInfo(finalPath);
if(targetInfo.exists())
{
// THROW
return;
}
if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
{
// THROW
return;
}
auto f = std::make_shared<VersionFile>();
auto jarMod = std::make_shared<Jarmod>();
jarMod->name = target_filename;
f->jarMods.append(jarMod);
f->name = target_name;
f->fileId = target_id;
f->order = getFreeOrderNumber();
QString patchFileName = PathCombine(patchDir, target_id + ".json");
f->filename = patchFileName;
QFile file(patchFileName);
if (!file.open(QFile::WriteOnly))
{
QLOG_ERROR() << "Error opening" << file.fileName()
<< "for reading:" << file.errorString();
return;
// THROW
}
file.write(f->toJson(true).toJson());
file.close();
int index = VersionPatches.size();
beginInsertRows(QModelIndex(), index, index);
VersionPatches.append(f);
endInsertRows();
saveCurrentOrder();
}
int InstanceVersion::getFreeOrderNumber()
{ {
int largest = 100; int largest = 100;
// yes, I do realize this is dumb. The order thing itself is dumb. and to be removed next. // yes, I do realize this is dumb. The order thing itself is dumb. and to be removed next.

View File

@ -25,13 +25,22 @@
#include "VersionFile.h" #include "VersionFile.h"
#include "JarMod.h" #include "JarMod.h"
class ProfileStrategy;
class OneSixInstance; class OneSixInstance;
class InstanceVersion : public QAbstractListModel class MinecraftProfile : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
friend class ProfileStrategy;
public: public:
explicit InstanceVersion(OneSixInstance *instance, QObject *parent = 0); explicit MinecraftProfile(ProfileStrategy *strategy);
/// construct a MinecraftProfile from a single file
static std::shared_ptr<MinecraftProfile> fromJson(const QJsonObject &obj);
void setStrategy(ProfileStrategy * strategy);
ProfileStrategy *strategy();
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
@ -39,57 +48,72 @@ public:
virtual int columnCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const;
void reload(const QStringList &external = QStringList()); /// is this version unchanged by the user?
void clear();
bool canRemove(const int index) const;
QString versionFileId(const int index) const;
// is this version unmodded vanilla minecraft?
bool isVanilla(); bool isVanilla();
// remove any customizations on top of vanilla
/// remove any customizations on top of whatever 'vanilla' means
bool revertToVanilla(); bool revertToVanilla();
// does this version consist of obsolete files? /// install more jar mods
bool hasDeprecatedVersionFiles();
// remove obsolete files
bool removeDeprecatedVersionFiles();
// does this version have an FTB pack patch file?
bool hasFtbPack();
// remove FTB pack
bool removeFtbPack();
// does this version have any jar mods?
bool hasJarMods();
void installJarMods(QStringList selectedFiles); void installJarMods(QStringList selectedFiles);
void installJarModByFilename(QString filepath);
/// DEPRECATED, remove ASAP
int getFreeOrderNumber();
/// Can patch file # be removed?
bool canRemove(const int index) const;
enum MoveDirection { MoveUp, MoveDown }; enum MoveDirection { MoveUp, MoveDown };
/// move patch file # up or down the list
void move(const int index, const MoveDirection direction); void move(const int index, const MoveDirection direction);
void resetOrder();
// clears and reapplies all version files /// remove patch file # - including files/records
void reapply(const bool alreadyReseting = false);
void finalize();
public
slots:
bool remove(const int index); bool remove(const int index);
/// remove patch file by id - including files/records
bool remove(const QString id); bool remove(const QString id);
void resetOrder();
/// reload all profile patches from storage, clear the profile and apply the patches
void reload();
/// clear the profile
void clear();
/// apply the patches
void reapply();
/// do a finalization step (should always be done after applying all patches to profile)
void finalize();
public: public:
/// get all java libraries that belong to the classpath
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs(); QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
/// get all native libraries that need to be available to the process
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs(); QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
static std::shared_ptr<InstanceVersion> fromJson(const QJsonObject &obj); /// get file ID of the patch file at #
QString versionFileId(const int index) const;
private: /// get the profile patch by id
bool preremove(VersionPatchPtr patch); ProfilePatchPtr versionPatch(const QString &id);
// data members /// get the profile patch by index
public: ProfilePatchPtr versionPatch(int index);
/// save the current patch order
void saveCurrentOrder() const;
public: /* only use in ProfileStrategy */
/// Remove all the patches
void clearPatches();
/// Add the patch object to the internal list of patches
void appendPatch(ProfilePatchPtr patch);
public: /* data */
/// the ID - determines which jar to use! ACTUALLY IMPORTANT! /// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id; QString id;
@ -171,14 +195,7 @@ public:
} }
*/ */
// QList<Rule> rules; // QList<Rule> rules;
QList<VersionPatchPtr> VersionPatches;
VersionPatchPtr versionPatch(const QString &id);
VersionPatchPtr versionPatch(int index);
private: private:
QStringList m_externalPatches; QList<ProfilePatchPtr> VersionPatches;
OneSixInstance *m_instance; ProfileStrategy *m_strategy = nullptr;
void saveCurrentOrder() const;
int getFreeOrderNumber();
}; };

View File

@ -1,7 +1,8 @@
#include "MinecraftVersion.h" #include "MinecraftVersion.h"
#include "InstanceVersion.h" #include "MinecraftProfile.h"
#include "VersionBuildError.h" #include "VersionBuildError.h"
#include "VersionBuilder.h" #include "VersionBuilder.h"
#include "ProfileUtils.h"
#include "MultiMC.h" #include "MultiMC.h"
#include "logic/settings/SettingsObject.h" #include "logic/settings/SettingsObject.h"
@ -56,15 +57,20 @@ bool MinecraftVersion::isMinecraftVersion()
// 1. assume the local file is good. load, check. If it's good, apply. // 1. assume the local file is good. load, check. If it's good, apply.
// 2. if discrepancies are found, fall out and fail (impossible to apply incomplete version). // 2. if discrepancies are found, fall out and fail (impossible to apply incomplete version).
void MinecraftVersion::applyFileTo(InstanceVersion *version) void MinecraftVersion::applyFileTo(MinecraftProfile *version)
{
getVersionFile()->applyTo(version);
}
VersionFilePtr MinecraftVersion::getVersionFile()
{ {
QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_descriptor)); QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_descriptor));
auto versionObj = VersionBuilder::parseBinaryJsonFile(versionFile); return ProfileUtils::parseBinaryJsonFile(versionFile);
versionObj->applyTo(version);
} }
void MinecraftVersion::applyTo(InstanceVersion *version)
void MinecraftVersion::applyTo(MinecraftProfile *version)
{ {
// do we have this one cached? // do we have this one cached?
if (m_versionSource == Local) if (m_versionSource == Local)

View File

@ -20,15 +20,15 @@
#include <QDateTime> #include <QDateTime>
#include "logic/BaseVersion.h" #include "logic/BaseVersion.h"
#include "VersionPatch.h" #include "ProfilePatch.h"
#include "VersionFile.h" #include "VersionFile.h"
#include "VersionSource.h" #include "VersionSource.h"
class InstanceVersion; class MinecraftProfile;
class MinecraftVersion; class MinecraftVersion;
typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr; typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr;
class MinecraftVersion : public BaseVersion, public VersionPatch class MinecraftVersion : public BaseVersion, public ProfilePatch
{ {
public: /* methods */ public: /* methods */
bool usesLegacyLauncher(); bool usesLegacyLauncher();
@ -37,9 +37,9 @@ public: /* methods */
virtual QString typeString() const override; virtual QString typeString() const override;
virtual bool hasJarMods() override; virtual bool hasJarMods() override;
virtual bool isMinecraftVersion() override; virtual bool isMinecraftVersion() override;
virtual void applyTo(InstanceVersion *version) override; virtual void applyTo(MinecraftProfile *version) override;
virtual int getOrder(); virtual int getOrder() override;
virtual void setOrder(int order); virtual void setOrder(int order) override;
virtual QList<JarmodPtr> getJarMods() override; virtual QList<JarmodPtr> getJarMods() override;
virtual QString getPatchID() override; virtual QString getPatchID() override;
virtual QString getPatchVersion() override; virtual QString getPatchVersion() override;
@ -47,10 +47,12 @@ public: /* methods */
virtual QString getPatchFilename() override; virtual QString getPatchFilename() override;
bool needsUpdate(); bool needsUpdate();
bool hasUpdate(); bool hasUpdate();
virtual bool isCustom(); virtual bool isCustom() override;
VersionFilePtr getVersionFile();
private: /* methods */ private: /* methods */
void applyFileTo(InstanceVersion *version); void applyFileTo(MinecraftProfile *version);
public: /* data */ public: /* data */
/// The URL that this version will be downloaded from. maybe. /// The URL that this version will be downloaded from. maybe.

View File

@ -25,12 +25,53 @@
#include "logic/net/URLConstants.h" #include "logic/net/URLConstants.h"
#include "ParseUtils.h" #include "ParseUtils.h"
#include "ProfileUtils.h"
#include "VersionBuilder.h" #include "VersionBuilder.h"
#include <logic/VersionFilterData.h> #include "VersionFilterData.h"
#include <pathutils.h> #include <pathutils.h>
static const char * localVersionCache = "versions/versions.dat"; static const char * localVersionCache = "versions/versions.dat";
class MCVListLoadTask : public Task
{
Q_OBJECT
public:
explicit MCVListLoadTask(MinecraftVersionList *vlist);
virtual ~MCVListLoadTask() override{};
virtual void executeTask() override;
protected
slots:
void list_downloaded();
protected:
QNetworkReply *vlistReply;
MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable;
};
class MCVListVersionUpdateTask : public Task
{
Q_OBJECT
public:
explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, QString updatedVersion);
virtual ~MCVListVersionUpdateTask() override{};
virtual void executeTask() override;
protected
slots:
void json_downloaded();
protected:
NetJobPtr specificVersionDownloadJob;
QString versionToUpdate;
MinecraftVersionList *m_list;
};
class ListLoadError : public MMCError class ListLoadError : public MMCError
{ {
public: public:
@ -442,21 +483,9 @@ void MCVListVersionUpdateTask::json_downloaded()
emitFailed(tr("Couldn't process version file: %1").arg(e.cause())); emitFailed(tr("Couldn't process version file: %1").arg(e.cause()));
return; return;
} }
QList<RawLibraryPtr> filteredLibs;
QList<RawLibraryPtr> lwjglLibs;
for (auto lib : file->overwriteLibs) // Strip LWJGL from the version file. We use our own.
{ ProfileUtils::removeLwjglFromPatch(file);
if (g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix()))
{
lwjglLibs.append(lib);
}
else
{
filteredLibs.append(lib);
}
}
file->overwriteLibs = filteredLibs;
// TODO: recognize and add LWJGL versions here. // TODO: recognize and add LWJGL versions here.
@ -593,3 +622,5 @@ void MinecraftVersionList::finalizeUpdate(QString version)
saveCachedList(); saveCachedList();
} }
#include "MinecraftVersionList.moc"

View File

@ -26,7 +26,6 @@
class MCVListLoadTask; class MCVListLoadTask;
class MCVListVersionUpdateTask; class MCVListVersionUpdateTask;
class QNetworkReply;
class MinecraftVersionList : public BaseVersionList class MinecraftVersionList : public BaseVersionList
{ {
@ -67,42 +66,3 @@ protected
slots: slots:
virtual void updateListData(QList<BaseVersionPtr> versions); virtual void updateListData(QList<BaseVersionPtr> versions);
}; };
class MCVListLoadTask : public Task
{
Q_OBJECT
public:
explicit MCVListLoadTask(MinecraftVersionList *vlist);
virtual ~MCVListLoadTask() override{};
virtual void executeTask() override;
protected
slots:
void list_downloaded();
protected:
QNetworkReply *vlistReply;
MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable;
};
class MCVListVersionUpdateTask : public Task
{
Q_OBJECT
public:
explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, QString updatedVersion);
virtual ~MCVListVersionUpdateTask() override{};
virtual void executeTask() override;
protected
slots:
void json_downloaded();
protected:
NetJobPtr specificVersionDownloadJob;
QString versionToUpdate;
MinecraftVersionList *m_list;
};

View File

@ -0,0 +1,24 @@
#pragma once
#include "ProfileStrategy.h"
class NullProfileStrategy: public ProfileStrategy
{
virtual bool installJarMods(QStringList filepaths)
{
return false;
}
virtual void load() {};
virtual bool removePatch(ProfilePatchPtr jarMod)
{
return false;
}
virtual bool resetOrder()
{
return false;
}
virtual bool saveOrder(ProfileUtils::PatchOrder order)
{
return false;
}
};

View File

@ -18,10 +18,6 @@
#include "OneSixLibrary.h" #include "OneSixLibrary.h"
#include "OneSixRule.h" #include "OneSixRule.h"
#include "OpSys.h" #include "OpSys.h"
#include "logic/net/URLConstants.h"
#include <pathutils.h>
#include <JlCompress.h>
#include "logger/QsLog.h"
OneSixLibrary::OneSixLibrary(RawLibraryPtr base) OneSixLibrary::OneSixLibrary(RawLibraryPtr base)
{ {

View File

@ -0,0 +1,270 @@
#include "logic/minecraft/OneSixProfileStrategy.h"
#include "logic/minecraft/VersionBuildError.h"
#include "logic/OneSixInstance.h"
#include "logic/minecraft/MinecraftVersionList.h"
#include "MultiMC.h"
#include <pathutils.h>
#include <QDir>
#include <QUuid>
#include <QJsonDocument>
#include <QJsonArray>
OneSixProfileStrategy::OneSixProfileStrategy(OneSixInstance* instance)
{
m_instance = instance;
}
void OneSixProfileStrategy::upgradeDeprecatedFiles()
{
auto versionJsonPath = PathCombine(m_instance->instanceRoot(), "version.json");
auto customJsonPath = PathCombine(m_instance->instanceRoot(), "custom.json");
auto mcJson = PathCombine(m_instance->instanceRoot(), "patches" , "net.minecraft.json");
// convert old crap.
if(QFile::exists(customJsonPath))
{
if(!ensureFilePathExists(mcJson))
{
// WHAT DO???
}
if(!QFile::rename(customJsonPath, mcJson))
{
// WHAT DO???
}
if(QFile::exists(versionJsonPath))
{
if(!QFile::remove(versionJsonPath))
{
// WHAT DO???
}
}
}
else if(QFile::exists(versionJsonPath))
{
if(!ensureFilePathExists(mcJson))
{
// WHAT DO???
}
if(!QFile::rename(versionJsonPath, mcJson))
{
// WHAT DO???
}
}
}
void OneSixProfileStrategy::loadDefaultBuiltinPatches()
{
auto mcJson = PathCombine(m_instance->instanceRoot(), "patches" , "net.minecraft.json");
// load up the base minecraft patch
ProfilePatchPtr minecraftPatch;
if(QFile::exists(mcJson))
{
auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false);
file->fileId = "net.minecraft";
file->name = "Minecraft";
if(file->version.isEmpty())
{
file->version = m_instance->intendedVersionId();
}
minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
}
else
{
auto minecraftList = MMC->minecraftlist();
auto mcversion = minecraftList->findVersion(m_instance->intendedVersionId());
minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(mcversion);
}
if (!minecraftPatch)
{
throw VersionIncomplete("net.minecraft");
}
minecraftPatch->setOrder(-2);
profile->appendPatch(minecraftPatch);
// TODO: this is obviously fake.
QResource LWJGL(":/versions/LWJGL/2.9.1.json");
auto lwjgl = ProfileUtils::parseJsonFile(LWJGL.absoluteFilePath(), false, false);
auto lwjglPatch = std::dynamic_pointer_cast<ProfilePatch>(lwjgl);
if (!lwjglPatch)
{
throw VersionIncomplete("org.lwjgl");
}
lwjglPatch->setOrder(-1);
lwjgl->setVanilla(true);
profile->appendPatch(lwjglPatch);
}
void OneSixProfileStrategy::loadUserPatches()
{
// load all patches, put into map for ordering, apply in the right order
ProfileUtils::PatchOrder userOrder;
ProfileUtils::readOverrideOrders(PathCombine(m_instance->instanceRoot(), "order.json"), userOrder);
QDir patches(PathCombine(m_instance->instanceRoot(),"patches"));
// first, load things by sort order.
for (auto id : userOrder)
{
// ignore builtins
if (id == "net.minecraft")
continue;
if (id == "org.lwjgl")
continue;
// parse the file
QString filename = patches.absoluteFilePath(id + ".json");
QFileInfo finfo(filename);
if(!finfo.exists())
{
QLOG_INFO() << "Patch file " << filename << " was deleted by external means...";
continue;
}
QLOG_INFO() << "Reading" << filename << "by user order";
auto file = ProfileUtils::parseJsonFile(finfo, false);
// sanity check. prevent tampering with files.
if (file->fileId != id)
{
throw VersionBuildError(
QObject::tr("load id %1 does not match internal id %2").arg(id, file->fileId));
}
profile->appendPatch(file);
}
// now load the rest by internal preference.
QMap<int, QPair<QString, VersionFilePtr>> files;
for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
// parse the file
QLOG_INFO() << "Reading" << info.fileName();
auto file = ProfileUtils::parseJsonFile(info, true);
// ignore builtins
if (file->fileId == "net.minecraft")
continue;
if (file->fileId == "org.lwjgl")
continue;
// do not load what we already loaded in the first pass
if (userOrder.contains(file->fileId))
continue;
if (files.contains(file->order))
{
// FIXME: do not throw?
throw VersionBuildError(QObject::tr("%1 has the same order as %2")
.arg(file->fileId, files[file->order].second->fileId));
}
files.insert(file->order, qMakePair(info.fileName(), file));
}
for (auto order : files.keys())
{
auto &filePair = files[order];
profile->appendPatch(filePair.second);
}
}
void OneSixProfileStrategy::load()
{
profile->clearPatches();
upgradeDeprecatedFiles();
loadDefaultBuiltinPatches();
loadUserPatches();
profile->finalize();
}
bool OneSixProfileStrategy::saveOrder(ProfileUtils::PatchOrder order)
{
return ProfileUtils::writeOverrideOrders(PathCombine(m_instance->instanceRoot(), "order.json"), order);
}
bool OneSixProfileStrategy::resetOrder()
{
return QDir(m_instance->instanceRoot()).remove("order.json");
}
bool OneSixProfileStrategy::removePatch(ProfilePatchPtr patch)
{
bool ok = true;
// first, remove the patch file. this ensures it's not used anymore
auto fileName = patch->getPatchFilename();
auto preRemoveJarMod = [&](JarmodPtr jarMod) -> bool
{
QString fullpath = PathCombine(m_instance->jarModsDir(), jarMod->name);
QFileInfo finfo (fullpath);
if(finfo.exists())
{
return QFile::remove(fullpath);
}
return true;
};
for(auto &jarmod: patch->getJarMods())
{
ok &= preRemoveJarMod(jarmod);
}
return ok;
}
bool OneSixProfileStrategy::installJarMods(QStringList filepaths)
{
QString patchDir = PathCombine(m_instance->instanceRoot(), "patches");
if(!ensureFolderPathExists(patchDir))
{
return false;
}
if (!ensureFolderPathExists(m_instance->jarModsDir()))
{
return false;
}
for(auto filepath:filepaths)
{
QFileInfo sourceInfo(filepath);
auto uuid = QUuid::createUuid();
QString id = uuid.toString().remove('{').remove('}');
QString target_filename = id + ".jar";
QString target_id = "org.multimc.jarmod." + id;
QString target_name = sourceInfo.completeBaseName() + " (jar mod)";
QString finalPath = PathCombine(m_instance->jarModsDir(), target_filename);
QFileInfo targetInfo(finalPath);
if(targetInfo.exists())
{
return false;
}
if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
{
return false;
}
auto f = std::make_shared<VersionFile>();
auto jarMod = std::make_shared<Jarmod>();
jarMod->name = target_filename;
f->jarMods.append(jarMod);
f->name = target_name;
f->fileId = target_id;
f->order = profile->getFreeOrderNumber();
QString patchFileName = PathCombine(patchDir, target_id + ".json");
f->filename = patchFileName;
QFile file(patchFileName);
if (!file.open(QFile::WriteOnly))
{
QLOG_ERROR() << "Error opening" << file.fileName()
<< "for reading:" << file.errorString();
return false;
}
file.write(f->toJson(true).toJson());
file.close();
profile->appendPatch(f);
}
profile->saveCurrentOrder();
profile->reapply();
return true;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "ProfileStrategy.h"
class OneSixInstance;
class OneSixProfileStrategy : public ProfileStrategy
{
public:
OneSixProfileStrategy(OneSixInstance * instance);
virtual ~OneSixProfileStrategy() {};
virtual void load() override;
virtual bool resetOrder() override;
virtual bool saveOrder(ProfileUtils::PatchOrder order) override;
virtual bool installJarMods(QStringList filepaths) override;
virtual bool removePatch(ProfilePatchPtr patch) override;
protected:
void loadDefaultBuiltinPatches();
void loadUserPatches();
void upgradeDeprecatedFiles();
protected:
OneSixInstance *m_instance;
};

View File

@ -4,12 +4,12 @@
#include <QList> #include <QList>
#include "JarMod.h" #include "JarMod.h"
class InstanceVersion; class MinecraftProfile;
class VersionPatch class ProfilePatch
{ {
public: public:
virtual ~VersionPatch(){}; virtual ~ProfilePatch(){};
virtual void applyTo(InstanceVersion *version) = 0; virtual void applyTo(MinecraftProfile *version) = 0;
virtual bool isMinecraftVersion() = 0; virtual bool isMinecraftVersion() = 0;
virtual bool hasJarMods() = 0; virtual bool hasJarMods() = 0;
@ -29,4 +29,4 @@ public:
virtual bool isCustom() = 0; virtual bool isCustom() = 0;
}; };
typedef std::shared_ptr<VersionPatch> VersionPatchPtr; typedef std::shared_ptr<ProfilePatch> ProfilePatchPtr;

View File

@ -0,0 +1,30 @@
#pragma once
#include "ProfileUtils.h"
class MinecraftProfile;
class ProfileStrategy
{
friend class MinecraftProfile;
public:
virtual ~ProfileStrategy(){};
/// load the patch files into the profile
virtual void load() = 0;
/// reset the order of patches
virtual bool resetOrder() = 0;
/// save the order of patches, given the order
virtual bool saveOrder(ProfileUtils::PatchOrder order) = 0;
/// install a list of jar mods into the instance
virtual bool installJarMods(QStringList filepaths) = 0;
/// remove any files or records that constitute the version patch
virtual bool removePatch(ProfilePatchPtr jarMod) = 0;
protected:
MinecraftProfile *profile;
};

View File

@ -0,0 +1,145 @@
#include "ProfileUtils.h"
#include "logic/minecraft/VersionFilterData.h"
#include "logic/MMCJson.h"
#include "logger/QsLog.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QRegularExpression>
namespace ProfileUtils
{
static const int currentOrderFileVersion = 1;
bool writeOverrideOrders(QString path, const PatchOrder &order)
{
QJsonObject obj;
obj.insert("version", currentOrderFileVersion);
QJsonArray orderArray;
for(auto str: order)
{
orderArray.append(str);
}
obj.insert("order", orderArray);
QFile orderFile(path);
if (!orderFile.open(QFile::WriteOnly))
{
QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
<< "for writing:" << orderFile.errorString();
return false;
}
orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
return true;
}
bool readOverrideOrders(QString path, PatchOrder &order)
{
QFile orderFile(path);
if (!orderFile.exists())
{
QLOG_WARN() << "Order file doesn't exist. Ignoring.";
return false;
}
if (!orderFile.open(QFile::ReadOnly))
{
QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
<< " for reading:" << orderFile.errorString();
QLOG_WARN() << "Ignoring overriden order";
return false;
}
// and it's valid JSON
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
QLOG_WARN() << "Ignoring overriden order";
return false;
}
// and then read it and process it if all above is true.
try
{
auto obj = MMCJson::ensureObject(doc);
// check order file version.
auto version = MMCJson::ensureInteger(obj.value("version"), "version");
if (version != currentOrderFileVersion)
{
throw JSONValidationError(QObject::tr("Invalid order file version, expected %1")
.arg(currentOrderFileVersion));
}
auto orderArray = MMCJson::ensureArray(obj.value("order"));
for(auto item: orderArray)
{
order.append(MMCJson::ensureString(item));
}
}
catch (JSONValidationError &err)
{
QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
QLOG_WARN() << "Ignoring overriden order";
order.clear();
return false;
}
return true;
}
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB)
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
.arg(fileInfo.fileName(), file.errorString()));
}
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
throw JSONValidationError(
QObject::tr("Unable to process the version file %1: %2 at %3.")
.arg(fileInfo.fileName(), error.errorString())
.arg(error.offset));
}
return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
}
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo)
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
.arg(fileInfo.fileName(), file.errorString()));
}
QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
file.close();
if (doc.isNull())
{
file.remove();
throw JSONValidationError(
QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
}
return VersionFile::fromJson(doc, file.fileName(), false, false);
}
void removeLwjglFromPatch(VersionFilePtr patch)
{
auto filter = [](QList<RawLibraryPtr>& libs)
{
QList<RawLibraryPtr> filteredLibs;
for (auto lib : libs)
{
if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix()))
{
filteredLibs.append(lib);
}
}
libs = filteredLibs;
};
filter(patch->addLibs);
filter(patch->overwriteLibs);
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "RawLibrary.h"
#include "VersionFile.h"
namespace ProfileUtils
{
typedef QStringList PatchOrder;
/// Read and parse a OneSix format order file
bool readOverrideOrders(QString path, PatchOrder &order);
/// Write a OneSix format order file
bool writeOverrideOrders(QString path, const PatchOrder &order);
/// Parse a version file in JSON format
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB = false);
/// Parse a version file in binary JSON format
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files.
void removeLwjglFromPatch(VersionFilePtr patch);
}

View File

@ -24,15 +24,17 @@
#include <QDir> #include <QDir>
#include <qresource.h> #include <qresource.h>
#include <modutils.h> #include <modutils.h>
#include <pathutils.h>
#include "MultiMC.h" #include "MultiMC.h"
#include "logic/minecraft/VersionBuilder.h" #include "logic/minecraft/VersionBuilder.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/OneSixRule.h" #include "logic/minecraft/OneSixRule.h"
#include "logic/minecraft/VersionPatch.h" #include "logic/minecraft/ProfilePatch.h"
#include "logic/minecraft/VersionFile.h" #include "logic/minecraft/VersionFile.h"
#include "VersionBuildError.h" #include "VersionBuildError.h"
#include "MinecraftVersionList.h" #include "MinecraftVersionList.h"
#include "ProfileUtils.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/MMCJson.h" #include "logic/MMCJson.h"
@ -43,17 +45,15 @@ VersionBuilder::VersionBuilder()
{ {
} }
void VersionBuilder::build(InstanceVersion *version, OneSixInstance *instance, void VersionBuilder::build(MinecraftProfile *version, OneSixInstance *instance)
const QStringList &external)
{ {
VersionBuilder builder; VersionBuilder builder;
builder.m_version = version; builder.m_version = version;
builder.m_instance = instance; builder.m_instance = instance;
builder.external_patches = external;
builder.buildInternal(); builder.buildInternal();
} }
void VersionBuilder::readJsonAndApplyToVersion(InstanceVersion *version, const QJsonObject &obj) void VersionBuilder::readJsonAndApplyToVersion(MinecraftProfile *version, const QJsonObject &obj)
{ {
VersionBuilder builder; VersionBuilder builder;
builder.m_version = version; builder.m_version = version;
@ -61,178 +61,6 @@ void VersionBuilder::readJsonAndApplyToVersion(InstanceVersion *version, const Q
builder.readJsonAndApply(obj); builder.readJsonAndApply(obj);
} }
void VersionBuilder::buildFromCustomJson()
{
QLOG_INFO() << "Building version from custom.json within the instance.";
QLOG_INFO() << "Reading custom.json";
auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("custom.json")), false);
file->name = "custom.json";
file->filename = "custom.json";
file->fileId = "org.multimc.custom.json";
file->order = -1;
file->version = QString();
m_version->VersionPatches.append(file);
m_version->finalize();
return;
}
void VersionBuilder::buildFromVersionJson()
{
QLOG_INFO() << "Building version from version.json and patches within the instance.";
QLOG_INFO() << "Reading version.json";
auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("version.json")), false);
file->name = "Minecraft";
file->fileId = "org.multimc.version.json";
file->order = -1;
file->version = m_instance->intendedVersionId();
file->mcVersion = m_instance->intendedVersionId();
m_version->VersionPatches.append(file);
// load all patches, put into map for ordering, apply in the right order
readInstancePatches();
// some final touches
m_version->finalize();
}
void VersionBuilder::readInstancePatches()
{
PatchOrder userOrder;
readOverrideOrders(m_instance, userOrder);
QDir patches(instance_root.absoluteFilePath("patches/"));
// first, load things by sort order.
for (auto id : userOrder)
{
// ignore builtins
if (id == "net.minecraft")
continue;
if (id == "org.lwjgl")
continue;
// parse the file
QString filename = patches.absoluteFilePath(id + ".json");
QFileInfo finfo(filename);
if(!finfo.exists())
{
QLOG_INFO() << "Patch file " << filename << " was deleted by external means...";
continue;
}
QLOG_INFO() << "Reading" << filename << "by user order";
auto file = parseJsonFile(finfo, false);
// sanity check. prevent tampering with files.
if (file->fileId != id)
{
throw VersionBuildError(
QObject::tr("load id %1 does not match internal id %2").arg(id, file->fileId));
}
m_version->VersionPatches.append(file);
}
// now load the rest by internal preference.
QMap<int, QPair<QString, VersionFilePtr>> files;
for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
// parse the file
QLOG_INFO() << "Reading" << info.fileName();
auto file = parseJsonFile(info, true);
// ignore builtins
if (file->fileId == "net.minecraft")
continue;
if (file->fileId == "org.lwjgl")
continue;
// do not load what we already loaded in the first pass
if (userOrder.contains(file->fileId))
continue;
if (files.contains(file->order))
{
// FIXME: do not throw?
throw VersionBuildError(QObject::tr("%1 has the same order as %2")
.arg(file->fileId, files[file->order].second->fileId));
}
files.insert(file->order, qMakePair(info.fileName(), file));
}
for (auto order : files.keys())
{
auto &filePair = files[order];
m_version->VersionPatches.append(filePair.second);
}
}
void VersionBuilder::buildFromExternalPatches()
{
QLOG_INFO() << "Building version from external files.";
int externalOrder = -1;
for (auto fileName : external_patches)
{
QLOG_INFO() << "Reading" << fileName;
auto file = parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
file->name = QFileInfo(fileName).fileName();
file->fileId = "org.multimc.external." + file->name;
file->order = (externalOrder += 1);
file->version = QString();
file->mcVersion = QString();
m_version->VersionPatches.append(file);
}
// some final touches
m_version->finalize();
}
void VersionBuilder::buildFromMultilayer()
{
QLOG_INFO() << "Building version from multilayered sources.";
// just the builtin stuff for now
auto minecraftList = MMC->minecraftlist();
auto mcversion = minecraftList->findVersion(m_instance->intendedVersionId());
auto minecraftPatch = std::dynamic_pointer_cast<VersionPatch>(mcversion);
if (!minecraftPatch)
{
throw VersionIncomplete("net.minecraft");
}
minecraftPatch->setOrder(-2);
m_version->VersionPatches.append(minecraftPatch);
// TODO: this is obviously fake.
QResource LWJGL(":/versions/LWJGL/2.9.1.json");
auto lwjgl = parseJsonFile(LWJGL.absoluteFilePath(), false, false);
auto lwjglPatch = std::dynamic_pointer_cast<VersionPatch>(lwjgl);
if (!lwjglPatch)
{
throw VersionIncomplete("org.lwjgl");
}
lwjglPatch->setOrder(-1);
lwjgl->setVanilla(true);
m_version->VersionPatches.append(lwjglPatch);
// load all patches, put into map for ordering, apply in the right order
readInstancePatches();
m_version->finalize();
}
void VersionBuilder::buildInternal()
{
m_version->VersionPatches.clear();
instance_root = QDir(m_instance->instanceRoot());
// if we do external files, do just those.
if (!external_patches.isEmpty())
{
buildFromExternalPatches();
}
// else, if there's custom json, we just do that.
else if (QFile::exists(instance_root.absoluteFilePath("custom.json")))
{
buildFromCustomJson();
}
// version.json -> patches/*.json
else if (QFile::exists(instance_root.absoluteFilePath("version.json")))
{
buildFromVersionJson();
}
else
{
buildFromMultilayer();
}
}
void VersionBuilder::readJsonAndApply(const QJsonObject &obj) void VersionBuilder::readJsonAndApply(const QJsonObject &obj)
{ {
m_version->clear(); m_version->clear();
@ -240,121 +68,17 @@ void VersionBuilder::readJsonAndApply(const QJsonObject &obj)
auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false); auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
file->applyTo(m_version); file->applyTo(m_version);
m_version->VersionPatches.append(file); m_version->appendPatch(file);
} }
VersionFilePtr VersionBuilder::parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
bool isFTB) void VersionBuilder::readInstancePatches()
{ {
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
.arg(fileInfo.fileName(), file.errorString()));
}
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
throw JSONValidationError(
QObject::tr("Unable to process the version file %1: %2 at %3.")
.arg(fileInfo.fileName(), error.errorString())
.arg(error.offset));
}
return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
} }
VersionFilePtr VersionBuilder::parseBinaryJsonFile(const QFileInfo &fileInfo) void VersionBuilder::buildInternal()
{ {
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
.arg(fileInfo.fileName(), file.errorString()));
}
QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
file.close();
if (doc.isNull())
{
file.remove();
throw JSONValidationError(
QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
}
return VersionFile::fromJson(doc, file.fileName(), false, false);
} }
static const int currentOrderFileVersion = 1;
bool VersionBuilder::readOverrideOrders(OneSixInstance *instance, PatchOrder &order)
{
QFile orderFile(instance->instanceRoot() + "/order.json");
if (!orderFile.exists())
{
QLOG_WARN() << "Order file doesn't exist. Ignoring.";
return false;
}
if (!orderFile.open(QFile::ReadOnly))
{
QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
<< " for reading:" << orderFile.errorString();
QLOG_WARN() << "Ignoring overriden order";
return false;
}
// and it's valid JSON
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
QLOG_WARN() << "Ignoring overriden order";
return false;
}
// and then read it and process it if all above is true.
try
{
auto obj = MMCJson::ensureObject(doc);
// check order file version.
auto version = MMCJson::ensureInteger(obj.value("version"), "version");
if (version != currentOrderFileVersion)
{
throw JSONValidationError(QObject::tr("Invalid order file version, expected %1")
.arg(currentOrderFileVersion));
}
auto orderArray = MMCJson::ensureArray(obj.value("order"));
for(auto item: orderArray)
{
order.append(MMCJson::ensureString(item));
}
}
catch (JSONValidationError &err)
{
QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
QLOG_WARN() << "Ignoring overriden order";
order.clear();
return false;
}
return true;
}
bool VersionBuilder::writeOverrideOrders(OneSixInstance *instance, const PatchOrder &order)
{
QJsonObject obj;
obj.insert("version", currentOrderFileVersion);
QJsonArray orderArray;
for(auto str: order)
{
orderArray.append(str);
}
obj.insert("order", orderArray);
QFile orderFile(instance->instanceRoot() + "/order.json");
if (!orderFile.open(QFile::WriteOnly))
{
QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
<< "for writing:" << orderFile.errorString();
return false;
}
orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
return true;
}

View File

@ -19,36 +19,23 @@
#include <QMap> #include <QMap>
#include "VersionFile.h" #include "VersionFile.h"
class InstanceVersion; class MinecraftProfile;
class OneSixInstance; class OneSixInstance;
class QJsonObject; class QJsonObject;
class QFileInfo; class QFileInfo;
typedef QStringList PatchOrder;
class VersionBuilder class VersionBuilder
{ {
VersionBuilder(); VersionBuilder();
public: public:
static void build(InstanceVersion *version, OneSixInstance *instance, const QStringList &external); static void build(MinecraftProfile *version, OneSixInstance *instance);
static void readJsonAndApplyToVersion(InstanceVersion *version, const QJsonObject &obj); static void readJsonAndApplyToVersion(MinecraftProfile *version, const QJsonObject &obj);
static VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB = false);
static VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
bool readOverrideOrders(OneSixInstance *instance, PatchOrder &order);
static bool writeOverrideOrders(OneSixInstance *instance, const PatchOrder &order);
private: private:
InstanceVersion *m_version; MinecraftProfile *m_version;
OneSixInstance *m_instance; OneSixInstance *m_instance;
QStringList external_patches;
QDir instance_root;
void buildInternal(); void buildInternal();
void buildFromExternalPatches();
void buildFromCustomJson();
void buildFromVersionJson();
void buildFromMultilayer();
void readInstancePatches(); void readInstancePatches();

View File

@ -6,7 +6,7 @@
#include "logic/minecraft/VersionFile.h" #include "logic/minecraft/VersionFile.h"
#include "logic/minecraft/OneSixLibrary.h" #include "logic/minecraft/OneSixLibrary.h"
#include "logic/minecraft/InstanceVersion.h" #include "logic/minecraft/MinecraftProfile.h"
#include "logic/minecraft/JarMod.h" #include "logic/minecraft/JarMod.h"
#include "ParseUtils.h" #include "ParseUtils.h"
@ -256,8 +256,7 @@ QJsonDocument VersionFile::toJson(bool saveOrder)
bool VersionFile::isMinecraftVersion() bool VersionFile::isMinecraftVersion()
{ {
return (fileId == "org.multimc.version.json") || (fileId == "net.minecraft") || return fileId == "net.minecraft";
(fileId == "org.multimc.custom.json");
} }
bool VersionFile::hasJarMods() bool VersionFile::hasJarMods()
@ -265,7 +264,7 @@ bool VersionFile::hasJarMods()
return !jarMods.isEmpty(); return !jarMods.isEmpty();
} }
void VersionFile::applyTo(InstanceVersion *version) void VersionFile::applyTo(MinecraftProfile *version)
{ {
if (minimumLauncherVersion != -1) if (minimumLauncherVersion != -1)
{ {

View File

@ -6,23 +6,23 @@
#include <memory> #include <memory>
#include "logic/minecraft/OpSys.h" #include "logic/minecraft/OpSys.h"
#include "logic/minecraft/OneSixRule.h" #include "logic/minecraft/OneSixRule.h"
#include "VersionPatch.h" #include "ProfilePatch.h"
#include "MMCError.h" #include "MMCError.h"
#include "OneSixLibrary.h" #include "OneSixLibrary.h"
#include "JarMod.h" #include "JarMod.h"
class InstanceVersion; class MinecraftProfile;
class VersionFile; class VersionFile;
typedef std::shared_ptr<VersionFile> VersionFilePtr; typedef std::shared_ptr<VersionFile> VersionFilePtr;
class VersionFile : public VersionPatch class VersionFile : public ProfilePatch
{ {
public: /* methods */ public: /* methods */
static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename, static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
const bool requireOrder, const bool isFTB = false); const bool requireOrder, const bool isFTB = false);
QJsonDocument toJson(bool saveOrder); QJsonDocument toJson(bool saveOrder);
virtual void applyTo(InstanceVersion *version) override; virtual void applyTo(MinecraftProfile *version) override;
virtual bool isMinecraftVersion() override; virtual bool isMinecraftVersion() override;
virtual bool hasJarMods() override; virtual bool hasJarMods() override;
virtual int getOrder() override virtual int getOrder() override

View File

@ -1,5 +1,5 @@
#include "VersionFilterData.h" #include "VersionFilterData.h"
#include "minecraft/ParseUtils.h" #include "ParseUtils.h"
VersionFilterData g_VersionFilterData = VersionFilterData(); VersionFilterData g_VersionFilterData = VersionFilterData();

View File

@ -3,8 +3,6 @@
#include "logger/QsLog.h" #include "logger/QsLog.h"
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument> #include <QJsonDocument>
#include "gui/dialogs/CustomMessageBox.h"
#include <QDesktopServices>
PasteUpload::PasteUpload(QWidget *window, QString text) : m_window(window) PasteUpload::PasteUpload(QWidget *window, QString text) : m_window(window)
{ {

View File

@ -30,31 +30,6 @@ qint64 BaseExternalTool::pid(QProcess *process)
#endif #endif
} }
QString BaseExternalTool::getSave() const
{
QDir saves(m_instance->minecraftRoot() + "/saves");
QStringList worlds = saves.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QMutableListIterator<QString> it(worlds);
while (it.hasNext())
{
it.next();
if (!QDir(saves.absoluteFilePath(it.value())).exists("level.dat"))
{
it.remove();
}
}
bool ok = true;
const QString save = QInputDialog::getItem(
MMC->activeWindow(), tr("MCEdit"), tr("Choose which world to open:"),
worlds, 0, false, &ok);
if (ok)
{
return saves.absoluteFilePath(save);
}
return QString();
}
BaseDetachedTool::BaseDetachedTool(InstancePtr instance, QObject *parent) BaseDetachedTool::BaseDetachedTool(InstancePtr instance, QObject *parent)
: BaseExternalTool(instance, parent) : BaseExternalTool(instance, parent)
{ {

View File

@ -5,7 +5,6 @@
class BaseInstance; class BaseInstance;
class SettingsObject; class SettingsObject;
class MinecraftProcess;
class QProcess; class QProcess;
class BaseExternalTool : public QObject class BaseExternalTool : public QObject
@ -19,7 +18,6 @@ protected:
InstancePtr m_instance; InstancePtr m_instance;
qint64 pid(QProcess *process); qint64 pid(QProcess *process);
QString getSave() const;
}; };
class BaseDetachedTool : public BaseExternalTool class BaseDetachedTool : public BaseExternalTool

View File

@ -7,7 +7,7 @@ BaseProfiler::BaseProfiler(InstancePtr instance, QObject *parent)
{ {
} }
void BaseProfiler::beginProfiling(MinecraftProcess *process) void BaseProfiler::beginProfiling(BaseProcess *process)
{ {
beginProfilingImpl(process); beginProfilingImpl(process);
} }

View File

@ -4,7 +4,7 @@
class BaseInstance; class BaseInstance;
class SettingsObject; class SettingsObject;
class MinecraftProcess; class BaseProcess;
class QProcess; class QProcess;
class BaseProfiler : public BaseExternalTool class BaseProfiler : public BaseExternalTool
@ -15,13 +15,13 @@ public:
public public
slots: slots:
void beginProfiling(MinecraftProcess *process); void beginProfiling(BaseProcess *process);
void abortProfiling(); void abortProfiling();
protected: protected:
QProcess *m_profilerProcess; QProcess *m_profilerProcess;
virtual void beginProfilingImpl(MinecraftProcess *process) = 0; virtual void beginProfilingImpl(BaseProcess *process) = 0;
virtual void abortProfilingImpl(); virtual void abortProfilingImpl();
signals: signals:

View File

@ -4,7 +4,7 @@
#include <QMessageBox> #include <QMessageBox>
#include "logic/settings/SettingsObject.h" #include "logic/settings/SettingsObject.h"
#include "logic/MinecraftProcess.h" #include "logic/BaseProcess.h"
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "MultiMC.h" #include "MultiMC.h"
@ -12,7 +12,7 @@ JProfiler::JProfiler(InstancePtr instance, QObject *parent) : BaseProfiler(insta
{ {
} }
void JProfiler::beginProfilingImpl(MinecraftProcess *process) void JProfiler::beginProfilingImpl(BaseProcess *process)
{ {
int port = MMC->settings()->get("JProfilerPort").toInt(); int port = MMC->settings()->get("JProfilerPort").toInt();
QProcess *profiler = new QProcess(this); QProcess *profiler = new QProcess(this);

View File

@ -9,7 +9,7 @@ public:
JProfiler(InstancePtr instance, QObject *parent = 0); JProfiler(InstancePtr instance, QObject *parent = 0);
protected: protected:
void beginProfilingImpl(MinecraftProcess *process); void beginProfilingImpl(BaseProcess *process);
}; };
class JProfilerFactory : public BaseProfilerFactory class JProfilerFactory : public BaseProfilerFactory

View File

@ -4,7 +4,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include "logic/settings/SettingsObject.h" #include "logic/settings/SettingsObject.h"
#include "logic/MinecraftProcess.h" #include "logic/BaseProcess.h"
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "MultiMC.h" #include "MultiMC.h"
@ -12,7 +12,7 @@ JVisualVM::JVisualVM(InstancePtr instance, QObject *parent) : BaseProfiler(insta
{ {
} }
void JVisualVM::beginProfilingImpl(MinecraftProcess *process) void JVisualVM::beginProfilingImpl(BaseProcess *process)
{ {
QProcess *profiler = new QProcess(this); QProcess *profiler = new QProcess(this);
profiler->setArguments(QStringList() << "--openpid" << QString::number(pid(process))); profiler->setArguments(QStringList() << "--openpid" << QString::number(pid(process)));

View File

@ -9,7 +9,7 @@ public:
JVisualVM(InstancePtr instance, QObject *parent = 0); JVisualVM(InstancePtr instance, QObject *parent = 0);
protected: protected:
void beginProfilingImpl(MinecraftProcess *process); void beginProfilingImpl(BaseProcess *process);
}; };
class JVisualVMFactory : public BaseProfilerFactory class JVisualVMFactory : public BaseProfilerFactory

View File

@ -4,9 +4,12 @@
#include <QProcess> #include <QProcess>
#include <QDesktopServices> #include <QDesktopServices>
#include <QUrl> #include <QUrl>
// FIXME: mixing logic and UI!!!!
#include <QInputDialog>
#include "logic/settings/SettingsObject.h" #include "logic/settings/SettingsObject.h"
#include "logic/BaseInstance.h" #include "logic/BaseInstance.h"
#include "logic/minecraft/MinecraftInstance.h"
#include "MultiMC.h" #include "MultiMC.h"
MCEditTool::MCEditTool(InstancePtr instance, QObject *parent) MCEditTool::MCEditTool(InstancePtr instance, QObject *parent)
@ -14,6 +17,36 @@ MCEditTool::MCEditTool(InstancePtr instance, QObject *parent)
{ {
} }
QString MCEditTool::getSave() const
{
// FIXME: mixing logic and UI!!!!
auto mcInstance = std::dynamic_pointer_cast<MinecraftInstance>(m_instance);
if(!mcInstance)
{
return QString();
}
QDir saves(mcInstance->minecraftRoot() + "/saves");
QStringList worlds = saves.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QMutableListIterator<QString> it(worlds);
while (it.hasNext())
{
it.next();
if (!QDir(saves.absoluteFilePath(it.value())).exists("level.dat"))
{
it.remove();
}
}
bool ok = true;
const QString save = QInputDialog::getItem(
MMC->activeWindow(), tr("MCEdit"), tr("Choose which world to open:"),
worlds, 0, false, &ok);
if (ok)
{
return saves.absoluteFilePath(save);
}
return QString();
}
void MCEditTool::runImpl() void MCEditTool::runImpl()
{ {
const QString mceditPath = MMC->settings()->get("MCEditPath").toString(); const QString mceditPath = MMC->settings()->get("MCEditPath").toString();

View File

@ -9,6 +9,7 @@ public:
explicit MCEditTool(InstancePtr instance, QObject *parent = 0); explicit MCEditTool(InstancePtr instance, QObject *parent = 0);
protected: protected:
QString getSave() const;
void runImpl() override; void runImpl() override;
}; };

View File

@ -9,7 +9,6 @@ int main_gui(MultiMC &app)
mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray())); mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray())); mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
mainWin.show(); mainWin.show();
mainWin.checkMigrateLegacyAssets();
mainWin.checkSetDefaultJava(); mainWin.checkSetDefaultJava();
mainWin.checkInstancePathForProblems(); mainWin.checkInstancePathForProblems();
return app.exec(); return app.exec();