GH-1053 base process and launch refactor, part 1
This commit is contained in:
		| @@ -53,7 +53,7 @@ private: | ||||
| 	BasePage * m_log_page; | ||||
| }; | ||||
|  | ||||
| ConsoleWindow::ConsoleWindow(BaseProcess *process, QWidget *parent) | ||||
| ConsoleWindow::ConsoleWindow(BaseLauncher *process, QWidget *parent) | ||||
| 	: QMainWindow(parent), m_proc(process) | ||||
| { | ||||
| 	MultiMCPlatform::fixWM_CLASS(this); | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
|  | ||||
| #include <QMainWindow> | ||||
| #include <QSystemTrayIcon> | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
|  | ||||
| class QPushButton; | ||||
| class PageContainer; | ||||
| @@ -26,7 +26,7 @@ class ConsoleWindow : public QMainWindow | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
| 	explicit ConsoleWindow(BaseProcess *proc, QWidget *parent = 0); | ||||
| 	explicit ConsoleWindow(BaseLauncher *proc, QWidget *parent = 0); | ||||
| 	virtual ~ConsoleWindow(); | ||||
|  | ||||
| 	/** | ||||
| @@ -47,7 +47,7 @@ slots: | ||||
| 	void onEnded(InstancePtr instance, int code, QProcess::ExitStatus status); | ||||
| 	void onLaunchFailed(InstancePtr instance); | ||||
|  | ||||
| 	// FIXME: add handlers for the other MinecraftProcess signals (pre/post launch command | ||||
| 	// FIXME: add handlers for the other MinecraftLauncher signals (pre/post launch command | ||||
| 	// failures) | ||||
|  | ||||
| 	void iconActivated(QSystemTrayIcon::ActivationReason); | ||||
| @@ -56,7 +56,7 @@ protected: | ||||
| 	void closeEvent(QCloseEvent *); | ||||
|  | ||||
| private: | ||||
| 	BaseProcess *m_proc = nullptr; | ||||
| 	BaseLauncher *m_proc = nullptr; | ||||
| 	bool m_mayclose = true; | ||||
| 	QSystemTrayIcon *m_trayIcon = nullptr; | ||||
| 	PageContainer *m_container = nullptr; | ||||
|   | ||||
| @@ -378,7 +378,7 @@ namespace Ui { | ||||
| #include "Env.h" | ||||
|  | ||||
| #include "BaseInstance.h" | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
| #include "java/JavaUtils.h" | ||||
| #include "JavaCommon.h" | ||||
| #include "InstancePageProvider.h" | ||||
| @@ -1744,7 +1744,7 @@ void MainWindow::launchInstance(InstancePtr instance, AuthSessionPtr session, | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	BaseProcess *proc = instance->prepareForLaunch(session); | ||||
| 	BaseLauncher *proc = instance->prepareForLaunch(session); | ||||
| 	if (!proc) | ||||
| 		return; | ||||
|  | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class QToolButton; | ||||
| class InstanceProxyModel; | ||||
| class LabeledToolButton; | ||||
| class QLabel; | ||||
| class MinecraftProcess; | ||||
| class MinecraftLauncher; | ||||
| class ConsoleWindow; | ||||
| class BaseProfilerFactory; | ||||
| class GenericPageProvider; | ||||
| @@ -196,7 +196,7 @@ private: | ||||
| 	class GroupView *view; | ||||
| 	InstanceProxyModel *proxymodel; | ||||
|     NetJobPtr skin_download_job; | ||||
| 	MinecraftProcess *proc; | ||||
| 	MinecraftLauncher *proc; | ||||
| 	ConsoleWindow *console; | ||||
| 	LabeledToolButton *renameButton; | ||||
| 	QToolButton *changeIconButton; | ||||
|   | ||||
| @@ -7,11 +7,11 @@ | ||||
| #include <QScrollBar> | ||||
| #include <QShortcut> | ||||
|  | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
| #include <settings/Setting.h> | ||||
| #include "GuiUtil.h" | ||||
|  | ||||
| LogPage::LogPage(BaseProcess *proc, QWidget *parent) | ||||
| LogPage::LogPage(BaseLauncher *proc, QWidget *parent) | ||||
| 	: QWidget(parent), ui(new Ui::LogPage), m_process(proc) | ||||
| { | ||||
| 	ui->setupUi(this); | ||||
| @@ -148,14 +148,14 @@ void LogPage::write(QString data, MessageLevel::Enum mode) | ||||
| { | ||||
| 	if (!m_write_active) | ||||
| 	{ | ||||
| 		if (mode != MessageLevel::PrePost && mode != MessageLevel::MultiMC) | ||||
| 		if (mode != MessageLevel::MultiMC) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	if(m_stopOnOverflow && m_write_active) | ||||
| 	{ | ||||
| 		if(mode != MessageLevel::PrePost && mode != MessageLevel::MultiMC) | ||||
| 		if(mode != MessageLevel::MultiMC) | ||||
| 		{ | ||||
| 			if(ui->text->blockCount() >= ui->text->maximumBlockCount()) | ||||
| 			{ | ||||
| @@ -231,11 +231,6 @@ void LogPage::write(QString data, MessageLevel::Enum mode) | ||||
| 			format.setBackground(QColor("black")); | ||||
| 			break; | ||||
| 		} | ||||
| 		case MessageLevel::PrePost: | ||||
| 		{ | ||||
| 			format.setForeground(QColor("grey")); | ||||
| 			break; | ||||
| 		} | ||||
| 		case MessageLevel::Info: | ||||
| 		case MessageLevel::Message: | ||||
| 		default: | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
| #include <QWidget> | ||||
|  | ||||
| #include "BaseInstance.h" | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
| #include "BasePage.h" | ||||
| #include <MultiMC.h> | ||||
|  | ||||
| @@ -33,7 +33,7 @@ class LogPage : public QWidget, public BasePage | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
| 	explicit LogPage(BaseProcess *proc, QWidget *parent = 0); | ||||
| 	explicit LogPage(BaseLauncher *proc, QWidget *parent = 0); | ||||
| 	virtual ~LogPage(); | ||||
| 	virtual QString displayName() const override | ||||
| 	{ | ||||
| @@ -77,7 +77,7 @@ private slots: | ||||
|  | ||||
| private: | ||||
| 	Ui::LogPage *ui; | ||||
| 	BaseProcess *m_process; | ||||
| 	BaseLauncher *m_process; | ||||
| 	int m_last_scroll_value = 0; | ||||
| 	bool m_scroll_active = true; | ||||
| 	int m_saved_offset = 0; | ||||
|   | ||||
| @@ -64,7 +64,7 @@ public class Utils | ||||
| 	 * Log to the MultiMC console | ||||
| 	 * | ||||
| 	 * @param message A String containing the message | ||||
| 	 * @param level   A String containing the level name. See MinecraftProcess::getLevel() | ||||
| 	 * @param level   A String containing the level name. See MinecraftLauncher::getLevel() | ||||
| 	 */ | ||||
| 	public static void log(String message, String level) | ||||
| 	{ | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|  | ||||
| class QDir; | ||||
| class Task; | ||||
| class BaseProcess; | ||||
| class BaseLauncher; | ||||
| class BaseInstance; | ||||
|  | ||||
| // pointer for lazy people | ||||
| @@ -138,7 +138,7 @@ public: | ||||
| 	virtual std::shared_ptr<Task> doUpdate() = 0; | ||||
|  | ||||
| 	/// returns a valid process, ready for launch with the given account. | ||||
| 	virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) = 0; | ||||
| 	virtual BaseLauncher *prepareForLaunch(AuthSessionPtr account) = 0; | ||||
|  | ||||
| 	/// do any necessary cleanups after the instance finishes. also runs before | ||||
| 	/// 'prepareForLaunch' | ||||
|   | ||||
							
								
								
									
										311
									
								
								logic/BaseLauncher.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								logic/BaseLauncher.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,311 @@ | ||||
| /* Copyright 2013-2015 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 "BaseLauncher.h" | ||||
| #include "MessageLevel.h" | ||||
| #include <QDebug> | ||||
| #include <QDir> | ||||
| #include <QEventLoop> | ||||
|  | ||||
| BaseLauncher::BaseLauncher(InstancePtr instance): m_instance(instance) | ||||
| { | ||||
| } | ||||
|  | ||||
| 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 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; | ||||
| 		} | ||||
| #endif | ||||
| 		qDebug() << "Env: " << key << value; | ||||
| 		m_env.insert(key, value); | ||||
| 	} | ||||
| #ifdef LINUX | ||||
| 	// HACK: Workaround for QTBUG-42500 | ||||
| 	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) | ||||
| { | ||||
| 	QDir mcDir(path); | ||||
| 	m_process.setWorkingDirectory(mcDir.absolutePath()); | ||||
| 	m_prelaunchprocess.setWorkingDirectory(mcDir.absolutePath()); | ||||
| 	m_postlaunchprocess.setWorkingDirectory(mcDir.absolutePath()); | ||||
| } | ||||
|  | ||||
| void BaseLauncher::printHeader() | ||||
| { | ||||
| 	emit log(m_header); | ||||
| } | ||||
|  | ||||
| void BaseLauncher::on_log(QStringList lines, MessageLevel::Enum level) | ||||
| { | ||||
| 	logOutput(lines, level); | ||||
| } | ||||
|  | ||||
| void BaseLauncher::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel) | ||||
| { | ||||
| 	for (auto & line: lines) | ||||
| 	{ | ||||
| 		logOutput(line, defaultLevel); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseLauncher::logOutput(QString line, MessageLevel::Enum level) | ||||
| { | ||||
| 	// if the launcher part set a log level, use it | ||||
| 	auto innerLevel = MessageLevel::fromLine(line); | ||||
| 	if(innerLevel != MessageLevel::Unknown) | ||||
| 	{ | ||||
| 		level = innerLevel; | ||||
| 	} | ||||
|  | ||||
| 	// If the level is still undetermined, guess level | ||||
| 	if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) | ||||
| 	{ | ||||
| 		level = this->guessLevel(line, level); | ||||
| 	} | ||||
|  | ||||
| 	// censor private user info | ||||
| 	line = censorPrivateInfo(line); | ||||
|  | ||||
| 	emit log(line, level); | ||||
| } | ||||
|  | ||||
| void BaseLauncher::preLaunch() | ||||
| { | ||||
| 	QString prelaunch_cmd = m_instance->settings()->get("PreLaunchCommand").toString(); | ||||
| 	if (!prelaunch_cmd.isEmpty()) | ||||
| 	{ | ||||
| 		prelaunch_cmd = substituteVariables(prelaunch_cmd); | ||||
| 		// Launch | ||||
| 		emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd)); | ||||
| 		m_prelaunchprocess.start(prelaunch_cmd); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		on_pre_state(LoggedProcess::Skipped); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseLauncher::on_pre_state(LoggedProcess::State state) | ||||
| { | ||||
| 	switch(state) | ||||
| 	{ | ||||
| 		case LoggedProcess::Aborted: | ||||
| 		case LoggedProcess::Crashed: | ||||
| 		case LoggedProcess::FailedToStart: | ||||
| 		{ | ||||
| 			emit log(tr("Pre-Launch command failed with code %1.\n\n") | ||||
| 						 .arg(m_prelaunchprocess.exitCode()), | ||||
| 					 MessageLevel::Fatal); | ||||
| 			m_instance->cleanupAfterRun(); | ||||
| 			emit prelaunch_failed(m_instance, m_prelaunchprocess.exitCode(), m_prelaunchprocess.exitStatus()); | ||||
| 			// not running, failed | ||||
| 			m_instance->setRunning(false); | ||||
| 		} | ||||
| 		case LoggedProcess::Finished: | ||||
| 		{ | ||||
| 			emit log(tr("Pre-Launch command ran successfully.\n\n")); | ||||
| 			m_instance->reload(); | ||||
| 		} | ||||
| 		case LoggedProcess::Skipped: | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseLauncher::on_state(LoggedProcess::State state) | ||||
| { | ||||
| 	QProcess::ExitStatus estat = QProcess::NormalExit; | ||||
| 	switch(state) | ||||
| 	{ | ||||
| 		case LoggedProcess::Aborted: | ||||
| 		case LoggedProcess::Crashed: | ||||
| 		case LoggedProcess::FailedToStart: | ||||
| 			estat = QProcess::CrashExit; | ||||
| 		case LoggedProcess::Finished: | ||||
| 		{ | ||||
| 			auto exitCode = m_process.exitCode(); | ||||
| 			m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); | ||||
|  | ||||
| 			// run post-exit | ||||
| 			postLaunch(); | ||||
| 			m_instance->cleanupAfterRun(); | ||||
| 			// no longer running... | ||||
| 			m_instance->setRunning(false); | ||||
| 			emit ended(m_instance, exitCode, estat); | ||||
| 		} | ||||
| 		case LoggedProcess::Skipped: | ||||
| 			qWarning() << "Illegal game state: Skipped"; | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseLauncher::on_post_state(LoggedProcess::State state) | ||||
| { | ||||
| 	switch(state) | ||||
| 	{ | ||||
| 		case LoggedProcess::Aborted: | ||||
| 		case LoggedProcess::Crashed: | ||||
| 		case LoggedProcess::FailedToStart: | ||||
| 		case LoggedProcess::Finished: | ||||
| 		case LoggedProcess::Skipped: | ||||
| 		{ | ||||
|  | ||||
| 		} | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseLauncher::killProcess() | ||||
| { | ||||
| 	killed = true; | ||||
| 	if (m_prelaunchprocess.state() == LoggedProcess::Running) | ||||
| 	{ | ||||
| 		m_prelaunchprocess.kill(); | ||||
| 	} | ||||
| 	else if(m_process.state() == LoggedProcess::Running) | ||||
| 	{ | ||||
| 		m_process.kill(); | ||||
| 	} | ||||
| 	else if(m_postlaunchprocess.state() == LoggedProcess::Running) | ||||
| 	{ | ||||
| 		m_postlaunchprocess.kill(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseLauncher::postLaunch() | ||||
| { | ||||
| 	QString postlaunch_cmd = m_instance->settings()->get("PostExitCommand").toString(); | ||||
| 	if (!postlaunch_cmd.isEmpty()) | ||||
| 	{ | ||||
| 		postlaunch_cmd = substituteVariables(postlaunch_cmd); | ||||
| 		emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd)); | ||||
| 		m_postlaunchprocess.start(postlaunch_cmd); | ||||
| 		if (m_postlaunchprocess.exitStatus() != QProcess::NormalExit) | ||||
| 		{ | ||||
| 			emit log(tr("Post-Launch command failed with code %1.\n\n") | ||||
| 						 .arg(m_postlaunchprocess.exitCode()), | ||||
| 					 MessageLevel::Error); | ||||
| 			emit postlaunch_failed(m_instance, m_postlaunchprocess.exitCode(), | ||||
| 								   m_postlaunchprocess.exitStatus()); | ||||
| 			// not running, failed | ||||
| 			m_instance->setRunning(false); | ||||
| 		} | ||||
| 		else | ||||
| 			emit log(tr("Post-Launch command ran successfully.\n\n")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QString BaseLauncher::substituteVariables(const QString &cmd) const | ||||
| { | ||||
| 	QString out = cmd; | ||||
| 	auto variables = getVariables(); | ||||
| 	for (auto it = variables.begin(); it != variables.end(); ++it) | ||||
| 	{ | ||||
| 		out.replace("$" + it.key(), it.value()); | ||||
| 	} | ||||
| 	auto env = QProcessEnvironment::systemEnvironment(); | ||||
| 	for (auto var : env.keys()) | ||||
| 	{ | ||||
| 		out.replace("$" + var, env.value(var)); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
|  | ||||
| qint64 BaseLauncher::pid() | ||||
| { | ||||
| #ifdef Q_OS_WIN | ||||
| 	struct _PROCESS_INFORMATION *procinfo = m_process.pid(); | ||||
| 	return procinfo->dwProcessId; | ||||
| #else | ||||
| 	return m_process.pid(); | ||||
| #endif | ||||
| } | ||||
| @@ -18,36 +18,18 @@ | ||||
| #pragma once | ||||
| #include <QProcess> | ||||
| #include "BaseInstance.h" | ||||
| #include "MessageLevel.h" | ||||
| #include "LoggedProcess.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief the MessageLevel Enum | ||||
|  * defines what level a message is | ||||
|  */ | ||||
| namespace MessageLevel | ||||
| { | ||||
| enum Enum | ||||
| { | ||||
| 	MultiMC, /**< MultiMC Messages */ | ||||
| 	Debug,   /**< Debug Messages */ | ||||
| 	Info,    /**< Info Messages */ | ||||
| 	Message, /**< Standard Messages */ | ||||
| 	Warning, /**< Warnings */ | ||||
| 	Error,   /**< Errors */ | ||||
| 	Fatal,   /**< Fatal Errors */ | ||||
| 	PrePost, /**< Pre/Post Launch command output */ | ||||
| }; | ||||
| MessageLevel::Enum getLevel(const QString &levelName); | ||||
| } | ||||
| 
 | ||||
| class BaseProcess: public QProcess | ||||
| class BaseLauncher: public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
| protected: | ||||
| 	explicit BaseProcess(InstancePtr instance); | ||||
| 	explicit BaseLauncher(InstancePtr instance); | ||||
| 	void init(); | ||||
| 
 | ||||
| public: /* methods */ | ||||
| 	virtual ~BaseProcess() {}; | ||||
| 	virtual ~BaseLauncher() {}; | ||||
| 
 | ||||
| 	InstancePtr instance() | ||||
| 	{ | ||||
| @@ -64,6 +46,8 @@ public: /* methods */ | ||||
| 
 | ||||
| 	void killProcess(); | ||||
| 
 | ||||
| 	qint64 pid(); | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * @brief prepare the process for launch (for multi-stage launch) | ||||
| 	 */ | ||||
| @@ -80,10 +64,10 @@ public: /* methods */ | ||||
| 	virtual void abort() = 0; | ||||
| 
 | ||||
| protected: /* methods */ | ||||
| 	bool preLaunch(); | ||||
| 	bool postLaunch(); | ||||
| 	bool waitForPrePost(); | ||||
| 	void preLaunch(); | ||||
| 	void postLaunch(); | ||||
| 	QString substituteVariables(const QString &cmd) const; | ||||
| 	void initializeEnvironment(); | ||||
| 
 | ||||
| 	void printHeader(); | ||||
| 
 | ||||
| @@ -120,23 +104,22 @@ signals: | ||||
| 	void log(QString text, MessageLevel::Enum level = MessageLevel::MultiMC); | ||||
| 
 | ||||
| protected slots: | ||||
| 	void finish(int, QProcess::ExitStatus status); | ||||
| 	void on_stdErr(); | ||||
| 	void on_stdOut(); | ||||
| 	void on_prepost_stdOut(); | ||||
| 	void on_prepost_stdErr(); | ||||
| 	void logOutput(const QStringList &lines, | ||||
| 				   MessageLevel::Enum defaultLevel = MessageLevel::Message, | ||||
| 				   bool guessLevel = true, bool censor = true); | ||||
| 	void logOutput(QString line, | ||||
| 				   MessageLevel::Enum defaultLevel = MessageLevel::Message, | ||||
| 				   bool guessLevel = true, bool censor = true); | ||||
| 	void on_log(QStringList lines, MessageLevel::Enum level); | ||||
| 	void logOutput(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::Message); | ||||
| 	void logOutput(QString line, MessageLevel::Enum defaultLevel = MessageLevel::Message); | ||||
| 
 | ||||
| 	void on_pre_state(LoggedProcess::State state); | ||||
| 	void on_state(LoggedProcess::State state); | ||||
| 	void on_post_state(LoggedProcess::State state); | ||||
| 
 | ||||
| protected: | ||||
| 	InstancePtr m_instance; | ||||
| 	QString m_err_leftover; | ||||
| 	QString m_out_leftover; | ||||
| 	QProcess m_prepostlaunchprocess; | ||||
| 
 | ||||
| 	LoggedProcess m_prelaunchprocess; | ||||
| 	LoggedProcess m_postlaunchprocess; | ||||
| 	LoggedProcess m_process; | ||||
| 	QProcessEnvironment m_env; | ||||
| 
 | ||||
| 	bool killed = false; | ||||
| 	QString m_header; | ||||
| }; | ||||
| @@ -1,420 +0,0 @@ | ||||
| /* Copyright 2013-2015 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 "BaseProcess.h" | ||||
| #include <QDebug> | ||||
| #include <QDir> | ||||
| #include <QEventLoop> | ||||
|  | ||||
| #define IBUS "@im=ibus" | ||||
|  | ||||
| MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) | ||||
| { | ||||
| 	if (levelName == "MultiMC") | ||||
| 		return MessageLevel::MultiMC; | ||||
| 	else if (levelName == "Debug") | ||||
| 		return MessageLevel::Debug; | ||||
| 	else if (levelName == "Info") | ||||
| 		return MessageLevel::Info; | ||||
| 	else if (levelName == "Message") | ||||
| 		return MessageLevel::Message; | ||||
| 	else if (levelName == "Warning") | ||||
| 		return MessageLevel::Warning; | ||||
| 	else if (levelName == "Error") | ||||
| 		return MessageLevel::Error; | ||||
| 	else if (levelName == "Fatal") | ||||
| 		return MessageLevel::Fatal; | ||||
| 	// Skip PrePost, it's not exposed to !![]! | ||||
| 	else | ||||
| 		return MessageLevel::Message; | ||||
| } | ||||
|  | ||||
| BaseProcess::BaseProcess(InstancePtr instance):  QProcess(), m_instance(instance) | ||||
| { | ||||
| } | ||||
|  | ||||
| void BaseProcess::init() | ||||
| { | ||||
| 	connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), | ||||
| 			SLOT(finish(int, QProcess::ExitStatus))); | ||||
|  | ||||
| 	// prepare the process environment | ||||
| 	QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); | ||||
|  | ||||
| 	QProcessEnvironment env; | ||||
|  | ||||
| 	QStringList ignored = | ||||
| 	{ | ||||
| 		"JAVA_ARGS", | ||||
| 		"CLASSPATH", | ||||
| 		"CONFIGPATH", | ||||
| 		"JAVA_HOME", | ||||
| 		"JRE_HOME", | ||||
| 		"_JAVA_OPTIONS", | ||||
| 		"JAVA_OPTIONS", | ||||
| 		"JAVA_TOOL_OPTIONS", | ||||
| 		"CDPATH" | ||||
| 	}; | ||||
| 	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; | ||||
| 		} | ||||
| #endif | ||||
| 		if(key == "GAME_PRELOAD") | ||||
| 		{ | ||||
| 			env.insert("LD_PRELOAD", value); | ||||
| 			continue; | ||||
| 		} | ||||
| 		if(key == "GAME_LIBRARY_PATH") | ||||
| 		{ | ||||
| 			env.insert("LD_LIBRARY_PATH", value); | ||||
| 			continue; | ||||
| 		} | ||||
| 		qDebug() << "Env: " << key << value; | ||||
| 		env.insert(key, value); | ||||
| 	} | ||||
| #ifdef Q_OS_LINUX | ||||
| 	// HACK: Workaround for QTBUG-42500 | ||||
| 	if(!env.contains("LD_LIBRARY_PATH")) | ||||
| 	{ | ||||
| 		env.insert("LD_LIBRARY_PATH", ""); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	// export some infos | ||||
| 	auto variables = getVariables(); | ||||
| 	for (auto it = variables.begin(); it != variables.end(); ++it) | ||||
| 	{ | ||||
| 		env.insert(it.key(), it.value()); | ||||
| 	} | ||||
|  | ||||
| 	this->setProcessEnvironment(env); | ||||
| 	m_prepostlaunchprocess.setProcessEnvironment(env); | ||||
|  | ||||
| 	// std channels | ||||
| 	connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr())); | ||||
| 	connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut())); | ||||
|  | ||||
| 	// Log prepost launch command output (can be disabled.) | ||||
| 	if (m_instance->settings()->get("LogPrePostOutput").toBool()) | ||||
| 	{ | ||||
| 		connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this, | ||||
| 				&BaseProcess::on_prepost_stdErr); | ||||
| 		connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this, | ||||
| 				&BaseProcess::on_prepost_stdOut); | ||||
| 	} | ||||
|  | ||||
| 	// a process has been constructed for the instance. It is running from MultiMC POV | ||||
| 	m_instance->setRunning(true); | ||||
| } | ||||
|  | ||||
|  | ||||
| void BaseProcess::setWorkdir(QString path) | ||||
| { | ||||
| 	QDir mcDir(path); | ||||
| 	this->setWorkingDirectory(mcDir.absolutePath()); | ||||
| 	m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath()); | ||||
| } | ||||
|  | ||||
| void BaseProcess::printHeader() | ||||
| { | ||||
| 	emit log(m_header); | ||||
| } | ||||
|  | ||||
|  | ||||
| void BaseProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel, | ||||
| 								 bool guessLevel, bool censor) | ||||
| { | ||||
| 	for (int i = 0; i < lines.size(); ++i) | ||||
| 		logOutput(lines[i], defaultLevel, guessLevel, censor); | ||||
| } | ||||
|  | ||||
| void BaseProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel, | ||||
| 								 bool censor) | ||||
| { | ||||
| 	MessageLevel::Enum level = defaultLevel; | ||||
|  | ||||
| 	// Level prefix | ||||
| 	int endmark = line.indexOf("]!"); | ||||
| 	if (line.startsWith("! | ||||
| 	{ | ||||
| 		if (state == QProcess::NotRunning) | ||||
| 		{ | ||||
| 			eventLoop.quit(); | ||||
| 		} | ||||
| 	}; | ||||
| 	auto connection = connect(&m_prepostlaunchprocess, &QProcess::stateChanged, finisher); | ||||
| 	int ret = eventLoop.exec(); | ||||
| 	disconnect(connection); | ||||
| 	return ret == 0; | ||||
| } | ||||
|  | ||||
| QString BaseProcess::substituteVariables(const QString &cmd) const | ||||
| { | ||||
| 	QString out = cmd; | ||||
| 	auto variables = getVariables(); | ||||
| 	for (auto it = variables.begin(); it != variables.end(); ++it) | ||||
| 	{ | ||||
| 		out.replace("$" + it.key(), it.value()); | ||||
| 	} | ||||
| 	auto env = QProcessEnvironment::systemEnvironment(); | ||||
| 	for (auto var : env.keys()) | ||||
| 	{ | ||||
| 		out.replace("$" + var, env.value(var)); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| @@ -8,9 +8,13 @@ set(LOGIC_SOURCES | ||||
| 	BaseVersionList.cpp | ||||
| 	InstanceList.h | ||||
| 	InstanceList.cpp | ||||
| 	LoggedProcess.h | ||||
| 	LoggedProcess.cpp | ||||
| 	MessageLevel.cpp | ||||
| 	MessageLevel.h | ||||
| 	BaseVersion.h | ||||
| 	BaseProcess.h | ||||
| 	BaseProcess.cpp | ||||
| 	BaseLauncher.h | ||||
| 	BaseLauncher.cpp | ||||
| 	BaseInstance.h | ||||
| 	BaseInstance.cpp | ||||
| 	NullInstance.h | ||||
| @@ -136,8 +140,8 @@ set(LOGIC_SOURCES | ||||
| 	minecraft/JarMod.h | ||||
| 	minecraft/MinecraftInstance.cpp | ||||
| 	minecraft/MinecraftInstance.h | ||||
| 	minecraft/MinecraftProcess.cpp | ||||
| 	minecraft/MinecraftProcess.h | ||||
| 	minecraft/MinecraftLauncher.cpp | ||||
| 	minecraft/MinecraftLauncher.h | ||||
| 	minecraft/MinecraftVersion.cpp | ||||
| 	minecraft/MinecraftVersion.h | ||||
| 	minecraft/MinecraftVersionList.cpp | ||||
|   | ||||
							
								
								
									
										147
									
								
								logic/LoggedProcess.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								logic/LoggedProcess.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| #include "LoggedProcess.h" | ||||
| #include "MessageLevel.h" | ||||
| #include <QDebug> | ||||
|  | ||||
| LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) | ||||
| { | ||||
| 	// QProcess has a strange interface... let's map a lot of those into a few. | ||||
| 	connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); | ||||
| 	connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); | ||||
| 	connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus))); | ||||
| 	connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); | ||||
| 	connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); | ||||
| } | ||||
|  | ||||
| QStringList reprocess(const QByteArray & data, QString & leftover) | ||||
| { | ||||
| 	QString str = leftover + QString::fromLocal8Bit(data); | ||||
|  | ||||
| 	str.remove('\r'); | ||||
| 	QStringList lines = str.split("\n"); | ||||
| 	leftover = lines.takeLast(); | ||||
| 	return lines; | ||||
| } | ||||
|  | ||||
| void LoggedProcess::on_stdErr() | ||||
| { | ||||
| 	auto lines = reprocess(readAllStandardError(), m_err_leftover); | ||||
| 	emit log(lines, MessageLevel::StdErr); | ||||
| } | ||||
|  | ||||
| void LoggedProcess::on_stdOut() | ||||
| { | ||||
| 	auto lines = reprocess(readAllStandardOutput(), m_out_leftover); | ||||
| 	emit log(lines, MessageLevel::StdOut); | ||||
| } | ||||
|  | ||||
| void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status) | ||||
| { | ||||
| 	// Flush console window | ||||
| 	if (!m_err_leftover.isEmpty()) | ||||
| 	{ | ||||
| 		emit log({m_err_leftover}, MessageLevel::StdErr); | ||||
| 		m_err_leftover.clear(); | ||||
| 	} | ||||
| 	if (!m_out_leftover.isEmpty()) | ||||
| 	{ | ||||
| 		emit log({m_err_leftover}, MessageLevel::StdOut); | ||||
| 		m_out_leftover.clear(); | ||||
| 	} | ||||
|  | ||||
| 	// based on state, send signals | ||||
| 	if (!m_is_aborting) | ||||
| 	{ | ||||
| 		if (status == QProcess::NormalExit) | ||||
| 		{ | ||||
| 			//: Message displayed on instance exit | ||||
| 			emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC); | ||||
| 			changeState(LoggedProcess::Finished); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//: Message displayed on instance crashed | ||||
| 			if(exit_code == -1) | ||||
| 				emit log({tr("Process crashed.").arg(exit_code)}, MessageLevel::MultiMC); | ||||
| 			else | ||||
| 				emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC); | ||||
| 			changeState(LoggedProcess::Crashed); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		//: Message displayed after the instance exits due to kill request | ||||
| 		emit log({tr("Process was killed by user.")}, MessageLevel::Error); | ||||
| 		changeState(LoggedProcess::Aborted); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void LoggedProcess::on_error(QProcess::ProcessError error) | ||||
| { | ||||
| 	switch(error) | ||||
| 	{ | ||||
| 		case QProcess::FailedToStart: | ||||
| 		{ | ||||
| 			emit log({tr("The process failed to start.")}, MessageLevel::Fatal); | ||||
| 			changeState(LoggedProcess::FailedToStart); | ||||
| 			break; | ||||
| 		} | ||||
| 		// we'll just ignore those... never needed them | ||||
| 		case QProcess::Crashed: | ||||
| 		case QProcess::ReadError: | ||||
| 		case QProcess::Timedout: | ||||
| 		case QProcess::UnknownError: | ||||
| 		case QProcess::WriteError: | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void LoggedProcess::kill() | ||||
| { | ||||
| 	m_is_aborting = true; | ||||
| 	QProcess::kill(); | ||||
| } | ||||
|  | ||||
| int LoggedProcess::exitCode() const | ||||
| { | ||||
| 	return m_exit_code; | ||||
| } | ||||
|  | ||||
| void LoggedProcess::changeState(LoggedProcess::State state) | ||||
| { | ||||
| 	if(state == m_state) | ||||
| 		return; | ||||
| 	m_state = state; | ||||
| 	emit stateChanged(m_state); | ||||
| } | ||||
|  | ||||
| LoggedProcess::State LoggedProcess::state() const | ||||
| { | ||||
| 	return m_state; | ||||
| } | ||||
|  | ||||
| void LoggedProcess::on_stateChange(QProcess::ProcessState state) | ||||
| { | ||||
| 	switch(state) | ||||
| 	{ | ||||
| 		case QProcess::NotRunning: | ||||
| 			break; // let's not - there are too many that handle this already. | ||||
| 		case QProcess::Starting: | ||||
| 		{ | ||||
| 			if(m_state != LoggedProcess::NotRunning) | ||||
| 			{ | ||||
| 				qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting; | ||||
| 			} | ||||
| 			changeState(LoggedProcess::Starting); | ||||
| 			return; | ||||
| 		} | ||||
| 		case QProcess::Running: | ||||
| 		{ | ||||
| 			if(m_state != LoggedProcess::Starting) | ||||
| 			{ | ||||
| 				qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running; | ||||
| 			} | ||||
| 			changeState(LoggedProcess::Running); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										60
									
								
								logic/LoggedProcess.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								logic/LoggedProcess.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| #pragma once | ||||
| #include <QProcess> | ||||
| #include "MessageLevel.h" | ||||
|  | ||||
| /* | ||||
|  * This is a basic process. | ||||
|  * It has line-based logging support and hides some of the nasty bits. | ||||
|  */ | ||||
| class LoggedProcess : public QProcess | ||||
| { | ||||
| Q_OBJECT | ||||
| public: | ||||
| 	enum State | ||||
| 	{ | ||||
| 		NotRunning, | ||||
| 		Starting, | ||||
| 		FailedToStart, | ||||
| 		Running, | ||||
| 		Finished, | ||||
| 		Crashed, | ||||
| 		Aborted, | ||||
| 		Skipped | ||||
| 	}; | ||||
|  | ||||
| public: | ||||
|     explicit LoggedProcess(QObject* parent = 0); | ||||
|     virtual ~LoggedProcess() {}; | ||||
|  | ||||
| 	State state() const; | ||||
| 	int exitCode() const; | ||||
|  | ||||
| signals: | ||||
| 	void log(QStringList lines, MessageLevel::Enum level); | ||||
| 	void stateChanged(LoggedProcess::State state); | ||||
|  | ||||
| public slots: | ||||
| 	/** | ||||
| 	 * @brief kill the process - equivalent to kill -9 | ||||
| 	 */ | ||||
| 	void kill(); | ||||
|  | ||||
|  | ||||
| private slots: | ||||
| 	void on_stdErr(); | ||||
| 	void on_stdOut(); | ||||
| 	void on_exit(int exit_code, QProcess::ExitStatus status); | ||||
| 	void on_error(QProcess::ProcessError error); | ||||
| 	void on_stateChange(QProcess::ProcessState); | ||||
|  | ||||
| private: | ||||
| 	void changeState(LoggedProcess::State state); | ||||
|  | ||||
| private: | ||||
| 	QString m_err_leftover; | ||||
| 	QString m_out_leftover; | ||||
| 	bool m_killed = false; | ||||
| 	State m_state = NotRunning; | ||||
| 	int m_exit_code = 0; | ||||
| 	bool m_is_aborting = false; | ||||
| }; | ||||
							
								
								
									
										36
									
								
								logic/MessageLevel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								logic/MessageLevel.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| #include "MessageLevel.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 !![]! | ||||
| 	// Also skip StdErr and StdOut | ||||
| 	else | ||||
| 		return MessageLevel::Unknown; | ||||
| } | ||||
|  | ||||
| MessageLevel::Enum MessageLevel::fromLine(QString &line) | ||||
| { | ||||
| 	// Level prefix | ||||
| 	int endmark = line.indexOf("]!"); | ||||
| 	if (line.startsWith("!![") && endmark != -1) | ||||
| 	{ | ||||
| 		auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); | ||||
| 		line = line.mid(endmark + 2); | ||||
| 		return level; | ||||
| 	} | ||||
| 	return MessageLevel::Unknown; | ||||
| } | ||||
							
								
								
									
										28
									
								
								logic/MessageLevel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								logic/MessageLevel.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QString> | ||||
