GH-1053 move instance update into the launch task (BaseLauncher)

This commit is contained in:
Petr Mrázek 2015-07-04 20:02:43 +02:00
parent 5628d3d379
commit 526a511f45
18 changed files with 303 additions and 262 deletions

View File

@ -53,7 +53,7 @@ private:
BasePage * m_log_page; BasePage * m_log_page;
}; };
ConsoleWindow::ConsoleWindow(BaseLauncher *process, QWidget *parent) ConsoleWindow::ConsoleWindow(std::shared_ptr<BaseLauncher> process, QWidget *parent)
: QMainWindow(parent), m_proc(process) : QMainWindow(parent), m_proc(process)
{ {
MultiMCPlatform::fixWM_CLASS(this); MultiMCPlatform::fixWM_CLASS(this);
@ -129,11 +129,11 @@ ConsoleWindow::ConsoleWindow(BaseLauncher *process, QWidget *parent)
} }
// Set up signal connections // Set up signal connections
connect(m_proc, SIGNAL(ended(InstancePtr, int, QProcess::ExitStatus)), this, connect(m_proc.get(), SIGNAL(ended(InstancePtr, int, QProcess::ExitStatus)), this,
SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus))); SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
connect(m_proc, SIGNAL(prelaunch_failed(InstancePtr, int, QProcess::ExitStatus)), this, connect(m_proc.get(), SIGNAL(prelaunch_failed(InstancePtr, int, QProcess::ExitStatus)), this,
SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus))); SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
connect(m_proc, SIGNAL(launch_failed(InstancePtr)), this, connect(m_proc.get(), SIGNAL(launch_failed(InstancePtr)), this,
SLOT(onLaunchFailed(InstancePtr))); SLOT(onLaunchFailed(InstancePtr)));
setMayClose(false); setMayClose(false);

View File

