From f0b71f989ea798495ad80d1f059ae0a28514f9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 3 Nov 2016 01:10:16 +0100 Subject: NOISSUE use LoggedProcess to work around issues with QProcess on macOS --- api/logic/BaseInstance.h | 2 +- api/logic/CMakeLists.txt | 8 +- api/logic/LoggedProcess.cpp | 176 +++++++++++++++++++++ api/logic/LoggedProcess.h | 80 ++++++++++ api/logic/MessageLevel.cpp | 36 +++++ api/logic/MessageLevel.h | 28 ++++ api/logic/java/launch/CheckJava.h | 2 +- api/logic/launch/LoggedProcess.cpp | 176 --------------------- api/logic/launch/LoggedProcess.h | 79 --------- api/logic/launch/MessageLevel.cpp | 36 ----- api/logic/launch/MessageLevel.h | 28 ---- api/logic/launch/steps/PostLaunchCommand.h | 2 +- api/logic/launch/steps/PreLaunchCommand.h | 4 +- api/logic/launch/steps/TextPrint.h | 2 +- api/logic/launch/steps/Update.h | 2 +- .../launch/CreateServerResourcePacksFolder.h | 2 +- api/logic/minecraft/launch/DirectJavaLaunch.h | 2 +- api/logic/minecraft/launch/LauncherPartLaunch.h | 2 +- 18 files changed, 334 insertions(+), 333 deletions(-) create mode 100644 api/logic/LoggedProcess.cpp create mode 100644 api/logic/LoggedProcess.h create mode 100644 api/logic/MessageLevel.cpp create mode 100644 api/logic/MessageLevel.h delete mode 100644 api/logic/launch/LoggedProcess.cpp delete mode 100644 api/logic/launch/LoggedProcess.h delete mode 100644 api/logic/launch/MessageLevel.cpp delete mode 100644 api/logic/launch/MessageLevel.h (limited to 'api') diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index 56d3d6cb..669bad79 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -27,7 +27,7 @@ #include "settings/INIFile.h" #include "BaseVersionList.h" #include "minecraft/auth/MojangAccount.h" -#include "launch/MessageLevel.h" +#include "MessageLevel.h" #include "pathmatcher/IPathMatcher.h" #include "multimc_logic_export.h" diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 0741fb1a..6d0f78f9 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -16,6 +16,10 @@ set(CORE_SOURCES InstanceImportTask.cpp InstanceList.h InstanceList.cpp + LoggedProcess.h + LoggedProcess.cpp + MessageLevel.cpp + MessageLevel.h BaseInstanceProvider.h FolderInstanceProvider.h FolderInstanceProvider.cpp @@ -126,12 +130,8 @@ set(LAUNCH_SOURCES launch/LaunchStep.h launch/LaunchTask.cpp launch/LaunchTask.h - launch/LoggedProcess.cpp - launch/LoggedProcess.h launch/LogModel.cpp launch/LogModel.h - launch/MessageLevel.cpp - launch/MessageLevel.h ) # Old update system diff --git a/api/logic/LoggedProcess.cpp b/api/logic/LoggedProcess.cpp new file mode 100644 index 00000000..f89b4acc --- /dev/null +++ b/api/logic/LoggedProcess.cpp @@ -0,0 +1,176 @@ +#include "LoggedProcess.h" +#include "MessageLevel.h" +#include + +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); +} + +LoggedProcess::~LoggedProcess() +{ + if(m_is_detachable) + { + setProcessState(QProcess::NotRunning); + } +} + +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) +{ + // save the exit code + m_exit_code = exit_code; + + // 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.")}, 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; + } + } +} + +#if defined Q_OS_WIN32 +#include +#endif + +qint64 LoggedProcess::processId() const +{ +#ifdef Q_OS_WIN + return pid() ? pid()->dwProcessId : 0; +#else + return pid(); +#endif +} + +void LoggedProcess::setDetachable(bool detachable) +{ + m_is_detachable = detachable; +} diff --git a/api/logic/LoggedProcess.h b/api/logic/LoggedProcess.h new file mode 100644 index 00000000..e1928689 --- /dev/null +++ b/api/logic/LoggedProcess.h @@ -0,0 +1,80 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include "MessageLevel.h" +#include "multimc_logic_export.h" + +/* + * This is a basic process. + * It has line-based logging support and hides some of the nasty bits. + */ +class MULTIMC_LOGIC_EXPORT LoggedProcess : public QProcess +{ +Q_OBJECT +public: + enum State + { + NotRunning, + Starting, + FailedToStart, + Running, + Finished, + Crashed, + Aborted + }; + +public: + explicit LoggedProcess(QObject* parent = 0); + virtual ~LoggedProcess(); + + State state() const; + int exitCode() const; + qint64 processId() const; + + void setDetachable(bool detachable); + +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; + bool m_is_detachable = false; +}; diff --git a/api/logic/MessageLevel.cpp b/api/logic/MessageLevel.cpp new file mode 100644 index 00000000..a5191290 --- /dev/null +++ b/api/logic/MessageLevel.cpp @@ -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; +} diff --git a/api/logic/MessageLevel.h b/api/logic/MessageLevel.h new file mode 100644 index 00000000..0128148d --- /dev/null +++ b/api/logic/MessageLevel.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +/** + * @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); +} diff --git a/api/logic/java/launch/CheckJava.h b/api/logic/java/launch/CheckJava.h index 118da225..7126f4c4 100644 --- a/api/logic/java/launch/CheckJava.h +++ b/api/logic/java/launch/CheckJava.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #include class CheckJava: public LaunchStep diff --git a/api/logic/launch/LoggedProcess.cpp b/api/logic/launch/LoggedProcess.cpp deleted file mode 100644 index f89b4acc..00000000 --- a/api/logic/launch/LoggedProcess.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "LoggedProcess.h" -#include "MessageLevel.h" -#include - -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); -} - -LoggedProcess::~LoggedProcess() -{ - if(m_is_detachable) - { - setProcessState(QProcess::NotRunning); - } -} - -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) -{ - // save the exit code - m_exit_code = exit_code; - - // 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.")}, 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; - } - } -} - -#if defined Q_OS_WIN32 -#include -#endif - -qint64 LoggedProcess::processId() const -{ -#ifdef Q_OS_WIN - return pid() ? pid()->dwProcessId : 0; -#else - return pid(); -#endif -} - -void LoggedProcess::setDetachable(bool detachable) -{ - m_is_detachable = detachable; -} diff --git a/api/logic/launch/LoggedProcess.h b/api/logic/launch/LoggedProcess.h deleted file mode 100644 index c4e5c52c..00000000 --- a/api/logic/launch/LoggedProcess.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#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 - }; - -public: - explicit LoggedProcess(QObject* parent = 0); - virtual ~LoggedProcess(); - - State state() const; - int exitCode() const; - qint64 processId() const; - - void setDetachable(bool detachable); - -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; - bool m_is_detachable = false; -}; diff --git a/api/logic/launch/MessageLevel.cpp b/api/logic/launch/MessageLevel.cpp deleted file mode 100644 index a5191290..00000000 --- a/api/logic/launch/MessageLevel.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#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; -} diff --git a/api/logic/launch/MessageLevel.h b/api/logic/launch/MessageLevel.h deleted file mode 100644 index 0128148d..00000000 --- a/api/logic/launch/MessageLevel.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -/** - * @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); -} diff --git a/api/logic/launch/steps/PostLaunchCommand.h b/api/logic/launch/steps/PostLaunchCommand.h index 4d5b0a52..2b68ee86 100644 --- a/api/logic/launch/steps/PostLaunchCommand.h +++ b/api/logic/launch/steps/PostLaunchCommand.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include class PostLaunchCommand: public LaunchStep { diff --git a/api/logic/launch/steps/PreLaunchCommand.h b/api/logic/launch/steps/PreLaunchCommand.h index 077bdfca..eb7bc0b6 100644 --- a/api/logic/launch/steps/PreLaunchCommand.h +++ b/api/logic/launch/steps/PreLaunchCommand.h @@ -15,8 +15,8 @@ #pragma once -#include -#include +#include "launch/LaunchStep.h" +#include "LoggedProcess.h" class PreLaunchCommand: public LaunchStep { diff --git a/api/logic/launch/steps/TextPrint.h b/api/logic/launch/steps/TextPrint.h index fdd9014a..5b82aff8 100644 --- a/api/logic/launch/steps/TextPrint.h +++ b/api/logic/launch/steps/TextPrint.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #include #include "multimc_logic_export.h" diff --git a/api/logic/launch/steps/Update.h b/api/logic/launch/steps/Update.h index 1739de47..ad7cfa69 100644 --- a/api/logic/launch/steps/Update.h +++ b/api/logic/launch/steps/Update.h @@ -17,7 +17,7 @@ #include #include -#include +#include #include // FIXME: stupid. should be defined by the instance type? or even completely abstracted away... diff --git a/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h b/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h index d35ccc69..46a3a974 100644 --- a/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h +++ b/api/logic/minecraft/launch/CreateServerResourcePacksFolder.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #include // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder is created. diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.h b/api/logic/minecraft/launch/DirectJavaLaunch.h index 0e3d66cc..eff13f16 100644 --- a/api/logic/minecraft/launch/DirectJavaLaunch.h +++ b/api/logic/minecraft/launch/DirectJavaLaunch.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #include class DirectJavaLaunch: public LaunchStep diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.h b/api/logic/minecraft/launch/LauncherPartLaunch.h index 209902e0..dfa08f7a 100644 --- a/api/logic/minecraft/launch/LauncherPartLaunch.h +++ b/api/logic/minecraft/launch/LauncherPartLaunch.h @@ -16,7 +16,7 @@ #pragma once #include -#include +#include #include class LauncherPartLaunch: public LaunchStep -- cgit v1.2.3