|  | ||||
| /** | ||||
|  * @brief the MessageLevel Enum | ||||
|  * defines what level a log message is | ||||
|  */ | ||||
| namespace MessageLevel | ||||
| { | ||||
| enum Enum | ||||
| { | ||||
| 	Unknown, /**< No idea what this is or where it came from */ | ||||
| 	StdOut,  /**< Undetermined stderr messages */ | ||||
| 	StdErr,  /**< Undetermined stdout messages */ | ||||
| 	MultiMC, /**< MultiMC Messages */ | ||||
| 	Debug,   /**< Debug Messages */ | ||||
| 	Info,    /**< Info Messages */ | ||||
| 	Message, /**< Standard Messages */ | ||||
| 	Warning, /**< Warnings */ | ||||
| 	Error,   /**< Errors */ | ||||
| 	Fatal,   /**< Fatal Errors */ | ||||
| }; | ||||
| MessageLevel::Enum getLevel(const QString &levelName); | ||||
|  | ||||
| /* Get message level from a line. Line is modified if it was successful. */ | ||||
| MessageLevel::Enum fromLine(QString &line); | ||||
| } | ||||
| @@ -43,7 +43,7 @@ public: | ||||
| 	{ | ||||
| 		return instanceRoot(); | ||||
| 	}; | ||||
| 	virtual BaseProcess* prepareForLaunch(AuthSessionPtr) | ||||
| 	virtual BaseLauncher* prepareForLaunch(AuthSessionPtr) | ||||
| 	{ | ||||
| 		return nullptr; | ||||
| 	} | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|  | ||||
| #include "minecraft/LegacyUpdate.h" | ||||
| #include "icons/IconList.h" | ||||
| #include "minecraft/MinecraftProcess.h" | ||||
| #include "minecraft/MinecraftLauncher.h" | ||||
| #include "minecraft/ModList.h" | ||||
|  | ||||
| LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) | ||||
| @@ -95,7 +95,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate() | ||||
| 	return std::shared_ptr<Task>(new LegacyUpdate(this, this)); | ||||
| } | ||||
|  | ||||
| BaseProcess *LegacyInstance::prepareForLaunch(AuthSessionPtr account) | ||||
| BaseLauncher *LegacyInstance::prepareForLaunch(AuthSessionPtr account) | ||||
| { | ||||
| 	QString launchScript; | ||||
| 	QIcon icon = ENV.icons()->getIcon(iconKey()); | ||||
| @@ -122,7 +122,7 @@ BaseProcess *LegacyInstance::prepareForLaunch(AuthSessionPtr account) | ||||
| 		launchScript += "lwjgl " + lwjgl + "\n"; | ||||
| 		launchScript += "launcher legacy\n"; | ||||
| 	} | ||||
| 	auto process = MinecraftProcess::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); | ||||
| 	auto process = MinecraftLauncher::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); | ||||
| 	process->setLaunchScript(launchScript); | ||||
| 	process->setWorkdir(minecraftRoot()); | ||||
| 	process->setLogin(account); | ||||
|   | ||||
| @@ -111,7 +111,7 @@ public: | ||||
| 	virtual void setShouldUpdate(bool val) override; | ||||
| 	virtual std::shared_ptr<Task> doUpdate() override; | ||||
|  | ||||
| 	virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) override; | ||||
| 	virtual BaseLauncher *prepareForLaunch(AuthSessionPtr account) override; | ||||
| 	virtual void cleanupAfterRun() override; | ||||
|  | ||||
| 	virtual QString getStatusbarDescription() override; | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| #include "minecraft/MinecraftProcess.h" | ||||
| #include "minecraft/MinecraftLauncher.h" | ||||
| #include "BaseInstance.h" | ||||
| #include <java/JavaChecker.h> | ||||
| #include <MMCStrings.h> | ||||
| @@ -32,19 +32,19 @@ | ||||
| #include "cmdutils.h" | ||||
| 
 | ||||