@ -26,7 +26,7 @@ class ConsoleWindow : public QMainWindow
Q_OBJECT Q_OBJECT
public: public:
explicit ConsoleWindow(BaseLauncher *proc, QWidget *parent = 0); explicit ConsoleWindow(std::shared_ptr<BaseLauncher> proc, QWidget *parent = 0);
virtual ~ConsoleWindow(); virtual ~ConsoleWindow();
/** /**
@ -56,7 +56,7 @@ protected:
void closeEvent(QCloseEvent *); void closeEvent(QCloseEvent *);
private: private:
BaseLauncher *m_proc = nullptr; std::shared_ptr<BaseLauncher> m_proc;
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

@ -1699,39 +1699,14 @@ void MainWindow::doLaunch(bool online, BaseProfilerFactory *profiler)
} }
case AuthSession::PlayableOnline: case AuthSession::PlayableOnline:
{ {
// update first if the server actually responded launchInstance(m_selectedInstance, session, profiler);
if (session->auth_server_online)
{
updateInstance(m_selectedInstance, session, profiler);
}
else
{
launchInstance(m_selectedInstance, session, profiler);
}
tryagain = false; tryagain = false;
} }
} }
} }
} }
void MainWindow::updateInstance(InstancePtr instance, AuthSessionPtr session, void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session, BaseProfilerFactory *profiler)
BaseProfilerFactory *profiler)
{
auto updateTask = instance->doUpdate();
if (!updateTask)
{
launchInstance(instance, session, profiler);
return;
}
ProgressDialog tDialog(this);
connect(updateTask.get(), &Task::succeeded, [this, instance, session, profiler]
{ launchInstance(instance, session, profiler); });
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
tDialog.exec(updateTask.get());
}
void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session,
BaseProfilerFactory *profiler)
{ {
Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL");
Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL"); Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL");
@ -1744,35 +1719,43 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session,
return; return;
} }
BaseLauncher *proc = instance->prepareForLaunch(session); auto proc = instance->prepareForLaunch(session);
if (!proc) if (!proc)
return; return;
proc->setProfiler(profiler);
this->hide(); this->hide();
console = new ConsoleWindow(proc); console = new ConsoleWindow(proc);
connect(console, &ConsoleWindow::isClosing, this, &MainWindow::instanceEnded); connect(console, &ConsoleWindow::isClosing, this, &MainWindow::instanceEnded);
connect(proc.get(), &BaseLauncher::readyForLaunch, this, &MainWindow::readyForLaunch);
proc->setHeader("MultiMC version: " + BuildConfig.printableVersionString() + "\n\n"); proc->setHeader("MultiMC version: " + BuildConfig.printableVersionString() + "\n\n");
proc->arm(); proc->start();
}
void MainWindow::readyForLaunch(std::shared_ptr<BaseLauncher> launcher)
{
auto profiler = launcher->getProfiler();
if (!profiler) if (!profiler)
{ {
proc->launch(); launcher->launch();
return; return;
} }
QString error; QString error;
if (!profiler->check(&error)) if (!profiler->check(&error))
{ {
proc->abort(); launcher->abort();
QMessageBox::critical(this, tr("Error"), tr("Couldn't start profiler: %1").arg(error)); QMessageBox::critical(this, tr("Error"), tr("Couldn't start profiler: %1").arg(error));
return; return;
} }
BaseProfiler *profilerInstance = profiler->createProfiler(instance, this); BaseProfiler *profilerInstance = profiler->createProfiler(launcher->instance(), this);
connect(profilerInstance, &BaseProfiler::readyToLaunch, connect(profilerInstance, &BaseProfiler::readyToLaunch,
[this, proc](const QString & message) [this, launcher](const QString & message)
{ {
QMessageBox msg; QMessageBox msg;
msg.setText(tr("The game launch is delayed until you press the " msg.setText(tr("The game launch is delayed until you press the "
@ -1783,10 +1766,10 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session,
msg.addButton(tr("Launch"), QMessageBox::AcceptRole); msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
msg.setModal(true); msg.setModal(true);
msg.exec(); msg.exec();
proc->launch(); launcher->launch();
}); });
connect(profilerInstance, &BaseProfiler::abortLaunch, connect(profilerInstance, &BaseProfiler::abortLaunch,
[this, proc](const QString & message) [this, launcher](const QString & message)
{ {
QMessageBox msg; QMessageBox msg;
msg.setText(tr("Couldn't start the profiler: %1").arg(message)); msg.setText(tr("Couldn't start the profiler: %1").arg(message));
@ -1795,17 +1778,17 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session,
msg.addButton(QMessageBox::Ok); msg.addButton(QMessageBox::Ok);
msg.setModal(true); msg.setModal(true);
msg.exec(); msg.exec();
proc->abort(); launcher->abort();
}); });
profilerInstance->beginProfiling(proc); profilerInstance->beginProfiling(launcher);
} }
/*
void MainWindow::onGameUpdateError(QString error) void MainWindow::onGameUpdateError(QString error)
{ {
CustomMessageBox::selectable(this, tr("Error updating instance"), error, CustomMessageBox::selectable(this, tr("Error updating instance"), error,
QMessageBox::Warning)->show(); QMessageBox::Warning)->show();
} }
*/
void MainWindow::taskStart() void MainWindow::taskStart()
{ {
// Nothing to do here yet. // Nothing to do here yet.

View File

@ -128,12 +128,7 @@ slots:
*/ */
void launchInstance(InstancePtr instance, AuthSessionPtr session, BaseProfilerFactory *profiler = 0); void launchInstance(InstancePtr instance, AuthSessionPtr session, BaseProfilerFactory *profiler = 0);
/*! void readyForLaunch(std::shared_ptr<BaseLauncher>);
* Prepares the given instance for launch with the given account.
*/
void updateInstance(InstancePtr instance, AuthSessionPtr account, BaseProfilerFactory *profiler = 0);
void onGameUpdateError(QString error);
void taskStart(); void taskStart();
void taskEnd(); void taskEnd();
@ -196,7 +191,6 @@ private:
class GroupView *view; class GroupView *view;
InstanceProxyModel *proxymodel; InstanceProxyModel *proxymodel;
NetJobPtr skin_download_job; NetJobPtr skin_download_job;
MinecraftLauncher *proc;
ConsoleWindow *console; ConsoleWindow *console;
LabeledToolButton *renameButton; LabeledToolButton *renameButton;
QToolButton *changeIconButton; QToolButton *changeIconButton;

View File

@ -11,12 +11,12 @@
#include <settings/Setting.h> #include <settings/Setting.h>
#include "GuiUtil.h" #include "GuiUtil.h"
LogPage::LogPage(BaseLauncher *proc, QWidget *parent) LogPage::LogPage(std::shared_ptr<BaseLauncher> 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);
ui->tabWidget->tabBar()->hide(); ui->tabWidget->tabBar()->hide();
connect(m_process, SIGNAL(log(QString, MessageLevel::Enum)), this, connect(m_process.get(), SIGNAL(log(QString, MessageLevel::Enum)), this,
SLOT(write(QString, MessageLevel::Enum))); SLOT(write(QString, MessageLevel::Enum)));
// create the format and set its font // create the format and set its font

View File

@ -33,7 +33,7 @@ class LogPage : public QWidget, public BasePage
Q_OBJECT Q_OBJECT
public: public:
explicit LogPage(BaseLauncher *proc, QWidget *parent = 0); explicit LogPage(std::shared_ptr<BaseLauncher> proc, QWidget *parent = 0);
virtual ~LogPage(); virtual ~LogPage();
virtual QString displayName() const override virtual QString displayName() const override
{ {
@ -77,7 +77,7 @@ private slots:
private: private:
Ui::LogPage *ui; Ui::LogPage *ui;
BaseLauncher *m_process; std::shared_ptr<BaseLauncher> 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

@ -138,7 +138,7 @@ public:
virtual std::shared_ptr<Task> doUpdate() = 0; virtual std::shared_ptr<Task> doUpdate() = 0;
/// returns a valid process, ready for launch with the given account. /// returns a valid process, ready for launch with the given account.
virtual BaseLauncher *prepareForLaunch(AuthSessionPtr account) = 0; virtual std::shared_ptr<BaseLauncher> 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

@ -19,6 +19,7 @@
#include "MessageLevel.h" #include "MessageLevel.h"
#include "MMCStrings.h" #include "MMCStrings.h"
#include "java/JavaChecker.h" #include "java/JavaChecker.h"
#include "tasks/Task.h"
#include <pathutils.h> #include <pathutils.h>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
@ -29,14 +30,108 @@
#define IBUS "@im=ibus" #define IBUS "@im=ibus"
BaseLauncher* BaseLauncher::create(MinecraftInstancePtr inst) void BaseLauncher::initializeEnvironment()
{ {
auto proc = new BaseLauncher(inst); // prepare the process environment
QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
QStringList ignored =
{
"JAVA_ARGS",
"CLASSPATH",
"CONFIGPATH",
"JAVA_HOME",
"JRE_HOME",
"_JAVA_OPTIONS",
"JAVA_OPTIONS",
"JAVA_TOOL_OPTIONS"
};
for(auto key: rawenv.keys())
{
auto value = rawenv.value(key);
// filter out dangerous java crap
if(ignored.contains(key))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// filter MultiMC-related things
if(key.startsWith("QT_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
#ifdef Q_OS_LINUX
// Do not pass LD_* variables to java. They were intended for MultiMC
if(key.startsWith("LD_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// Strip IBus
// IBus is a Linux IME framework. For some reason, it breaks MC?
if (key == "XMODIFIERS" && value.contains(IBUS))
{
QString save = value;
value.replace(IBUS, "");
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
}
if(key == "GAME_PRELOAD")
{
m_env.insert("LD_PRELOAD", value);
continue;
}
if(key == "GAME_LIBRARY_PATH")
{
m_env.insert("LD_LIBRARY_PATH", value);
continue;
}
#endif
qDebug() << "Env: " << key << value;
m_env.insert(key, value);
}
#ifdef Q_OS_LINUX
// HACK: Workaround for QTBUG42500
if(!m_env.contains("LD_LIBRARY_PATH"))
{
m_env.insert("LD_LIBRARY_PATH", "");
}
#endif
// export some infos
auto variables = getVariables();
for (auto it = variables.begin(); it != variables.end(); ++it)
{
m_env.insert(it.key(), it.value());
}
}
void BaseLauncher::init()
{
initializeEnvironment();
m_process.setProcessEnvironment(m_env);
connect(&m_process, &LoggedProcess::log, this, &BaseLauncher::on_log);
connect(&m_process, &LoggedProcess::stateChanged, this, &BaseLauncher::on_state);
m_prelaunchprocess.setProcessEnvironment(m_env);
connect(&m_prelaunchprocess, &LoggedProcess::log, this, &BaseLauncher::on_log);
connect(&m_prelaunchprocess, &LoggedProcess::stateChanged, this, &BaseLauncher::on_pre_state);
m_postlaunchprocess.setProcessEnvironment(m_env);
connect(&m_postlaunchprocess, &LoggedProcess::log, this, &BaseLauncher::on_log);
connect(&m_postlaunchprocess, &LoggedProcess::stateChanged, this, &BaseLauncher::on_post_state);
m_instance->setRunning(true);
}
std::shared_ptr<BaseLauncher> BaseLauncher::create(MinecraftInstancePtr inst)
{
std::shared_ptr<BaseLauncher> proc(new BaseLauncher(inst));
proc->init(); proc->init();
return proc; return proc;
} }
BaseLauncher::BaseLauncher(InstancePtr instance): m_instance(instance) BaseLauncher::BaseLauncher(InstancePtr instance): m_instance(instance)
{ {
} }
@ -158,126 +253,68 @@ QStringList BaseLauncher::javaArguments() const
return args; return args;
} }
bool BaseLauncher::checkJava(QString JavaPath) void BaseLauncher::checkJava()
{ {
auto realJavaPath = QStandardPaths::findExecutable(JavaPath); m_javaPath = m_instance->settings()->get("JavaPath").toString();
emit log("Java path is:\n" + m_javaPath + "\n\n");
auto realJavaPath = QStandardPaths::findExecutable(m_javaPath);
if (realJavaPath.isEmpty()) if (realJavaPath.isEmpty())
{ {
emit log(tr("The java binary \"%1\" couldn't be found. You may have to set up java " 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), "if Minecraft fails to launch.").arg(m_javaPath),
MessageLevel::Warning); MessageLevel::Warning);
} }
QFileInfo javaInfo(realJavaPath); QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = m_instance->settings()->get("JavaTimestamp").toLongLong(); auto storedUnixTime = m_instance->settings()->get("JavaTimestamp").toLongLong();
this->m_javaUnixTime = javaUnixTime;
// if they are not the same, check! // if they are not the same, check!
if(javaUnixTime != storedUnixTime) if(javaUnixTime != storedUnixTime)
{ {
QEventLoop ev; m_JavaChecker = std::make_shared<JavaChecker>();
auto checker = std::make_shared<JavaChecker>();
bool successful = false; bool successful = false;
QString errorLog; QString errorLog;
QString version; QString version;
emit log(tr("Checking Java version..."), MessageLevel::MultiMC); emit log(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(checker.get(), &JavaChecker::checkFinished, connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &BaseLauncher::checkJavaFinished);
[&](JavaCheckResult result) m_JavaChecker->m_path = realJavaPath;
{ m_JavaChecker->performCheck();
successful = result.valid;
errorLog = result.errorLog;
version = result.javaVersion;
ev.exit();
});
checker->m_path = realJavaPath;
checker->performCheck();
ev.exec();
if(!successful)
{
// Error message displayed if java can't start
emit log(tr("Could not start java:"), MessageLevel::Error);
auto lines = errorLog.split('\n');
for(auto line: lines)
{
emit log(line, MessageLevel::Error);
}
emit log("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
m_instance->cleanupAfterRun();
emit launch_failed(m_instance);
// not running, failed
m_instance->setRunning(false);
return false;
}
emit log(tr("Java version is %1!\n").arg(version), MessageLevel::MultiMC);
m_instance->settings()->set("JavaVersion", version);
m_instance->settings()->set("JavaTimestamp", javaUnixTime);
} }
return true; preLaunch();
} }
void BaseLauncher::arm() void BaseLauncher::checkJavaFinished(JavaCheckResult result)
{ {
printHeader(); if(!result.valid)
emit log("Minecraft folder is:\n" + m_process.workingDirectory() + "\n\n");
/*
if (!preLaunch())
{ {
emit ended(m_instance, 1, QProcess::CrashExit); // Error message displayed if java can't start
return; emit log(tr("Could not start java:"), MessageLevel::Error);
} auto lines = result.errorLog.split('\n');
*/ for(auto line: lines)
m_instance->setLastLaunch();
QString JavaPath = m_instance->settings()->get("JavaPath").toString();
emit log("Java path is:\n" + JavaPath + "\n\n");
if(!checkJava(JavaPath))
{
return;
}
QStringList args = javaArguments();
QString allArgs = args.join(", ");
emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n");
QString wrapperCommand = m_instance->settings()->get("WrapperCommand").toString();
if(!wrapperCommand.isEmpty())
{
auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
if (realWrapperCommand.isEmpty())
{ {
emit log(tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand), MessageLevel::Warning); emit log(line, MessageLevel::Error);
m_instance->cleanupAfterRun();
emit launch_failed(m_instance);
m_instance->setRunning(false);
return;
} }
emit log("Wrapper command is:\n" + wrapperCommand + "\n\n"); emit log("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC);
args.prepend(JavaPath);
m_process.start(wrapperCommand, args);
}
else
{
m_process.start(JavaPath, args);
}
// instantiate the launcher part
if (!m_process.waitForStarted())
{
//: Error message displayed if instace can't start
emit log(tr("Could not launch minecraft!"), MessageLevel::Error);
m_instance->cleanupAfterRun(); m_instance->cleanupAfterRun();
emit launch_failed(m_instance); emit launch_failed(m_instance);
// not running, failed // not running, failed
m_instance->setRunning(false); m_instance->setRunning(false);
return; return;
} }
emit log(tr("Java version is %1!\n").arg(result.javaVersion), MessageLevel::MultiMC);
m_instance->settings()->set("JavaVersion", result.javaVersion);
m_instance->settings()->set("JavaTimestamp", m_javaUnixTime);
preLaunch();
}
emit log(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC); void BaseLauncher::executeTask()
{
printHeader();
emit log("Minecraft folder is:\n" + m_process.workingDirectory() + "\n\n");
// send the launch script to the launcher part checkJava();
m_process.write(launchScript.toUtf8());
} }
void BaseLauncher::launch() void BaseLauncher::launch()
@ -293,103 +330,6 @@ void BaseLauncher::abort()
} }
void BaseLauncher::initializeEnvironment()
{
// prepare the process environment
QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment();
QStringList ignored =
{
"JAVA_ARGS",
"CLASSPATH",
"CONFIGPATH",
"JAVA_HOME",
"JRE_HOME",
"_JAVA_OPTIONS",
"JAVA_OPTIONS",
"JAVA_TOOL_OPTIONS"
};
for(auto key: rawenv.keys())
{
auto value = rawenv.value(key);
// filter out dangerous java crap
if(ignored.contains(key))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// filter MultiMC-related things
if(key.startsWith("QT_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
#ifdef Q_OS_LINUX
// Do not pass LD_* variables to java. They were intended for MultiMC
if(key.startsWith("LD_"))
{
qDebug() << "Env: ignoring" << key << value;
continue;
}
// Strip IBus
// IBus is a Linux IME framework. For some reason, it breaks MC?
if (key == "XMODIFIERS" && value.contains(IBUS))
{
QString save = value;
value.replace(IBUS, "");
qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value;
}
if(key == "GAME_PRELOAD")
{
m_env.insert("LD_PRELOAD", value);
continue;
}
if(key == "GAME_LIBRARY_PATH")
{
m_env.insert("LD_LIBRARY_PATH", value);
continue;
}
#endif
qDebug() << "Env: " << key << value;
m_env.insert(key, value);
}
#ifdef Q_OS_LINUX
// HACK: Workaround for QTBUG42500
if(!m_env.contains("LD_LIBRARY_PATH"))
{
m_env.insert("LD_LIBRARY_PATH", "");
}
#endif
// export some infos
auto variables = getVariables();
for (auto it = variables.begin(); it != variables.end(); ++it)
{
m_env.insert(it.key(), it.value());
}
}
void BaseLauncher::init()
{
initializeEnvironment();
m_process.setProcessEnvironment(m_env);
connect(&m_process, &LoggedProcess::log, this, &BaseLauncher::on_log);
connect(&m_process, &LoggedProcess::stateChanged, this, &BaseLauncher::on_state);
m_prelaunchprocess.setProcessEnvironment(m_env);
connect(&m_prelaunchprocess, &LoggedProcess::log, this, &BaseLauncher::on_log);
connect(&m_prelaunchprocess, &LoggedProcess::stateChanged, this, &BaseLauncher::on_pre_state);
m_postlaunchprocess.setProcessEnvironment(m_env);
connect(&m_postlaunchprocess, &LoggedProcess::log, this, &BaseLauncher::on_log);
connect(&m_postlaunchprocess, &LoggedProcess::stateChanged, this, &BaseLauncher::on_post_state);
// a process has been constructed for the instance. It is running from MultiMC POV
m_instance->setRunning(true);
}
void BaseLauncher::setWorkdir(QString path) void BaseLauncher::setWorkdir(QString path)
{ {
QDir mcDir(path); QDir mcDir(path);
@ -468,18 +408,98 @@ void BaseLauncher::on_pre_state(LoggedProcess::State state)
emit prelaunch_failed(m_instance, m_prelaunchprocess.exitCode(), m_prelaunchprocess.exitStatus()); emit prelaunch_failed(m_instance, m_prelaunchprocess.exitCode(), m_prelaunchprocess.exitStatus());
// not running, failed // not running, failed
m_instance->setRunning(false); m_instance->setRunning(false);
return;
} }
case LoggedProcess::Finished: case LoggedProcess::Finished:
{ {
emit log(tr("Pre-Launch command ran successfully.\n\n")); emit log(tr("Pre-Launch command ran successfully.\n\n"));
m_instance->reload();
} }
case LoggedProcess::Skipped: case LoggedProcess::Skipped:
{
m_instance->reload();
updateInstance();
}
default: default:
break; break;
} }
} }
void BaseLauncher::updateInstance()
{
m_updateTask = m_instance->doUpdate();
if(m_updateTask)
{
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
m_updateTask->start();
return;
}
makeReady();
}
void BaseLauncher::updateFinished()
{
if(m_updateTask->successful())
{
makeReady();
}
else
{
QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason());
emit log(reason, MessageLevel::Fatal);
m_instance->cleanupAfterRun();
emit update_failed(m_instance);
emitFailed(reason);
m_instance->setRunning(false);
}
}
void BaseLauncher::makeReady()
{
QStringList args = javaArguments();
QString allArgs = args.join(", ");
emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n");
QString wrapperCommand = m_instance->settings()->get("WrapperCommand").toString();
if(!wrapperCommand.isEmpty())
{
auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
if (realWrapperCommand.isEmpty())
{
emit log(tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand), MessageLevel::Warning);
m_instance->cleanupAfterRun();
emit launch_failed(m_instance);
m_instance->setRunning(false);
return;
}
emit log("Wrapper command is:\n" + wrapperCommand + "\n\n");
args.prepend(m_javaPath);
m_process.start(wrapperCommand, args);
}
else
{
m_process.start(m_javaPath, args);
}
// instantiate the launcher part
if (!m_process.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;
}
emit log(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
// send the launch script to the launcher part
m_process.write(launchScript.toUtf8());
emit readyForLaunch(shared_from_this());
}
void BaseLauncher::on_state(LoggedProcess::State state) void BaseLauncher::on_state(LoggedProcess::State state)
{ {
QProcess::ExitStatus estat = QProcess::NormalExit; QProcess::ExitStatus estat = QProcess::NormalExit;
@ -500,9 +520,14 @@ void BaseLauncher::on_state(LoggedProcess::State state)
// no longer running... // no longer running...
m_instance->setRunning(false); m_instance->setRunning(false);
emit ended(m_instance, exitCode, estat); emit ended(m_instance, exitCode, estat);
break;
} }
case LoggedProcess::Skipped: case LoggedProcess::Skipped:
qWarning() << "Illegal game state: Skipped"; qWarning() << "Illegal game state: Skipped";
break;
case LoggedProcess::Running:
m_instance->setLastLaunch();
break;
default: default:
break; break;
} }

