3a53349e33
Both act as the first step of AuthContext.
1449 lines
45 KiB
C++
1449 lines
45 KiB
C++
#include "MultiMC.h"
|
|
#include "BuildConfig.h"
|
|
#include "MainWindow.h"
|
|
#include "InstanceWindow.h"
|
|
|
|
#include "groupview/AccessibleGroupView.h"
|
|
#include <QAccessible>
|
|
|
|
#include "pages/BasePageProvider.h"
|
|
#include "pages/global/MultiMCPage.h"
|
|
#include "pages/global/MinecraftPage.h"
|
|
#include "pages/global/JavaPage.h"
|
|
#include "pages/global/LanguagePage.h"
|
|
#include "pages/global/ProxyPage.h"
|
|
#include "pages/global/ExternalToolsPage.h"
|
|
#include "pages/global/AccountListPage.h"
|
|
#include "pages/global/PasteEEPage.h"
|
|
#include "pages/global/CustomCommandsPage.h"
|
|
|
|
#include "themes/ITheme.h"
|
|
#include "themes/SystemTheme.h"
|
|
#include "themes/DarkTheme.h"
|
|
#include "themes/BrightTheme.h"
|
|
#include "themes/CustomTheme.h"
|
|
|
|
#include "setupwizard/SetupWizard.h"
|
|
#include "setupwizard/LanguageWizardPage.h"
|
|
#include "setupwizard/JavaWizardPage.h"
|
|
#include "setupwizard/AnalyticsWizardPage.h"
|
|
|
|
#include <iostream>
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QNetworkAccessManager>
|
|
#include <QTranslator>
|
|
#include <QLibraryInfo>
|
|
#include <QList>
|
|
#include <QStringList>
|
|
#include <QDebug>
|
|
#include <QStyleFactory>
|
|
|
|
#include "dialogs/CustomMessageBox.h"
|
|
#include "InstanceList.h"
|
|
|
|
#include <minecraft/auth/AccountList.h>
|
|
#include "icons/IconList.h"
|
|
#include "net/HttpMetaCache.h"
|
|
#include "Env.h"
|
|
|
|
#include "java/JavaUtils.h"
|
|
|
|
#include "updater/UpdateChecker.h"
|
|
|
|
#include "tools/JProfiler.h"
|
|
#include "tools/JVisualVM.h"
|
|
#include "tools/MCEditTool.h"
|
|
|
|
#include <xdgicon.h>
|
|
#include "settings/INISettingsObject.h"
|
|
#include "settings/Setting.h"
|
|
|
|
#include "translations/TranslationsModel.h"
|
|
|
|
#include <Commandline.h>
|
|
#include <FileSystem.h>
|
|
#include <DesktopServices.h>
|
|
#include <LocalPeer.h>
|
|
|
|
#include <ganalytics.h>
|
|
#include <sys.h>
|
|
|
|
#include "pagedialog/PageDialog.h"
|
|
|
|
|
|
#if defined Q_OS_WIN32
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#define STRINGIFY(x) #x
|
|
#define TOSTRING(x) STRINGIFY(x)
|
|
|
|
static const QLatin1String liveCheckFile("live.check");
|
|
|
|
using namespace Commandline;
|
|
|
|
#define MACOS_HINT "If you are on macOS Sierra, you might have to move MultiMC.app to your /Applications or ~/Applications folder. "\
|
|
"This usually fixes the problem and you can move the application elsewhere afterwards.\n"\
|
|
"\n"
|
|
|
|
static void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
|
{
|
|
const char *levels = "DWCFIS";
|
|
const QString format("%1 %2 %3\n");
|
|
|
|
qint64 msecstotal = MMC->timeSinceStart();
|
|
qint64 seconds = msecstotal / 1000;
|
|
qint64 msecs = msecstotal % 1000;
|
|
QString foo;
|
|
char buf[1025] = {0};
|
|
::snprintf(buf, 1024, "%5lld.%03lld", seconds, msecs);
|
|
|
|
QString out = format.arg(buf).arg(levels[type]).arg(msg);
|
|
|
|
MMC->logFile->write(out.toUtf8());
|
|
MMC->logFile->flush();
|
|
QTextStream(stderr) << out.toLocal8Bit();
|
|
fflush(stderr);
|
|
}
|
|
|
|
MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
|
|
{
|
|
#if defined Q_OS_WIN32
|
|
// attach the parent console
|
|
if(AttachConsole(ATTACH_PARENT_PROCESS))
|
|
{
|
|
// if attach succeeds, reopen and sync all the i/o
|
|
if(freopen("CON", "w", stdout))
|
|
{
|
|
std::cout.sync_with_stdio();
|
|
}
|
|
if(freopen("CON", "w", stderr))
|
|
{
|
|
std::cerr.sync_with_stdio();
|
|
}
|
|
if(freopen("CON", "r", stdin))
|
|
{
|
|
std::cin.sync_with_stdio();
|
|
}
|
|
auto out = GetStdHandle (STD_OUTPUT_HANDLE);
|
|
DWORD written;
|
|
const char * endline = "\n";
|
|
WriteConsole(out, endline, strlen(endline), &written, NULL);
|
|
consoleAttached = true;
|
|
}
|
|
#endif
|
|
setOrganizationName("MultiMC");
|
|
setOrganizationDomain("multimc.org");
|
|
setApplicationName("MultiMC5");
|
|
setApplicationDisplayName("MultiMC 5");
|
|
setApplicationVersion(BuildConfig.printableVersionString());
|
|
|
|
startTime = QDateTime::currentDateTime();
|
|
|
|
#ifdef Q_OS_LINUX
|
|
{
|
|
QFile osrelease("/proc/sys/kernel/osrelease");
|
|
if (osrelease.open(QFile::ReadOnly | QFile::Text)) {
|
|
QTextStream in(&osrelease);
|
|
auto contents = in.readAll();
|
|
if(
|
|
contents.contains("WSL", Qt::CaseInsensitive) ||
|
|
contents.contains("Microsoft", Qt::CaseInsensitive)
|
|
) {
|
|
showFatalErrorMessage(
|
|
"Unsupported system detected!",
|
|
"Linux-on-Windows distributions are not supported.\n\n"
|
|
"Please use the Windows MultiMC binary when playing on Windows."
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Don't quit on hiding the last window
|
|
this->setQuitOnLastWindowClosed(false);
|
|
|
|
// Commandline parsing
|
|
QHash<QString, QVariant> args;
|
|
{
|
|
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
|
|
|
// --help
|
|
parser.addSwitch("help");
|
|
parser.addShortOpt("help", 'h');
|
|
parser.addDocumentation("help", "Display this help and exit.");
|
|
// --version
|
|
parser.addSwitch("version");
|
|
parser.addShortOpt("version", 'V');
|
|
parser.addDocumentation("version", "Display program version and exit.");
|
|
// --dir
|
|
parser.addOption("dir");
|
|
parser.addShortOpt("dir", 'd');
|
|
parser.addDocumentation("dir", "Use the supplied folder as MultiMC root instead of "
|
|
"the binary location (use '.' for current)");
|
|
// --launch
|
|
parser.addOption("launch");
|
|
parser.addShortOpt("launch", 'l');
|
|
parser.addDocumentation("launch", "Launch the specified instance (by instance ID)");
|
|
// --server
|
|
parser.addOption("server");
|
|
parser.addShortOpt("server", 's');
|
|
parser.addDocumentation("server", "Join the specified server on launch "
|
|
"(only valid in combination with --launch)");
|
|
// --alive
|
|
parser.addSwitch("alive");
|
|
parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after MultiMC starts");
|
|
// --import
|
|
parser.addOption("import");
|
|
parser.addShortOpt("import", 'I');
|
|
parser.addDocumentation("import", "Import instance from specified zip (local path or URL)");
|
|
|
|
// parse the arguments
|
|
try
|
|
{
|
|
args = parser.parse(arguments());
|
|
}
|
|
catch (const ParsingError &e)
|
|
{
|
|
std::cerr << "CommandLineError: " << e.what() << std::endl;
|
|
if(argc > 0)
|
|
std::cerr << "Try '" << argv[0] << " -h' to get help on MultiMC's command line parameters."
|
|
<< std::endl;
|
|
m_status = MultiMC::Failed;
|
|
return;
|
|
}
|
|
|
|
// display help and exit
|
|
if (args["help"].toBool())
|
|
{
|
|
std::cout << qPrintable(parser.compileHelp(arguments()[0]));
|
|
m_status = MultiMC::Succeeded;
|
|
return;
|
|
}
|
|
|
|
// display version and exit
|
|
if (args["version"].toBool())
|
|
{
|
|
std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
|
|
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
|
|
m_status = MultiMC::Succeeded;
|
|
return;
|
|
}
|
|
}
|
|
m_instanceIdToLaunch = args["launch"].toString();
|
|
m_serverToJoin = args["server"].toString();
|
|
m_liveCheck = args["alive"].toBool();
|
|
m_zipToImport = args["import"].toUrl();
|
|
|
|
QString origcwdPath = QDir::currentPath();
|
|
QString binPath = applicationDirPath();
|
|
QString adjustedBy;
|
|
QString dataPath;
|
|
// change folder
|
|
QString dirParam = args["dir"].toString();
|
|
if (!dirParam.isEmpty())
|
|
{
|
|
// the dir param. it makes multimc data path point to whatever the user specified
|
|
// on command line
|
|
adjustedBy += "Command line " + dirParam;
|
|
dataPath = dirParam;
|
|
}
|
|
else
|
|
{
|
|
#ifdef MULTIMC_LINUX_DATADIR
|
|
QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME"));
|
|
if (xdgDataHome.isEmpty())
|
|
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
|
|
dataPath = xdgDataHome + "/multimc";
|
|
adjustedBy += "XDG standard " + dataPath;
|
|
#elif defined(Q_OS_MAC)
|
|
QDir foo(FS::PathCombine(applicationDirPath(), "../../Data"));
|
|
dataPath = foo.absolutePath();
|
|
adjustedBy += "Fallback to special Mac location " + dataPath;
|
|
#else
|
|
dataPath = applicationDirPath();
|
|
adjustedBy += "Fallback to binary path " + dataPath;
|
|
#endif
|
|
}
|
|
|
|
if (!FS::ensureFolderPathExists(dataPath))
|
|
{
|
|
showFatalErrorMessage(
|
|
"MultiMC data folder could not be created.",
|
|
"MultiMC data folder could not be created.\n"
|
|
"\n"
|
|
#if defined(Q_OS_MAC)
|
|
MACOS_HINT
|
|
#endif
|
|
"Make sure you have the right permissions to the MultiMC data folder and any folder needed to access it.\n"
|
|
"\n"
|
|
"MultiMC cannot continue until you fix this problem."
|
|
);
|
|
return;
|
|
}
|
|
if (!QDir::setCurrent(dataPath))
|
|
{
|
|
showFatalErrorMessage(
|
|
"MultiMC data folder could not be opened.",
|
|
"MultiMC data folder could not be opened.\n"
|
|
"\n"
|
|
#if defined(Q_OS_MAC)
|
|
MACOS_HINT
|
|
#endif
|
|
"Make sure you have the right permissions to the MultiMC data folder.\n"
|
|
"\n"
|
|
"MultiMC cannot continue until you fix this problem."
|
|
);
|
|
return;
|
|
}
|
|
|
|
if(m_instanceIdToLaunch.isEmpty() && !m_serverToJoin.isEmpty())
|
|
{
|
|
std::cerr << "--server can only be used in combination with --launch!" << std::endl;
|
|
m_status = MultiMC::Failed;
|
|
return;
|
|
}
|
|
|
|
#if defined(Q_OS_MAC)
|
|
// move user data to new location if on macOS and it still exists in Contents/MacOS
|
|
QDir fi(applicationDirPath());
|
|
QString originalData = fi.absolutePath();
|
|
// if the config file exists in Contents/MacOS, then user data is still there and needs to moved
|
|
if (QFileInfo::exists(FS::PathCombine(originalData, "multimc.cfg")))
|
|
{
|
|
if (!QFileInfo::exists(FS::PathCombine(originalData, "dontmovemacdata")))
|
|
{
|
|
QMessageBox::StandardButton askMoveDialogue;
|
|
askMoveDialogue = QMessageBox::question(nullptr, "MultiMC 5", "Would you like to move application data to a new data location? It will improve MultiMC's performance, but if you switch to older versions it will look like instances have disappeared. If you select no, you can migrate later in settings. You should select yes unless you're commonly switching between different versions of MultiMC (eg. develop and stable).", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
|
if (askMoveDialogue == QMessageBox::Yes)
|
|
{
|
|
qDebug() << "On macOS and found config file in old location, moving user data...";
|
|
QDir dir;
|
|
QStringList dataFiles {
|
|
"*.log", // MultiMC-@.log
|
|
"accounts.json",
|
|
"accounts",
|
|
"assets",
|
|
"cache",
|
|
"icons",
|
|
"instances",
|
|
"libraries",
|
|
"meta",
|
|
"metacache",
|
|
"mods",
|
|
"multimc.cfg",
|
|
"themes",
|
|
"translations"
|
|
};
|
|
QDirIterator files(originalData, dataFiles);
|
|
while (files.hasNext()) {
|
|
QString filePath(files.next());
|
|
QString fileName(files.fileName());
|
|
if (!dir.rename(filePath, FS::PathCombine(dataPath, fileName)))
|
|
{
|
|
qWarning() << "Failed to move " << fileName;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dataPath = originalData;
|
|
QDir::setCurrent(dataPath);
|
|
QFile file(originalData + "/dontmovemacdata");
|
|
file.open(QIODevice::WriteOnly);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dataPath = originalData;
|
|
QDir::setCurrent(dataPath);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Establish the mechanism for communication with an already running MultiMC that uses the same data path.
|
|
* If there is one, tell it what the user actually wanted to do and exit.
|
|
* We want to initialize this before logging to avoid messing with the log of a potential already running copy.
|
|
*/
|
|
auto appID = ApplicationId::fromPathAndVersion(QDir::currentPath(), BuildConfig.printableVersionString());
|
|
{
|
|
// FIXME: you can run the same binaries with multiple data dirs and they won't clash. This could cause issues for updates.
|
|
m_peerInstance = new LocalPeer(this, appID);
|
|
connect(m_peerInstance, &LocalPeer::messageReceived, this, &MultiMC::messageReceived);
|
|
if(m_peerInstance->isClient())
|
|
{
|
|
int timeout = 2000;
|
|
|
|
if(m_instanceIdToLaunch.isEmpty())
|
|
{
|
|
m_peerInstance->sendMessage("activate", timeout);
|
|
|
|
if(!m_zipToImport.isEmpty())
|
|
{
|
|
m_peerInstance->sendMessage("import " + m_zipToImport.toString(), timeout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!m_serverToJoin.isEmpty())
|
|
{
|
|
m_peerInstance->sendMessage(
|
|
"launch-with-server " + m_instanceIdToLaunch + " " + m_serverToJoin, timeout);
|
|
}
|
|
else
|
|
{
|
|
m_peerInstance->sendMessage("launch " + m_instanceIdToLaunch, timeout);
|
|
}
|
|
}
|
|
m_status = MultiMC::Succeeded;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// init the logger
|
|
{
|
|
static const QString logBase = "MultiMC-%0.log";
|
|
auto moveFile = [](const QString &oldName, const QString &newName)
|
|
{
|
|
QFile::remove(newName);
|
|
QFile::copy(oldName, newName);
|
|
QFile::remove(oldName);
|
|
};
|
|
|
|
moveFile(logBase.arg(3), logBase.arg(4));
|
|
moveFile(logBase.arg(2), logBase.arg(3));
|
|
moveFile(logBase.arg(1), logBase.arg(2));
|
|
moveFile(logBase.arg(0), logBase.arg(1));
|
|
|
|
logFile = std::unique_ptr<QFile>(new QFile(logBase.arg(0)));
|
|
if(!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
|
|
{
|
|
showFatalErrorMessage(
|
|
"MultiMC data folder is not writable!",
|
|
"MultiMC couldn't create a log file - the MultiMC data folder is not writable.\n"
|
|
"\n"
|
|
#if defined(Q_OS_MAC)
|
|
MACOS_HINT
|
|
#endif
|
|
"Make sure you have write permissions to the MultiMC data folder.\n"
|
|
"\n"
|
|
"MultiMC cannot continue until you fix this problem."
|
|
);
|
|
return;
|
|
}
|
|
qInstallMessageHandler(appDebugOutput);
|
|
qDebug() << "<> Log initialized.";
|
|
}
|
|
|
|
// Set up paths
|
|
{
|
|
// Root path is used for updates.
|
|
#ifdef Q_OS_LINUX
|
|
QDir foo(FS::PathCombine(binPath, ".."));
|
|
m_rootPath = foo.absolutePath();
|
|
#elif defined(Q_OS_WIN32)
|
|
m_rootPath = binPath;
|
|
#elif defined(Q_OS_MAC)
|
|
QDir foo(FS::PathCombine(binPath, "../.."));
|
|
m_rootPath = foo.absolutePath();
|
|
// on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
|
|
FS::updateTimestamp(m_rootPath);
|
|
#endif
|
|
|
|
#ifdef MULTIMC_JARS_LOCATION
|
|
ENV.setJarsPath( TOSTRING(MULTIMC_JARS_LOCATION) );
|
|
#endif
|
|
|
|
qDebug() << "MultiMC 5, (c) 2013-2021 MultiMC Contributors";
|
|
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
|
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
|
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
|
if (adjustedBy.size())
|
|
{
|
|
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
|
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
|
|
qDebug() << "Adjusted by : " << adjustedBy;
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Work dir : " << QDir::currentPath();
|
|
}
|
|
qDebug() << "Binary path : " << binPath;
|
|
qDebug() << "Application root path : " << m_rootPath;
|
|
if(!m_instanceIdToLaunch.isEmpty())
|
|
{
|
|
qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
|
|
}
|
|
if(!m_serverToJoin.isEmpty())
|
|
{
|
|
qDebug() << "Address of server to join :" << m_serverToJoin;
|
|
}
|
|
qDebug() << "<> Paths set.";
|
|
}
|
|
|
|
do // once
|
|
{
|
|
if(m_liveCheck)
|
|
{
|
|
QFile check(liveCheckFile);
|
|
if(!check.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
|
{
|
|
qWarning() << "Could not open" << liveCheckFile << "for writing!";
|
|
break;
|
|
}
|
|
auto payload = appID.toString().toUtf8();
|
|
if(check.write(payload) != payload.size())
|
|
{
|
|
qWarning() << "Could not write into" << liveCheckFile << "!";
|
|
check.remove();
|
|
break;
|
|
}
|
|
check.close();
|
|
}
|
|
} while(false);
|
|
|
|
// Initialize application settings
|
|
{
|
|
m_settings.reset(new INISettingsObject("multimc.cfg", this));
|
|
// Updates
|
|
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
|
m_settings->registerSetting("AutoUpdate", true);
|
|
|
|
// Theming
|
|
m_settings->registerSetting("IconTheme", QString("multimc"));
|
|
m_settings->registerSetting("ApplicationTheme", QString("system"));
|
|
|
|
// Notifications
|
|
m_settings->registerSetting("ShownNotifications", QString());
|
|
|
|
// Remembered state
|
|
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
|
|
|
|
QString defaultMonospace;
|
|
int defaultSize = 11;
|
|
#ifdef Q_OS_WIN32
|
|
defaultMonospace = "Courier";
|
|
defaultSize = 10;
|
|
#elif defined(Q_OS_MAC)
|
|
defaultMonospace = "Menlo";
|
|
#else
|
|
defaultMonospace = "Monospace";
|
|
#endif
|
|
|
|
// resolve the font so the default actually matches
|
|
QFont consoleFont;
|
|
consoleFont.setFamily(defaultMonospace);
|
|
consoleFont.setStyleHint(QFont::Monospace);
|
|
consoleFont.setFixedPitch(true);
|
|
QFontInfo consoleFontInfo(consoleFont);
|
|
QString resolvedDefaultMonospace = consoleFontInfo.family();
|
|
QFont resolvedFont(resolvedDefaultMonospace);
|
|
qDebug() << "Detected default console font:" << resolvedDefaultMonospace
|
|
<< ", substitutions:" << resolvedFont.substitutions().join(',');
|
|
|
|
m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace);
|
|
m_settings->registerSetting("ConsoleFontSize", defaultSize);
|
|
m_settings->registerSetting("ConsoleMaxLines", 100000);
|
|
m_settings->registerSetting("ConsoleOverflowStop", true);
|
|
|
|
// Folders
|
|
m_settings->registerSetting("InstanceDir", "instances");
|
|
m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
|
|
m_settings->registerSetting("IconsDir", "icons");
|
|
|
|
// Editors
|
|
m_settings->registerSetting("JsonEditor", QString());
|
|
|
|
// Language
|
|
m_settings->registerSetting("Language", QString());
|
|
|
|
// Console
|
|
m_settings->registerSetting("ShowConsole", false);
|
|
m_settings->registerSetting("AutoCloseConsole", false);
|
|
m_settings->registerSetting("ShowConsoleOnError", true);
|
|
m_settings->registerSetting("LogPrePostOutput", true);
|
|
|
|
// Window Size
|
|
m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false);
|
|
m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854);
|
|
m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
|
|
|
|
// Proxy Settings
|
|
m_settings->registerSetting("ProxyType", "None");
|
|
m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1");
|
|
m_settings->registerSetting("ProxyPort", 8080);
|
|
m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, "");
|
|
m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, "");
|
|
|
|
// Memory
|
|
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
|
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
|
|
m_settings->registerSetting("PermGen", 128);
|
|
|
|
// Java Settings
|
|
m_settings->registerSetting("JavaPath", "");
|
|
m_settings->registerSetting("JavaTimestamp", 0);
|
|
m_settings->registerSetting("JavaArchitecture", "");
|
|
m_settings->registerSetting("JavaVersion", "");
|
|
m_settings->registerSetting("JavaVendor", "");
|
|
m_settings->registerSetting("LastHostname", "");
|
|
m_settings->registerSetting("JvmArgs", "");
|
|
|
|
// Native library workarounds
|
|
m_settings->registerSetting("UseNativeOpenAL", false);
|
|
m_settings->registerSetting("UseNativeGLFW", false);
|
|
|
|
// Game time
|
|
m_settings->registerSetting("ShowGameTime", true);
|
|
m_settings->registerSetting("RecordGameTime", true);
|
|
|
|
// Minecraft launch method
|
|
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
|
|
|
|
// Wrapper command for launch
|
|
m_settings->registerSetting("WrapperCommand", "");
|
|
|
|
// Custom Commands
|
|
m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
|
|
m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
|
|
|
|
// The cat
|
|
m_settings->registerSetting("TheCat", false);
|
|
|
|
m_settings->registerSetting("InstSortMode", "Name");
|
|
m_settings->registerSetting("SelectedInstance", QString());
|
|
|
|
// Window state and geometry
|
|
m_settings->registerSetting("MainWindowState", "");
|
|
m_settings->registerSetting("MainWindowGeometry", "");
|
|
|
|
m_settings->registerSetting("ConsoleWindowState", "");
|
|
m_settings->registerSetting("ConsoleWindowGeometry", "");
|
|
|
|
m_settings->registerSetting("SettingsGeometry", "");
|
|
|
|
m_settings->registerSetting("PagedGeometry", "");
|
|
|
|
m_settings->registerSetting("NewInstanceGeometry", "");
|
|
|
|
m_settings->registerSetting("UpdateDialogGeometry", "");
|
|
|
|
// paste.ee API key
|
|
m_settings->registerSetting("PasteEEAPIKey", "multimc");
|
|
|
|
if(!BuildConfig.ANALYTICS_ID.isEmpty())
|
|
{
|
|
// Analytics
|
|
m_settings->registerSetting("Analytics", true);
|
|
m_settings->registerSetting("AnalyticsSeen", 0);
|
|
m_settings->registerSetting("AnalyticsClientID", QString());
|
|
}
|
|
|
|
// Init page provider
|
|
{
|
|
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
|
m_globalSettingsProvider->addPage<MultiMCPage>();
|
|
m_globalSettingsProvider->addPage<MinecraftPage>();
|
|
m_globalSettingsProvider->addPage<JavaPage>();
|
|
m_globalSettingsProvider->addPage<LanguagePage>();
|
|
m_globalSettingsProvider->addPage<CustomCommandsPage>();
|
|
m_globalSettingsProvider->addPage<ProxyPage>();
|
|
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
|
m_globalSettingsProvider->addPage<AccountListPage>();
|
|
m_globalSettingsProvider->addPage<PasteEEPage>();
|
|
}
|
|
qDebug() << "<> Settings loaded.";
|
|
}
|
|
|
|
#ifndef QT_NO_ACCESSIBILITY
|
|
QAccessible::installFactory(groupViewAccessibleFactory);
|
|
#endif /* !QT_NO_ACCESSIBILITY */
|
|
|
|
// load translations
|
|
{
|
|
m_translations.reset(new TranslationsModel("translations"));
|
|
auto bcp47Name = m_settings->get("Language").toString();
|
|
m_translations->selectLanguage(bcp47Name);
|
|
qDebug() << "Your language is" << bcp47Name;
|
|
qDebug() << "<> Translations loaded.";
|
|
}
|
|
|
|
// initialize the updater
|
|
if(BuildConfig.UPDATER_ENABLED)
|
|
{
|
|
m_updateChecker.reset(new UpdateChecker(BuildConfig.CHANLIST_URL, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
|
|
qDebug() << "<> Updater started.";
|
|
}
|
|
|
|
// Instance icons
|
|
{
|
|
auto setting = MMC->settings()->getSetting("IconsDir");
|
|
QStringList instFolders =
|
|
{
|
|
":/icons/multimc/32x32/instances/",
|
|
":/icons/multimc/50x50/instances/",
|
|
":/icons/multimc/128x128/instances/",
|
|
":/icons/multimc/scalable/instances/"
|
|
};
|
|
m_icons.reset(new IconList(instFolders, setting->get().toString()));
|
|
connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value)
|
|
{
|
|
m_icons->directoryChanged(value.toString());
|
|
});
|
|
ENV.registerIconList(m_icons);
|
|
qDebug() << "<> Instance icons intialized.";
|
|
}
|
|
|
|
// Icon themes
|
|
{
|
|
// TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies!
|
|
// set icon theme search path!
|
|
auto searchPaths = QIcon::themeSearchPaths();
|
|
searchPaths.append("iconthemes");
|
|
QIcon::setThemeSearchPaths(searchPaths);
|
|
qDebug() << "<> Icon themes initialized.";
|
|
}
|
|
|
|
// Initialize widget themes
|
|
{
|
|
auto insertTheme = [this](ITheme * theme)
|
|
{
|
|
m_themes.insert(std::make_pair(theme->id(), std::unique_ptr<ITheme>(theme)));
|
|
};
|
|
auto darkTheme = new DarkTheme();
|
|
insertTheme(new SystemTheme());
|
|
insertTheme(darkTheme);
|
|
insertTheme(new BrightTheme());
|
|
insertTheme(new CustomTheme(darkTheme, "custom"));
|
|
qDebug() << "<> Widget themes initialized.";
|
|
}
|
|
|
|
// initialize and load all instances
|
|
{
|
|
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
|
// instance path: check for problems with '!' in instance path and warn the user in the log
|
|
// and remember that we have to show him a dialog when the gui starts (if it does so)
|
|
QString instDir = InstDirSetting->get().toString();
|
|
qDebug() << "Instance path : " << instDir;
|
|
if (FS::checkProblemticPathJava(QDir(instDir)))
|
|
{
|
|
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
|
|
}
|
|
m_instances.reset(new InstanceList(m_settings, instDir, this));
|
|
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
|
|
qDebug() << "Loading Instances...";
|
|
m_instances->loadList();
|
|
qDebug() << "<> Instances loaded.";
|
|
}
|
|
|
|
// and accounts
|
|
{
|
|
m_accounts.reset(new AccountList(this));
|
|
qDebug() << "Loading accounts...";
|
|
m_accounts->setListFilePath("accounts.json", true);
|
|
m_accounts->loadList();
|
|
qDebug() << "<> Accounts loaded.";
|
|
}
|
|
|
|
// init the http meta cache
|
|
{
|
|
ENV.initHttpMetaCache();
|
|
qDebug() << "<> Cache initialized.";
|
|
}
|
|
|
|
// init proxy settings
|
|
{
|
|
QString proxyTypeStr = settings()->get("ProxyType").toString();
|
|
QString addr = settings()->get("ProxyAddr").toString();
|
|
int port = settings()->get("ProxyPort").value<qint16>();
|
|
QString user = settings()->get("ProxyUser").toString();
|
|
QString pass = settings()->get("ProxyPass").toString();
|
|
ENV.updateProxySettings(proxyTypeStr, addr, port, user, pass);
|
|
qDebug() << "<> Proxy settings done.";
|
|
}
|
|
|
|
// now we have network, download translation updates
|
|
m_translations->downloadIndex();
|
|
|
|
//FIXME: what to do with these?
|
|
m_profilers.insert("jprofiler", std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
|
|
m_profilers.insert("jvisualvm", std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
|
|
for (auto profiler : m_profilers.values())
|
|
{
|
|
profiler->registerSettings(m_settings);
|
|
}
|
|
|
|
// Create the MCEdit thing... why is this here?
|
|
{
|
|
m_mcedit.reset(new MCEditTool(m_settings));
|
|
}
|
|
|
|
connect(this, &MultiMC::aboutToQuit, [this](){
|
|
if(m_instances)
|
|
{
|
|
// save any remaining instance state
|
|
m_instances->saveNow();
|
|
}
|
|
if(logFile)
|
|
{
|
|
logFile->flush();
|
|
logFile->close();
|
|
}
|
|
});
|
|
|
|
{
|
|
setIconTheme(settings()->get("IconTheme").toString());
|
|
qDebug() << "<> Icon theme set.";
|
|
setApplicationTheme(settings()->get("ApplicationTheme").toString(), true);
|
|
qDebug() << "<> Application theme set.";
|
|
}
|
|
|
|
// Initialize analytics
|
|
[this]()
|
|
{
|
|
const int analyticsVersion = 2;
|
|
if(BuildConfig.ANALYTICS_ID.isEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto analyticsSetting = m_settings->getSetting("Analytics");
|
|
connect(analyticsSetting.get(), &Setting::SettingChanged, this, &MultiMC::analyticsSettingChanged);
|
|
QString clientID = m_settings->get("AnalyticsClientID").toString();
|
|
if(clientID.isEmpty())
|
|
{
|
|
clientID = QUuid::createUuid().toString();
|
|
clientID.remove(QLatin1Char('{'));
|
|
clientID.remove(QLatin1Char('}'));
|
|
m_settings->set("AnalyticsClientID", clientID);
|
|
}
|
|
m_analytics = new GAnalytics(BuildConfig.ANALYTICS_ID, clientID, analyticsVersion, this);
|
|
m_analytics->setLogLevel(GAnalytics::Debug);
|
|
m_analytics->setAnonymizeIPs(true);
|
|
m_analytics->setNetworkAccessManager(&ENV.qnam());
|
|
|
|
if(m_settings->get("AnalyticsSeen").toInt() < m_analytics->version())
|
|
{
|
|
qDebug() << "Analytics info not seen by user yet (or old version).";
|
|
return;
|
|
}
|
|
if(!m_settings->get("Analytics").toBool())
|
|
{
|
|
qDebug() << "Analytics disabled by user.";
|
|
return;
|
|
}
|
|
|
|
m_analytics->enable();
|
|
qDebug() << "<> Initialized analytics with tid" << BuildConfig.ANALYTICS_ID;
|
|
}();
|
|
|
|
if(createSetupWizard())
|
|
{
|
|
return;
|
|
}
|
|
performMainStartupAction();
|
|
}
|
|
|
|
bool MultiMC::createSetupWizard()
|
|
{
|
|
bool javaRequired = [&]()
|
|
{
|
|
QString currentHostName = QHostInfo::localHostName();
|
|
QString oldHostName = settings()->get("LastHostname").toString();
|
|
if (currentHostName != oldHostName)
|
|
{
|
|
settings()->set("LastHostname", currentHostName);
|
|
return true;
|
|
}
|
|
QString currentJavaPath = settings()->get("JavaPath").toString();
|
|
QString actualPath = FS::ResolveExecutable(currentJavaPath);
|
|
if (actualPath.isNull())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}();
|
|
bool analyticsRequired = [&]()
|
|
{
|
|
if(BuildConfig.ANALYTICS_ID.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
if (!settings()->get("Analytics").toBool())
|
|
{
|
|
return false;
|
|
}
|
|
if (settings()->get("AnalyticsSeen").toInt() < analytics()->version())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}();
|
|
bool languageRequired = [&]()
|
|
{
|
|
if (settings()->get("Language").toString().isEmpty())
|
|
return true;
|
|
return false;
|
|
}();
|
|
bool wizardRequired = javaRequired || analyticsRequired || languageRequired;
|
|
|
|
if(wizardRequired)
|
|
{
|
|
m_setupWizard = new SetupWizard(nullptr);
|
|
if (languageRequired)
|
|
{
|
|
m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
|
|
}
|
|
if (javaRequired)
|
|
{
|
|
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
|
|
}
|
|
if(analyticsRequired)
|
|
{
|
|
m_setupWizard->addPage(new AnalyticsWizardPage(m_setupWizard));
|
|
}
|
|
connect(m_setupWizard, &QDialog::finished, this, &MultiMC::setupWizardFinished);
|
|
m_setupWizard->show();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MultiMC::setupWizardFinished(int status)
|
|
{
|
|
qDebug() << "Wizard result =" << status;
|
|
performMainStartupAction();
|
|
}
|
|
|
|
void MultiMC::performMainStartupAction()
|
|
{
|
|
m_status = MultiMC::Initialized;
|
|
if(!m_instanceIdToLaunch.isEmpty())
|
|
{
|
|
auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
|
|
if(inst)
|
|
{
|
|
MinecraftServerTargetPtr serverToJoin = nullptr;
|
|
|
|
if(!m_serverToJoin.isEmpty())
|
|
{
|
|
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin)));
|
|
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching with server" << m_serverToJoin;
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
|
|
}
|
|
|
|
launch(inst, true, nullptr, serverToJoin);
|
|
return;
|
|
}
|
|
}
|
|
if(!m_mainWindow)
|
|
{
|
|
// normal main window
|
|
showMainWindow(false);
|
|
qDebug() << "<> Main window shown.";
|
|
}
|
|
if(!m_zipToImport.isEmpty())
|
|
{
|
|
qDebug() << "<> Importing instance from zip:" << m_zipToImport;
|
|
m_mainWindow->droppedURLs({ m_zipToImport });
|
|
}
|
|
}
|
|
|
|
void MultiMC::showFatalErrorMessage(const QString& title, const QString& content)
|
|
{
|
|
m_status = MultiMC::Failed;
|
|
auto dialog = CustomMessageBox::selectable(nullptr, title, content, QMessageBox::Critical);
|
|
dialog->exec();
|
|
}
|
|
|
|
MultiMC::~MultiMC()
|
|
{
|
|
// kill the other globals.
|
|
Env::dispose();
|
|
|
|
// Shut down logger by setting the logger function to nothing
|
|
qInstallMessageHandler(nullptr);
|
|
|
|
#if defined Q_OS_WIN32
|
|
// Detach from Windows console
|
|
if(consoleAttached)
|
|
{
|
|
fclose(stdout);
|
|
fclose(stdin);
|
|
fclose(stderr);
|
|
FreeConsole();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MultiMC::messageReceived(const QString& message)
|
|
{
|
|
if(status() != Initialized)
|
|
{
|
|
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
|
|
return;
|
|
}
|
|
|
|
QString command = message.section(' ', 0, 0);
|
|
|
|
if(command == "activate")
|
|
{
|
|
showMainWindow();
|
|
}
|
|
else if(command == "import")
|
|
{
|
|
QString arg = message.section(' ', 1);
|
|
if(arg.isEmpty())
|
|
{
|
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
|
return;
|
|
}
|
|
m_mainWindow->droppedURLs({ QUrl(arg) });
|
|
}
|
|
else if(command == "launch")
|
|
{
|
|
QString arg = message.section(' ', 1);
|
|
if(arg.isEmpty())
|
|
{
|
|
qWarning() << "Received" << command << "message without an instance ID.";
|
|
return;
|
|
}
|
|
auto inst = instances()->getInstanceById(arg);
|
|
if(inst)
|
|
{
|
|
launch(inst, true, nullptr);
|
|
}
|
|
}
|
|
else if(command == "launch-with-server")
|
|
{
|
|
QString instanceID = message.section(' ', 1, 1);
|
|
QString serverToJoin = message.section(' ', 2, 2);
|
|
if(instanceID.isEmpty())
|
|
{
|
|
qWarning() << "Received" << command << "message without an instance ID.";
|
|
return;
|
|
}
|
|
if(serverToJoin.isEmpty())
|
|
{
|
|
qWarning() << "Received" << command << "message without a server to join.";
|
|
return;
|
|
}
|
|
auto inst = instances()->getInstanceById(instanceID);
|
|
if(inst)
|
|
{
|
|
launch(
|
|
inst,
|
|
true,
|
|
nullptr,
|
|
std::make_shared<MinecraftServerTarget>(MinecraftServerTarget::parse(serverToJoin))
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "Received invalid message" << message;
|
|
}
|
|
}
|
|
|
|
void MultiMC::analyticsSettingChanged(const Setting&, QVariant value)
|
|
{
|
|
if(!m_analytics)
|
|
return;
|
|
bool enabled = value.toBool();
|
|
if(enabled)
|
|
{
|
|
qDebug() << "Analytics enabled by user.";
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Analytics disabled by user.";
|
|
}
|
|
m_analytics->enable(enabled);
|
|
}
|
|
|
|
std::shared_ptr<TranslationsModel> MultiMC::translations()
|
|
{
|
|
return m_translations;
|
|
}
|
|
|
|
std::shared_ptr<JavaInstallList> MultiMC::javalist()
|
|
{
|
|
if (!m_javalist)
|
|
{
|
|
m_javalist.reset(new JavaInstallList());
|
|
}
|
|
return m_javalist;
|
|
}
|
|
|
|
std::vector<ITheme *> MultiMC::getValidApplicationThemes()
|
|
{
|
|
std::vector<ITheme *> ret;
|
|
auto iter = m_themes.cbegin();
|
|
while (iter != m_themes.cend())
|
|
{
|
|
ret.push_back((*iter).second.get());
|
|
iter++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void MultiMC::setApplicationTheme(const QString& name, bool initial)
|
|
{
|
|
auto systemPalette = qApp->palette();
|
|
auto themeIter = m_themes.find(name);
|
|
if(themeIter != m_themes.end())
|
|
{
|
|
auto & theme = (*themeIter).second;
|
|
theme->apply(initial);
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "Tried to set invalid theme:" << name;
|
|
}
|
|
}
|
|
|
|
void MultiMC::setIconTheme(const QString& name)
|
|
{
|
|
XdgIcon::setThemeName(name);
|
|
}
|
|
|
|
QIcon MultiMC::getThemedIcon(const QString& name)
|
|
{
|
|
return XdgIcon::fromTheme(name);
|
|
}
|
|
|
|
bool MultiMC::openJsonEditor(const QString &filename)
|
|
{
|
|
const QString file = QDir::current().absoluteFilePath(filename);
|
|
if (m_settings->get("JsonEditor").toString().isEmpty())
|
|
{
|
|
return DesktopServices::openUrl(QUrl::fromLocalFile(file));
|
|
}
|
|
else
|
|
{
|
|
//return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file);
|
|
return DesktopServices::run(m_settings->get("JsonEditor").toString(), {file});
|
|
}
|
|
}
|
|
|
|
bool MultiMC::launch(
|
|
InstancePtr instance,
|
|
bool online,
|
|
BaseProfilerFactory *profiler,
|
|
MinecraftServerTargetPtr serverToJoin
|
|
) {
|
|
if(m_updateRunning)
|
|
{
|
|
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
|
|
}
|
|
else if(instance->canLaunch())
|
|
{
|
|
auto & extras = m_instanceExtras[instance->id()];
|
|
auto & window = extras.window;
|
|
if(window)
|
|
{
|
|
if(!window->saveAll())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
auto & controller = extras.controller;
|
|
controller.reset(new LaunchController());
|
|
controller->setInstance(instance);
|
|
controller->setOnline(online);
|
|
controller->setProfiler(profiler);
|
|
controller->setServerToJoin(serverToJoin);
|
|
if(window)
|
|
{
|
|
controller->setParentWidget(window);
|
|
}
|
|
else if(m_mainWindow)
|
|
{
|
|
controller->setParentWidget(m_mainWindow);
|
|
}
|
|
connect(controller.get(), &LaunchController::succeeded, this, &MultiMC::controllerSucceeded);
|
|
connect(controller.get(), &LaunchController::failed, this, &MultiMC::controllerFailed);
|
|
addRunningInstance();
|
|
controller->start();
|
|
return true;
|
|
}
|
|
else if (instance->isRunning())
|
|
{
|
|
showInstanceWindow(instance, "console");
|
|
return true;
|
|
}
|
|
else if (instance->canEdit())
|
|
{
|
|
showInstanceWindow(instance);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MultiMC::kill(InstancePtr instance)
|
|
{
|
|
if (!instance->isRunning())
|
|
{
|
|
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
|
|
return false;
|
|
}
|
|
auto & extras = m_instanceExtras[instance->id()];
|
|
// NOTE: copy of the shared pointer keeps it alive
|
|
auto controller = extras.controller;
|
|
if(controller)
|
|
{
|
|
return controller->abort();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MultiMC::addRunningInstance()
|
|
{
|
|
m_runningInstances ++;
|
|
if(m_runningInstances == 1)
|
|
{
|
|
emit updateAllowedChanged(false);
|
|
}
|
|
}
|
|
|
|
void MultiMC::subRunningInstance()
|
|
{
|
|
if(m_runningInstances == 0)
|
|
{
|
|
qCritical() << "Something went really wrong and we now have less than 0 running instances... WTF";
|
|
return;
|
|
}
|
|
m_runningInstances --;
|
|
if(m_runningInstances == 0)
|
|
{
|
|
emit updateAllowedChanged(true);
|
|
}
|
|
}
|
|
|
|
bool MultiMC::shouldExitNow() const
|
|
{
|
|
return m_runningInstances == 0 && m_openWindows == 0;
|
|
}
|
|
|
|
bool MultiMC::updatesAreAllowed()
|
|
{
|
|
return m_runningInstances == 0;
|
|
}
|
|
|
|
void MultiMC::updateIsRunning(bool running)
|
|
{
|
|
m_updateRunning = running;
|
|
}
|
|
|
|
|
|
void MultiMC::controllerSucceeded()
|
|
{
|
|
auto controller = qobject_cast<LaunchController *>(QObject::sender());
|
|
if(!controller)
|
|
return;
|
|
auto id = controller->id();
|
|
auto & extras = m_instanceExtras[id];
|
|
|
|
// on success, do...
|
|
if (controller->instance()->settings()->get("AutoCloseConsole").toBool())
|
|
{
|
|
if(extras.window)
|
|
{
|
|
extras.window->close();
|
|
}
|
|
}
|
|
extras.controller.reset();
|
|
subRunningInstance();
|
|
|
|
// quit when there are no more windows.
|
|
if(shouldExitNow())
|
|
{
|
|
m_status = Status::Succeeded;
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
void MultiMC::controllerFailed(const QString& error)
|
|
{
|
|
Q_UNUSED(error);
|
|
auto controller = qobject_cast<LaunchController *>(QObject::sender());
|
|
if(!controller)
|
|
return;
|
|
auto id = controller->id();
|
|
auto & extras = m_instanceExtras[id];
|
|
|
|
// on failure, do... nothing
|
|
extras.controller.reset();
|
|
subRunningInstance();
|
|
|
|
// quit when there are no more windows.
|
|
if(shouldExitNow())
|
|
{
|
|
m_status = Status::Failed;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void MultiMC::ShowGlobalSettings(class QWidget* parent, QString open_page)
|
|
{
|
|
if(!m_globalSettingsProvider) {
|
|
return;
|
|
}
|
|
emit globalSettingsAboutToOpen();
|
|
{
|
|
SettingsObject::Lock lock(MMC->settings());
|
|
PageDialog dlg(m_globalSettingsProvider.get(), open_page, parent);
|
|
dlg.exec();
|
|
}
|
|
emit globalSettingsClosed();
|
|
}
|
|
|
|
MainWindow* MultiMC::showMainWindow(bool minimized)
|
|
{
|
|
if(m_mainWindow)
|
|
{
|
|
m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized);
|
|
m_mainWindow->raise();
|
|
m_mainWindow->activateWindow();
|
|
}
|
|
else
|
|
{
|
|
m_mainWindow = new MainWindow();
|
|
m_mainWindow->restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
|
|
m_mainWindow->restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
|
|
if(minimized)
|
|
{
|
|
m_mainWindow->showMinimized();
|
|
}
|
|
else
|
|
{
|
|
m_mainWindow->show();
|
|
}
|
|
|
|
m_mainWindow->checkInstancePathForProblems();
|
|
connect(this, &MultiMC::updateAllowedChanged, m_mainWindow, &MainWindow::updatesAllowedChanged);
|
|
connect(m_mainWindow, &MainWindow::isClosing, this, &MultiMC::on_windowClose);
|
|
m_openWindows++;
|
|
}
|
|
// FIXME: move this somewhere else...
|
|
if(m_analytics)
|
|
{
|
|
auto windowSize = m_mainWindow->size();
|
|
auto sizeString = QString("%1x%2").arg(windowSize.width()).arg(windowSize.height());
|
|
qDebug() << "Viewport size" << sizeString;
|
|
m_analytics->setViewportSize(sizeString);
|
|
/*
|
|
* cm1 = java min heap [MB]
|
|
* cm2 = java max heap [MB]
|
|
* cm3 = system RAM [MB]
|
|
*
|
|
* cd1 = java version
|
|
* cd2 = java architecture
|
|
* cd3 = system architecture
|
|
* cd4 = CPU architecture
|
|
*/
|
|
QVariantMap customValues;
|
|
int min = m_settings->get("MinMemAlloc").toInt();
|
|
int max = m_settings->get("MaxMemAlloc").toInt();
|
|
if(min < max)
|
|
{
|
|
customValues["cm1"] = min;
|
|
customValues["cm2"] = max;
|
|
}
|
|
else
|
|
{
|
|
customValues["cm1"] = max;
|
|
customValues["cm2"] = min;
|
|
}
|
|
|
|
constexpr uint64_t Mega = 1024ull * 1024ull;
|
|
int ramSize = int(Sys::getSystemRam() / Mega);
|
|
qDebug() << "RAM size is" << ramSize << "MB";
|
|
customValues["cm3"] = ramSize;
|
|
|
|
customValues["cd1"] = m_settings->get("JavaVersion");
|
|
customValues["cd2"] = m_settings->get("JavaArchitecture");
|
|
customValues["cd3"] = Sys::isSystem64bit() ? "64":"32";
|
|
customValues["cd4"] = Sys::isCPU64bit() ? "64":"32";
|
|
auto kernelInfo = Sys::getKernelInfo();
|
|
customValues["cd5"] = kernelInfo.kernelName;
|
|
customValues["cd6"] = kernelInfo.kernelVersion;
|
|
auto distInfo = Sys::getDistributionInfo();
|
|
if(!distInfo.distributionName.isEmpty())
|
|
{
|
|
customValues["cd7"] = distInfo.distributionName;
|
|
}
|
|
if(!distInfo.distributionVersion.isEmpty())
|
|
{
|
|
customValues["cd8"] = distInfo.distributionVersion;
|
|
}
|
|
m_analytics->sendScreenView("Main Window", customValues);
|
|
}
|
|
return m_mainWindow;
|
|
}
|
|
|
|
InstanceWindow *MultiMC::showInstanceWindow(InstancePtr instance, QString page)
|
|
{
|
|
if(!instance)
|
|
return nullptr;
|
|
auto id = instance->id();
|
|
auto & extras = m_instanceExtras[id];
|
|
auto & window = extras.window;
|
|
|
|
if(window)
|
|
{
|
|
window->raise();
|
|
window->activateWindow();
|
|
}
|
|
else
|
|
{
|
|
window = new InstanceWindow(instance);
|
|
m_openWindows ++;
|
|
connect(window, &InstanceWindow::isClosing, this, &MultiMC::on_windowClose);
|
|
}
|
|
if(!page.isEmpty())
|
|
{
|
|
window->selectPage(page);
|
|
}
|
|
if(extras.controller)
|
|
{
|
|
extras.controller->setParentWidget(window);
|
|
}
|
|
return window;
|
|
}
|
|
|
|
void MultiMC::on_windowClose()
|
|
{
|
|
m_openWindows--;
|
|
auto instWindow = qobject_cast<InstanceWindow *>(QObject::sender());
|
|
if(instWindow)
|
|
{
|
|
auto & extras = m_instanceExtras[instWindow->instanceId()];
|
|
extras.window = nullptr;
|
|
if(extras.controller)
|
|
{
|
|
extras.controller->setParentWidget(m_mainWindow);
|
|
}
|
|
}
|
|
auto mainWindow = qobject_cast<MainWindow *>(QObject::sender());
|
|
if(mainWindow)
|
|
{
|
|
m_mainWindow = nullptr;
|
|
}
|
|
// quit when there are no more windows.
|
|
if(shouldExitNow())
|
|
{
|
|
exit(0);
|
|
}
|
|
}
|