| // constructor
 | ||||
| MinecraftProcess::MinecraftProcess(MinecraftInstancePtr inst) : BaseProcess(inst) | ||||
| MinecraftLauncher::MinecraftLauncher(MinecraftInstancePtr inst) : BaseLauncher(inst) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| MinecraftProcess* MinecraftProcess::create(MinecraftInstancePtr inst) | ||||
| MinecraftLauncher* MinecraftLauncher::create(MinecraftInstancePtr inst) | ||||
| { | ||||
| 	auto proc = new MinecraftProcess(inst); | ||||
| 	auto proc = new MinecraftLauncher(inst); | ||||
| 	proc->init(); | ||||
| 	return proc; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| QString MinecraftProcess::censorPrivateInfo(QString in) | ||||
| QString MinecraftLauncher::censorPrivateInfo(QString in) | ||||
| { | ||||
| 	if (!m_session) | ||||
| 		return in; | ||||
| @@ -67,7 +67,7 @@ QString MinecraftProcess::censorPrivateInfo(QString in) | ||||
| } | ||||
| 
 | ||||
| // console window
 | ||||
| MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLevel::Enum level) | ||||
| MessageLevel::Enum MinecraftLauncher::guessLevel(const QString &line, MessageLevel::Enum level) | ||||
| { | ||||
| 	QRegularExpression re("\\[(?<timestamp>[0-9:]+)\\] \\[[^/]+/(?<level>[^\\]]+)\\]"); | ||||
| 	auto match = re.match(line); | ||||
| @@ -107,7 +107,7 @@ MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLeve | ||||
| 	return level; | ||||
| } | ||||
| 
 | ||||