View File

@ -22,8 +22,12 @@
#include "LoggedProcess.h" #include "LoggedProcess.h"
/* HACK: MINECRAFT: split! */ /* HACK: MINECRAFT: split! */
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "java/JavaChecker.h"
#include "QObjectPtr.h"
#include "tasks/Task.h"
class BaseLauncher: public QObject class BaseProfilerFactory;
class BaseLauncher: public Task, public std::enable_shared_from_this<BaseLauncher>
{ {
Q_OBJECT Q_OBJECT
protected: protected:
@ -31,7 +35,7 @@ protected:
void init(); void init();
public: /* methods */ public: /* methods */
static BaseLauncher *create(MinecraftInstancePtr inst); static std::shared_ptr<BaseLauncher> create(MinecraftInstancePtr inst);
virtual ~BaseLauncher() {}; virtual ~BaseLauncher() {};
InstancePtr instance() InstancePtr instance()
@ -47,6 +51,16 @@ public: /* methods */
void setWorkdir(QString path); void setWorkdir(QString path);
BaseProfilerFactory * getProfiler()
{
return m_profiler;
}
void setProfiler(BaseProfilerFactory * profiler)
{
m_profiler = profiler;
}
void killProcess(); void killProcess();
qint64 pid(); qint64 pid();
@ -54,7 +68,7 @@ public: /* methods */
/** /**
* @brief prepare the process for launch (for multi-stage launch) * @brief prepare the process for launch (for multi-stage launch)
*/ */
virtual void arm(); virtual void executeTask() override;
/** /**
* @brief launch the armed instance * @brief launch the armed instance
@ -85,6 +99,8 @@ public: /* HACK: MINECRAFT: split! */
protected: /* methods */ protected: /* methods */
void preLaunch(); void preLaunch();
void updateInstance();
void makeReady();
void postLaunch(); void postLaunch();
QString substituteVariables(const QString &cmd) const; QString substituteVariables(const QString &cmd) const;
void initializeEnvironment(); void initializeEnvironment();
@ -106,6 +122,11 @@ signals:
*/ */
void prelaunch_failed(InstancePtr, int code, QProcess::ExitStatus status); void prelaunch_failed(InstancePtr, int code, QProcess::ExitStatus status);
/**
* @brief emitted when the instance update fails
*/
void update_failed(InstancePtr);
/** /**
* @brief emitted when the PostLaunchCommand fails * @brief emitted when the PostLaunchCommand fails
*/ */
@ -116,6 +137,11 @@ signals:
*/ */
void ended(InstancePtr, int code, QProcess::ExitStatus status); void ended(InstancePtr, int code, QProcess::ExitStatus status);
/**
* @brief emitted when the launch preparations are done
*/
void readyForLaunch(std::shared_ptr<BaseLauncher> launcher);
/** /**
* @brief emitted when we want to log something * @brief emitted when we want to log something
* @param text the text to log * @param text the text to log
@ -132,6 +158,8 @@ protected slots:
void on_state(LoggedProcess::State state); void on_state(LoggedProcess::State state);
void on_post_state(LoggedProcess::State state); void on_post_state(LoggedProcess::State state);
void checkJavaFinished(JavaCheckResult result);
protected: protected:
InstancePtr m_instance; InstancePtr m_instance;
@ -139,16 +167,27 @@ protected:
LoggedProcess m_postlaunchprocess; LoggedProcess m_postlaunchprocess;
LoggedProcess m_process; LoggedProcess m_process;
QProcessEnvironment m_env; QProcessEnvironment m_env;
BaseProfilerFactory * m_profiler = nullptr;
bool killed = false; bool killed = false;
QString m_header; QString m_header;
// for java checker and launch
QString m_javaPath;
qlonglong m_javaUnixTime;
protected: /* HACK: MINECRAFT: split! */ protected: /* HACK: MINECRAFT: split! */
AuthSessionPtr m_session; AuthSessionPtr m_session;
QString launchScript; QString launchScript;
QString m_nativeFolder; QString m_nativeFolder;
std::shared_ptr<JavaChecker> m_JavaChecker;
std::shared_ptr<Task> m_updateTask;
protected: /* HACK: MINECRAFT: split! */ protected: /* HACK: MINECRAFT: split! */
bool checkJava(QString path); void checkJava();
QStringList javaArguments() const; QStringList javaArguments() const;
private slots:
void updateFinished();
}; };
class BaseProfilerFactory;

View File

@ -43,7 +43,7 @@ public:
{ {
return instanceRoot(); return instanceRoot();
}; };
virtual BaseLauncher* prepareForLaunch(AuthSessionPtr) virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr)
{ {
return nullptr; return nullptr;
} }

View File

@ -95,7 +95,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate()
return std::shared_ptr<Task>(new LegacyUpdate(this, this)); return std::shared_ptr<Task>(new LegacyUpdate(this, this));
} }
BaseLauncher *LegacyInstance::prepareForLaunch(AuthSessionPtr account) std::shared_ptr<BaseLauncher> LegacyInstance::prepareForLaunch(AuthSessionPtr account)
{ {
QString launchScript; QString launchScript;
QIcon icon = ENV.icons()->getIcon(iconKey()); QIcon icon = ENV.icons()->getIcon(iconKey());

View File

@ -111,7 +111,7 @@ 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 BaseLauncher *prepareForLaunch(AuthSessionPtr account) override; virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;
virtual QString getStatusbarDescription() override; virtual QString getStatusbarDescription() override;

View File

@ -123,7 +123,7 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts; return parts;
} }
BaseLauncher *OneSixInstance::prepareForLaunch(AuthSessionPtr session) std::shared_ptr<BaseLauncher> OneSixInstance::prepareForLaunch(AuthSessionPtr session)
{ {
QString launchScript; QString launchScript;
QIcon icon = ENV.icons()->getIcon(iconKey()); QIcon icon = ENV.icons()->getIcon(iconKey());

View File

@ -49,7 +49,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 BaseLauncher *prepareForLaunch(AuthSessionPtr account) override; virtual std::shared_ptr<BaseLauncher> prepareForLaunch(AuthSessionPtr account) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;

View File

@ -7,7 +7,7 @@ BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QOb
{ {
} }
void BaseProfiler::beginProfiling(BaseLauncher *process) void BaseProfiler::beginProfiling(std::shared_ptr<BaseLauncher> process)
{ {
beginProfilingImpl(process); beginProfilingImpl(process);
} }

View File

@ -15,13 +15,13 @@ public:
public public
slots: slots:
void beginProfiling(BaseLauncher *process); void beginProfiling(std::shared_ptr<BaseLauncher> process);
void abortProfiling(); void abortProfiling();
protected: protected:
QProcess *m_profilerProcess; QProcess *m_profilerProcess;
virtual void beginProfilingImpl(BaseLauncher *process) = 0; virtual void beginProfilingImpl(std::shared_ptr<BaseLauncher> process) = 0;
virtual void abortProfilingImpl(); virtual void abortProfilingImpl();
signals: signals:

View File

@ -18,7 +18,7 @@ private slots:
void profilerFinished(int exit, QProcess::ExitStatus status); void profilerFinished(int exit, QProcess::ExitStatus status);
protected: protected:
void beginProfilingImpl(BaseLauncher *process); void beginProfilingImpl(std::shared_ptr<BaseLauncher> process);
private: private:
int listeningPort = 0; int listeningPort = 0;
@ -48,7 +48,7 @@ void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status)
} }
} }
void JProfiler::beginProfilingImpl(BaseLauncher *process) void JProfiler::beginProfilingImpl(std::shared_ptr<BaseLauncher> process)
{ {
listeningPort = globalSettings->get("JProfilerPort").toInt(); listeningPort = globalSettings->get("JProfilerPort").toInt();
QProcess *profiler = new QProcess(this); QProcess *profiler = new QProcess(this);

View File

@ -18,7 +18,7 @@ private slots:
void profilerFinished(int exit, QProcess::ExitStatus status); void profilerFinished(int exit, QProcess::ExitStatus status);
protected: protected:
void beginProfilingImpl(BaseLauncher *process); void beginProfilingImpl(std::shared_ptr<BaseLauncher> process);
}; };
@ -45,7 +45,7 @@ void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status)
} }
} }
void JVisualVM::beginProfilingImpl(BaseLauncher *process) void JVisualVM::beginProfilingImpl(std::shared_ptr<BaseLauncher> process)
{ {
QProcess *profiler = new QProcess(this); QProcess *profiler = new QProcess(this);
QStringList profilerArgs = QStringList profilerArgs =