| QMap<QString, QString> MinecraftProcess::getVariables() const | ||||
| QMap<QString, QString> MinecraftLauncher::getVariables() const | ||||
| { | ||||
| 	auto mcInstance = std::dynamic_pointer_cast<MinecraftInstance>(m_instance); | ||||
| 	QMap<QString, QString> out; | ||||
| @@ -120,7 +120,7 @@ QMap<QString, QString> MinecraftProcess::getVariables() const | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| QStringList MinecraftProcess::javaArguments() const | ||||
| QStringList MinecraftLauncher::javaArguments() const | ||||
| { | ||||
| 	QStringList args; | ||||
| 
 | ||||
| @@ -161,22 +161,8 @@ QStringList MinecraftProcess::javaArguments() const | ||||
| 	return args; | ||||
| } | ||||
| 
 | ||||
| void MinecraftProcess::arm() | ||||
| bool MinecraftLauncher::checkJava(QString JavaPath) | ||||
| { | ||||
| 	printHeader(); | ||||
| 	emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n"); | ||||
| 
 | ||||
| 	if (!preLaunch()) | ||||
| 	{ | ||||
| 		emit ended(m_instance, 1, QProcess::CrashExit); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	m_instance->setLastLaunch(); | ||||
| 
 | ||||
| 	QString JavaPath = m_instance->settings()->get("JavaPath").toString(); | ||||
| 	emit log("Java path is:\n" + JavaPath + "\n\n"); | ||||
| 
 | ||||
| 	auto realJavaPath = QStandardPaths::findExecutable(JavaPath); | ||||
| 	if (realJavaPath.isEmpty()) | ||||
| 	{ | ||||
| @@ -185,51 +171,73 @@ void MinecraftProcess::arm() | ||||
| 				 MessageLevel::Warning); | ||||
| 	} | ||||
| 
 | ||||
| 	// check java version here.
 | ||||
| 	QFileInfo javaInfo(realJavaPath); | ||||
| 	qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); | ||||
| 	auto storedUnixTime = m_instance->settings()->get("JavaTimestamp").toLongLong(); | ||||
| 	// if they are not the same, check!
 | ||||
| 	if(javaUnixTime != storedUnixTime) | ||||
| 	{ | ||||
| 		QFileInfo javaInfo(realJavaPath); | ||||
| 		qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); | ||||
| 		auto storedUnixTime = m_instance->settings()->get("JavaTimestamp").toLongLong(); | ||||
| 		// if they are not the same, check!
 | ||||
| 		if(javaUnixTime != storedUnixTime) | ||||
| 		{ | ||||
| 			QEventLoop ev; | ||||
| 			auto checker = std::make_shared<JavaChecker>(); | ||||
| 			bool successful = false; | ||||
| 			QString errorLog; | ||||
| 			QString version; | ||||
| 			emit log(tr("Checking Java version..."), MessageLevel::MultiMC); | ||||
| 			connect(checker.get(), &JavaChecker::checkFinished, | ||||
| 					[&](JavaCheckResult result) | ||||
| 					{ | ||||
| 						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) | ||||
| 		QEventLoop ev; | ||||
| 		auto checker = std::make_shared<JavaChecker>(); | ||||
| 		bool successful = false; | ||||
| 		QString errorLog; | ||||
| 		QString version; | ||||
| 		emit log(tr("Checking Java version..."), MessageLevel::MultiMC); | ||||
| 		connect(checker.get(), &JavaChecker::checkFinished, | ||||
| 				[&](JavaCheckResult result) | ||||
| 				{ | ||||
| 					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; | ||||
| 					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(tr("Java version is %1!\n").arg(version), MessageLevel::MultiMC); | ||||
| 			m_instance->settings()->set("JavaVersion", version); | ||||
| 			m_instance->settings()->set("JavaTimestamp", javaUnixTime); | ||||
| 			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; | ||||
| } | ||||
| 
 | ||||
| void MinecraftLauncher::arm() | ||||
| { | ||||
| 	printHeader(); | ||||
| 	emit log("Minecraft folder is:\n" + m_process.workingDirectory() + "\n\n"); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	if (!preLaunch()) | ||||
| 	{ | ||||
| 		emit ended(m_instance, 1, QProcess::CrashExit); | ||||
| 		return; | ||||
| 	} | ||||
| 	*/ | ||||
| 
 | ||||
| 	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(); | ||||
| @@ -250,14 +258,15 @@ void MinecraftProcess::arm() | ||||
| 		} | ||||
| 		emit log("Wrapper command is:\n" + wrapperCommand + "\n\n"); | ||||
| 		args.prepend(JavaPath); | ||||
| 		start(wrapperCommand, args); | ||||
| 		m_process.start(wrapperCommand, args); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		start(JavaPath, args); | ||||
| 		m_process.start(JavaPath, args); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!waitForStarted()) | ||||
| 	// 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); | ||||
| @@ -268,23 +277,20 @@ void MinecraftProcess::arm() | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	emit log(tr("Minecraft process ID: %1\n\n").arg(processId()), MessageLevel::MultiMC); | ||||
| 	emit log(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC); | ||||
| 
 | ||||
| 	// send the launch script to the launcher part
 | ||||
| 	QByteArray bytes = launchScript.toUtf8(); | ||||
| 	writeData(bytes.constData(), bytes.length()); | ||||
| 	m_process.write(launchScript.toUtf8()); | ||||
| } | ||||
| 
 | ||||
| void MinecraftProcess::launch() | ||||
| void MinecraftLauncher::launch() | ||||
| { | ||||
| 	QString launchString("launch\n"); | ||||
| 	QByteArray bytes = launchString.toUtf8(); | ||||
| 	writeData(bytes.constData(), bytes.length()); | ||||
| 	m_process.write(launchString.toUtf8()); | ||||
| } | ||||
| 
 | ||||
| void MinecraftProcess::abort() | ||||
| void MinecraftLauncher::abort() | ||||
| { | ||||
| 	QString launchString("abort\n"); | ||||
| 	QByteArray bytes = launchString.toUtf8(); | ||||
| 	writeData(bytes.constData(), bytes.length()); | ||||
| 	m_process.write(launchString.toUtf8()); | ||||
| } | ||||
| @@ -19,20 +19,20 @@ | ||||
| 
 | ||||
| #include <QString> | ||||
| #include "minecraft/MinecraftInstance.h" | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * The MinecraftProcess class | ||||
|  * The MinecraftLauncher class | ||||
|  */ | ||||
| class MinecraftProcess : public BaseProcess | ||||
| class MinecraftLauncher : public BaseLauncher | ||||
| { | ||||
| 	Q_OBJECT | ||||
| protected: | ||||
| 	MinecraftProcess(MinecraftInstancePtr inst); | ||||
| 	MinecraftLauncher(MinecraftInstancePtr inst); | ||||
| public: | ||||
| 	static MinecraftProcess *create(MinecraftInstancePtr inst); | ||||
| 	static MinecraftLauncher *create(MinecraftInstancePtr inst); | ||||
| 
 | ||||
| 	virtual ~MinecraftProcess(){}; | ||||
| 	virtual ~MinecraftLauncher(){}; | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * @brief start the launcher part with the provided launch script | ||||
| @@ -69,8 +69,9 @@ protected: | ||||
| 	QString launchScript; | ||||
| 	QString m_nativeFolder; | ||||
| 
 | ||||
| protected: | ||||
| 	bool checkJava(QString path); | ||||
| 	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; | ||||
| @@ -22,7 +22,7 @@ | ||||
| #include "minecraft/OneSixUpdate.h" | ||||
| #include "minecraft/MinecraftProfile.h" | ||||
| #include "minecraft/VersionBuildError.h" | ||||
| #include "minecraft/MinecraftProcess.h" | ||||
| #include "minecraft/MinecraftLauncher.h" | ||||
| #include "minecraft/OneSixProfileStrategy.h" | ||||
| #include "MMCZip.h" | ||||
|  | ||||
| @@ -123,7 +123,7 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) | ||||
| 	return parts; | ||||
| } | ||||
|  | ||||
| BaseProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session) | ||||
| BaseLauncher *OneSixInstance::prepareForLaunch(AuthSessionPtr session) | ||||
| { | ||||
| 	QString launchScript; | ||||
| 	QIcon icon = ENV.icons()->getIcon(iconKey()); | ||||
| @@ -230,7 +230,7 @@ BaseProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session) | ||||
| 	} | ||||
| 	launchScript += "launcher onesix\n"; | ||||
|  | ||||
| 	auto process = MinecraftProcess::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); | ||||
| 	auto process = MinecraftLauncher::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); | ||||
| 	process->setLaunchScript(launchScript); | ||||
| 	process->setWorkdir(minecraftRoot()); | ||||
| 	process->setLogin(session); | ||||
|   | ||||
| @@ -49,7 +49,7 @@ public: | ||||
| 	virtual QString instanceConfigFolder() const override; | ||||
|  | ||||
| 	virtual std::shared_ptr<Task> doUpdate() override; | ||||
| 	virtual BaseProcess *prepareForLaunch(AuthSessionPtr account) override; | ||||
| 	virtual BaseLauncher *prepareForLaunch(AuthSessionPtr account) override; | ||||
|  | ||||
| 	virtual void cleanupAfterRun() override; | ||||
|  | ||||
|   | ||||
| @@ -19,16 +19,6 @@ BaseExternalTool::~BaseExternalTool() | ||||
| { | ||||
| } | ||||
|  | ||||
| qint64 BaseExternalTool::pid(QProcess *process) | ||||
| { | ||||
| #ifdef Q_OS_WIN | ||||
| 	struct _PROCESS_INFORMATION *procinfo = process->pid(); | ||||
| 	return procinfo->dwProcessId; | ||||
| #else | ||||
| 	return process->pid(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| BaseDetachedTool::BaseDetachedTool(SettingsObjectPtr settings, InstancePtr instance, QObject *parent) | ||||
| 	: BaseExternalTool(settings, instance, parent) | ||||
| { | ||||
|   | ||||
| @@ -17,7 +17,6 @@ public: | ||||
| protected: | ||||
| 	InstancePtr m_instance; | ||||
| 	SettingsObjectPtr globalSettings; | ||||
| 	qint64 pid(QProcess *process); | ||||
| }; | ||||
|  | ||||
| class BaseDetachedTool : public BaseExternalTool | ||||
|   | ||||
| @@ -7,7 +7,7 @@ BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QOb | ||||
| { | ||||
| } | ||||
|  | ||||
| void BaseProfiler::beginProfiling(BaseProcess *process) | ||||
| void BaseProfiler::beginProfiling(BaseLauncher *process) | ||||
| { | ||||
| 	beginProfilingImpl(process); | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|  | ||||
| class BaseInstance; | ||||
| class SettingsObject; | ||||
| class BaseProcess; | ||||
| class BaseLauncher; | ||||
| class QProcess; | ||||
|  | ||||
| class BaseProfiler : public BaseExternalTool | ||||
| @@ -15,13 +15,13 @@ public: | ||||
|  | ||||
| public | ||||
| slots: | ||||
| 	void beginProfiling(BaseProcess *process); | ||||
| 	void beginProfiling(BaseLauncher *process); | ||||
| 	void abortProfiling(); | ||||
|  | ||||
| protected: | ||||
| 	QProcess *m_profilerProcess; | ||||
|  | ||||
| 	virtual void beginProfilingImpl(BaseProcess *process) = 0; | ||||
| 	virtual void beginProfilingImpl(BaseLauncher *process) = 0; | ||||
| 	virtual void abortProfilingImpl(); | ||||
|  | ||||
| signals: | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #include <QMessageBox> | ||||
|  | ||||
| #include "settings/SettingsObject.h" | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
| #include "BaseInstance.h" | ||||
|  | ||||
| class JProfiler : public BaseProfiler | ||||
| @@ -18,7 +18,7 @@ private slots: | ||||
| 	void profilerFinished(int exit, QProcess::ExitStatus status); | ||||
|  | ||||
| protected: | ||||
| 	void beginProfilingImpl(BaseProcess *process); | ||||
| 	void beginProfilingImpl(BaseLauncher *process); | ||||
|  | ||||
| private: | ||||
| 	int listeningPort = 0; | ||||
| @@ -48,13 +48,13 @@ void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void JProfiler::beginProfilingImpl(BaseProcess *process) | ||||
| void JProfiler::beginProfilingImpl(BaseLauncher *process) | ||||
| { | ||||
| 	listeningPort = globalSettings->get("JProfilerPort").toInt(); | ||||
| 	QProcess *profiler = new QProcess(this); | ||||
| 	QStringList profilerArgs = | ||||
| 	{ | ||||
| 		"-d", QString::number(pid(process)), | ||||
| 		"-d", QString::number(process->pid()), | ||||
| 		"--gui", | ||||
| 		"-p", QString::number(listeningPort) | ||||
| 	}; | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #include <QStandardPaths> | ||||
|  | ||||
| #include "settings/SettingsObject.h" | ||||
| #include "BaseProcess.h" | ||||
| #include "BaseLauncher.h" | ||||
| #include "BaseInstance.h" | ||||
|  | ||||
| class JVisualVM : public BaseProfiler | ||||
| @@ -18,7 +18,7 @@ private slots: | ||||
| 	void profilerFinished(int exit, QProcess::ExitStatus status); | ||||
|  | ||||
| protected: | ||||
| 	void beginProfilingImpl(BaseProcess *process); | ||||
| 	void beginProfilingImpl(BaseLauncher *process); | ||||
| }; | ||||
|  | ||||
|  | ||||
| @@ -45,12 +45,12 @@ void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void JVisualVM::beginProfilingImpl(BaseProcess *process) | ||||
| void JVisualVM::beginProfilingImpl(BaseLauncher *process) | ||||
| { | ||||
| 	QProcess *profiler = new QProcess(this); | ||||
| 	QStringList profilerArgs = | ||||
| 	{ | ||||
| 		"--openpid", QString::number(pid(process)) | ||||
| 		"--openpid", QString::number(process->pid()) | ||||
| 	}; | ||||
| 	auto programPath = globalSettings->get("JVisualVMPath").toString(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user