NOISSUE Flatten gui and logic libraries into MultiMC

This commit is contained in:
Petr Mrázek
2021-07-25 19:11:59 +02:00
parent dd13368085
commit 20b9f2b42a
1113 changed files with 1228 additions and 1401 deletions

View File

@ -0,0 +1,27 @@
/* Copyright 2013-2021 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.
*/
#include "LaunchStep.h"
#include "LaunchTask.h"
void LaunchStep::bind(LaunchTask *parent)
{
m_parent = parent;
connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch);
connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine);
connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines);
connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished);
connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested);
}

View File

@ -0,0 +1,50 @@
/* Copyright 2013-2021 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 "tasks/Task.h"
#include "MessageLevel.h"
#include <QStringList>
class LaunchTask;
class LaunchStep: public Task
{
Q_OBJECT
public: /* methods */
explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent)
{
bind(parent);
};
virtual ~LaunchStep() {};
private: /* methods */
void bind(LaunchTask *parent);
signals:
void logLines(QStringList lines, MessageLevel::Enum level);
void logLine(QString line, MessageLevel::Enum level);
void readyForLaunch();
void progressReportingRequest();
public slots:
virtual void proceed() {};
// called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends
virtual void finalize() {};
protected: /* data */
LaunchTask *m_parent;
};

View File

@ -0,0 +1,280 @@
/* Copyright 2013-2021 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 "launch/LaunchTask.h"
#include "MessageLevel.h"
#include "MMCStrings.h"
#include "java/JavaChecker.h"
#include "tasks/Task.h"
#include <QDebug>
#include <QDir>
#include <QEventLoop>
#include <QRegularExpression>
#include <QCoreApplication>
#include <QStandardPaths>
#include <assert.h>
void LaunchTask::init()
{
m_instance->setRunning(true);
}
shared_qobject_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
{
shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init();
return proc;
}
LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
{
}
void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
{
m_steps.append(step);
}
void LaunchTask::prependStep(shared_qobject_ptr<LaunchStep> step)
{
m_steps.prepend(step);
}
void LaunchTask::executeTask()
{
m_instance->setCrashed(false);
if(!m_steps.size())
{
state = LaunchTask::Finished;
emitSucceeded();
}
state = LaunchTask::Running;
onStepFinished();
}
void LaunchTask::onReadyForLaunch()
{
state = LaunchTask::Waiting;
emit readyForLaunch();
}
void LaunchTask::onStepFinished()
{
// initial -> just start the first step
if(currentStep == -1)
{
currentStep ++;
m_steps[currentStep]->start();
return;
}
auto step = m_steps[currentStep];
if(step->wasSuccessful())
{
// end?
if(currentStep == m_steps.size() - 1)
{
finalizeSteps(true, QString());
}
else
{
currentStep ++;
step = m_steps[currentStep];
step->start();
}
}
else
{
finalizeSteps(false, step->failReason());
}
}
void LaunchTask::finalizeSteps(bool successful, const QString& error)
{
for(auto step = currentStep; step >= 0; step--)
{
m_steps[step]->finalize();
}
if(successful)
{
emitSucceeded();
}
else
{
emitFailed(error);
}
}
void LaunchTask::onProgressReportingRequested()
{
state = LaunchTask::Waiting;
emit requestProgress(m_steps[currentStep].get());
}
void LaunchTask::setCensorFilter(QMap<QString, QString> filter)
{
m_censorFilter = filter;
}
QString LaunchTask::censorPrivateInfo(QString in)
{
auto iter = m_censorFilter.begin();
while (iter != m_censorFilter.end())
{
in.replace(iter.key(), iter.value());
iter++;
}
return in;
}
void LaunchTask::proceed()
{
if(state != LaunchTask::Waiting)
{
return;
}
m_steps[currentStep]->proceed();
}
bool LaunchTask::canAbort() const
{
switch(state)
{
case LaunchTask::Aborted:
case LaunchTask::Failed:
case LaunchTask::Finished:
return false;
case LaunchTask::NotStarted:
return true;
case LaunchTask::Running:
case LaunchTask::Waiting:
{
auto step = m_steps[currentStep];
return step->canAbort();
}
}
return false;
}
bool LaunchTask::abort()
{
switch(state)
{
case LaunchTask::Aborted:
case LaunchTask::Failed:
case LaunchTask::Finished:
return true;
case LaunchTask::NotStarted:
{
state = LaunchTask::Aborted;
emitFailed("Aborted");
return true;
}
case LaunchTask::Running:
case LaunchTask::Waiting:
{
auto step = m_steps[currentStep];
if(!step->canAbort())
{
return false;
}
if(step->abort())
{
state = LaunchTask::Aborted;
return true;
}
}
default:
break;
}
return false;
}
shared_qobject_ptr<LogModel> LaunchTask::getLogModel()
{
if(!m_logModel)
{
m_logModel.reset(new LogModel());
m_logModel->setMaxLines(m_instance->getConsoleMaxLines());
m_logModel->setStopOnOverflow(m_instance->shouldStopOnConsoleOverflow());
// FIXME: should this really be here?
m_logModel->setOverflowMessage(tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
"You may have to fix your mods because the game is still logging to files and"
" likely wasting harddrive space at an alarming rate!").arg(m_logModel->getMaxLines()));
}
return m_logModel;
}
void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel)
{
for (auto & line: lines)
{
onLogLine(line, defaultLevel);
}
}
void LaunchTask::onLogLine(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 = m_instance->guessLevel(line, level);
}
// censor private user info
line = censorPrivateInfo(line);
auto &model = *getLogModel();
model.append(level, line);
}
void LaunchTask::emitSucceeded()
{
m_instance->setRunning(false);
Task::emitSucceeded();
}
void LaunchTask::emitFailed(QString reason)
{
m_instance->setRunning(false);
m_instance->setCrashed(true);
Task::emitFailed(reason);
}
QString LaunchTask::substituteVariables(const QString &cmd) const
{
QString out = cmd;
auto variables = m_instance->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;
}

View File

@ -0,0 +1,123 @@
/* Copyright 2013-2021 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QProcess>
#include <QObjectPtr.h>
#include "LogModel.h"
#include "BaseInstance.h"
#include "MessageLevel.h"
#include "LoggedProcess.h"
#include "LaunchStep.h"
class LaunchTask: public Task
{
Q_OBJECT
protected:
explicit LaunchTask(InstancePtr instance);
void init();
public:
enum State
{
NotStarted,
Running,
Waiting,
Failed,
Aborted,
Finished
};
public: /* methods */
static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
virtual ~LaunchTask() {};
void appendStep(shared_qobject_ptr<LaunchStep> step);
void prependStep(shared_qobject_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter);
InstancePtr instance()
{
return m_instance;
}
void setPid(qint64 pid)
{
m_pid = pid;
}
qint64 pid()
{
return m_pid;
}
/**
* @brief prepare the process for launch (for multi-stage launch)
*/
virtual void executeTask() override;
/**
* @brief launch the armed instance
*/
void proceed();
/**
* @brief abort launch
*/
bool abort() override;
bool canAbort() const override;
shared_qobject_ptr<LogModel> getLogModel();
public:
QString substituteVariables(const QString &cmd) const;
QString censorPrivateInfo(QString in);
protected: /* methods */
virtual void emitFailed(QString reason) override;
virtual void emitSucceeded() override;
signals:
/**
* @brief emitted when the launch preparations are done
*/
void readyForLaunch();
void requestProgress(Task *task);
void requestLogging();
public slots:
void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC);
void onReadyForLaunch();
void onStepFinished();
void onProgressReportingRequested();
private: /*methods */
void finalizeSteps(bool successful, const QString & error);
protected: /* data */
InstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel;
QList <shared_qobject_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter;
int currentStep = -1;
State state = NotStarted;
qint64 m_pid = -1;
};

View File

@ -0,0 +1,167 @@
#include "LogModel.h"
LogModel::LogModel(QObject *parent):QAbstractListModel(parent)
{
m_content.resize(m_maxLines);
}
int LogModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_numLines;
}
QVariant LogModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_numLines)
return QVariant();
auto row = index.row();
auto realRow = (row + m_firstLine) % m_maxLines;
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
return m_content[realRow].line;
}
if(role == LevelRole)
{
return m_content[realRow].level;
}
return QVariant();
}
void LogModel::append(MessageLevel::Enum level, QString line)
{
if(m_suspended)
{
return;
}
int lineNum = (m_firstLine + m_numLines) % m_maxLines;
// overflow
if(m_numLines == m_maxLines)
{
if(m_stopOnOverflow)
{
// nothing more to do, the buffer is full
return;
}
beginRemoveRows(QModelIndex(), 0, 0);
m_firstLine = (m_firstLine + 1) % m_maxLines;
m_numLines --;
endRemoveRows();
}
else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow)
{
level = MessageLevel::Fatal;
line = m_overflowMessage;
}
beginInsertRows(QModelIndex(), m_numLines, m_numLines);
m_numLines ++;
m_content[lineNum].level = level;
m_content[lineNum].line = line;
endInsertRows();
}
void LogModel::suspend(bool suspend)
{
m_suspended = suspend;
}
bool LogModel::suspended()
{
return m_suspended;
}
void LogModel::clear()
{
beginResetModel();
m_firstLine = 0;
m_numLines = 0;
endResetModel();
}
QString LogModel::toPlainText()
{
QString out;
out.reserve(m_numLines * 80);
for(int i = 0; i < m_numLines; i++)
{
QString & line = m_content[(m_firstLine + i) % m_maxLines].line;
out.append(line + '\n');
}
out.squeeze();
return out;
}
void LogModel::setMaxLines(int maxLines)
{
// no-op
if(maxLines == m_maxLines)
{
return;
}
// if it all still fits in the buffer, just resize it
if(m_firstLine + m_numLines < m_maxLines)
{
m_maxLines = maxLines;
m_content.resize(maxLines);
return;
}
// otherwise, we need to reorganize the data because it crosses the wrap boundary
QVector<entry> newContent;
newContent.resize(maxLines);
if(m_numLines <= maxLines)
{
// if it all fits in the new buffer, just copy it over
for(int i = 0; i < m_numLines; i++)
{
newContent[i] = m_content[(m_firstLine + i) % m_maxLines];
}
m_content.swap(newContent);
}
else
{
// if it doesn't fit, part of the data needs to be thrown away (the oldest log messages)
int lead = m_numLines - maxLines;
beginRemoveRows(QModelIndex(), 0, lead - 1);
for(int i = 0; i < maxLines; i++)
{
newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines];
}
m_numLines = m_maxLines;
m_content.swap(newContent);
endRemoveRows();
}
m_firstLine = 0;
m_maxLines = maxLines;
}
int LogModel::getMaxLines()
{
return m_maxLines;
}
void LogModel::setStopOnOverflow(bool stop)
{
m_stopOnOverflow = stop;
}
void LogModel::setOverflowMessage(const QString& overflowMessage)
{
m_overflowMessage = overflowMessage;
}
void LogModel::setLineWrap(bool state)
{
if(m_lineWrap != state)
{
m_lineWrap = state;
}
}
bool LogModel::wrapLines() const
{
return m_lineWrap;
}

View File

@ -0,0 +1,58 @@
#pragma once
#include <QAbstractListModel>
#include <QString>
#include "MessageLevel.h"
class LogModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit LogModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
void append(MessageLevel::Enum, QString line);
void clear();
void suspend(bool suspend);
bool suspended();
QString toPlainText();
int getMaxLines();
void setMaxLines(int maxLines);
void setStopOnOverflow(bool stop);
void setOverflowMessage(const QString & overflowMessage);
void setLineWrap(bool state);
bool wrapLines() const;
enum Roles
{
LevelRole = Qt::UserRole
};
private /* types */:
struct entry
{
MessageLevel::Enum level;
QString line;
};
private: /* data */
QVector <entry> m_content;
int m_maxLines = 1000;
// first line in the circular buffer
int m_firstLine = 0;
// number of lines occupied in the circular buffer
int m_numLines = 0;
bool m_stopOnOverflow = false;
QString m_overflowMessage = "OVERFLOW";
bool m_suspended = false;
bool m_lineWrap = true;
private:
Q_DISABLE_COPY(LogModel)
};

View File

@ -0,0 +1,95 @@
/* Copyright 2013-2021 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.
*/
#include "LookupServerAddress.h"
#include <launch/LaunchTask.h>
LookupServerAddress::LookupServerAddress(LaunchTask *parent) :
LaunchStep(parent), m_dnsLookup(new QDnsLookup(this))
{
connect(m_dnsLookup, &QDnsLookup::finished, this, &LookupServerAddress::on_dnsLookupFinished);
m_dnsLookup->setType(QDnsLookup::SRV);
}
void LookupServerAddress::setLookupAddress(const QString &lookupAddress)
{
m_lookupAddress = lookupAddress;
m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress));
}
void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output)
{
m_output = std::move(output);
}
bool LookupServerAddress::abort()
{
m_dnsLookup->abort();
emitFailed("Aborted");
return true;
}
void LookupServerAddress::executeTask()
{
m_dnsLookup->lookup();
}
void LookupServerAddress::on_dnsLookupFinished()
{
if (isFinished())
{
// Aborted
return;
}
if (m_dnsLookup->error() != QDnsLookup::NoError)
{
emit logLine(QString("Failed to resolve server address (this is NOT an error!) %1: %2\n")
.arg(m_dnsLookup->name(), m_dnsLookup->errorString()), MessageLevel::MultiMC);
resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch
// and leave it up to minecraft to fail (or maybe not) when connecting
return;
}
const auto records = m_dnsLookup->serviceRecords();
if (records.empty())
{
emit logLine(
QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n")
.arg(m_dnsLookup->name()), MessageLevel::Warning);
resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch
// and leave it up to minecraft to fail (or maybe not) when connecting
return;
}
const auto &firstRecord = records.at(0);
quint16 port = firstRecord.port();
emit logLine(QString("Resolved server address %1 to %2 with port %3\n").arg(
m_dnsLookup->name(), firstRecord.target(), QString::number(port)),MessageLevel::MultiMC);
resolve(firstRecord.target(), port);
}
void LookupServerAddress::resolve(const QString &address, quint16 port)
{
m_output->address = address;
m_output->port = port;
emitSucceeded();
m_dnsLookup->deleteLater();
}

View File

@ -0,0 +1,49 @@
/* Copyright 2013-2021 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 <launch/LaunchStep.h>
#include <QObjectPtr.h>
#include <QDnsLookup>
#include "minecraft/launch/MinecraftServerTarget.h"
class LookupServerAddress: public LaunchStep {
Q_OBJECT
public:
explicit LookupServerAddress(LaunchTask *parent);
virtual ~LookupServerAddress() {};
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setLookupAddress(const QString &lookupAddress);
void setOutputAddressPtr(MinecraftServerTargetPtr output);
private slots:
void on_dnsLookupFinished();
private:
void resolve(const QString &address, quint16 port);
QDnsLookup *m_dnsLookup;
QString m_lookupAddress;
MinecraftServerTargetPtr m_output;
};

View File

@ -0,0 +1,84 @@
/* Copyright 2013-2021 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.
*/
#include "PostLaunchCommand.h"
#include <launch/LaunchTask.h>
PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{
auto instance = m_parent->instance();
m_command = instance->getPostExitCommand();
m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state);
}
void PostLaunchCommand::executeTask()
{
QString postlaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC);
m_process.start(postlaunch_cmd);
}
void PostLaunchCommand::on_state(LoggedProcess::State state)
{
auto getError = [&]()
{
return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
};
switch(state)
{
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart:
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
return;
}
case LoggedProcess::Finished:
{
if(m_process.exitCode() != 0)
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
}
else
{
emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded();
}
}
default:
break;
}
}
void PostLaunchCommand::setWorkingDirectory(const QString &wd)
{
m_process.setWorkingDirectory(wd);
}
bool PostLaunchCommand::abort()
{
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
}

View File

@ -0,0 +1,41 @@
/* Copyright 2013-2021 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 <launch/LaunchStep.h>
#include <LoggedProcess.h>
class PostLaunchCommand: public LaunchStep
{
Q_OBJECT
public:
explicit PostLaunchCommand(LaunchTask *parent);
virtual ~PostLaunchCommand() {};
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
private slots:
void on_state(LoggedProcess::State state);
private:
LoggedProcess m_process;
QString m_command;
};

View File

@ -0,0 +1,85 @@
/* Copyright 2013-2021 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.
*/
#include "PreLaunchCommand.h"
#include <launch/LaunchTask.h>
PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent)
{
auto instance = m_parent->instance();
m_command = instance->getPreLaunchCommand();
m_process.setProcessEnvironment(instance->createEnvironment());
connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state);
}
void PreLaunchCommand::executeTask()
{
//FIXME: where to put this?
QString prelaunch_cmd = m_parent->substituteVariables(m_command);
emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC);
m_process.start(prelaunch_cmd);
}
void PreLaunchCommand::on_state(LoggedProcess::State state)
{
auto getError = [&]()
{
return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode());
};
switch(state)
{
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
case LoggedProcess::FailedToStart:
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
return;
}
case LoggedProcess::Finished:
{
if(m_process.exitCode() != 0)
{
auto error = getError();
emit logLine(error, MessageLevel::Fatal);
emitFailed(error);
}
else
{
emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC);
emitSucceeded();
}
}
default:
break;
}
}
void PreLaunchCommand::setWorkingDirectory(const QString &wd)
{
m_process.setWorkingDirectory(wd);
}
bool PreLaunchCommand::abort()
{
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
}

View File

@ -0,0 +1,41 @@
/* Copyright 2013-2021 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 "launch/LaunchStep.h"
#include "LoggedProcess.h"
class PreLaunchCommand: public LaunchStep
{
Q_OBJECT
public:
explicit PreLaunchCommand(LaunchTask *parent);
virtual ~PreLaunchCommand() {};
virtual void executeTask();
virtual bool abort();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
private slots:
void on_state(LoggedProcess::State state);
private:
LoggedProcess m_process;
QString m_command;
};

View File

@ -0,0 +1,29 @@
#include "TextPrint.h"
TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent)
{
m_lines = lines;
m_level = level;
}
TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent)
{
m_lines.append(line);
m_level = level;
}
void TextPrint::executeTask()
{
emit logLines(m_lines, m_level);
emitSucceeded();
}
bool TextPrint::canAbort() const
{
return true;
}
bool TextPrint::abort()
{
emitFailed("Aborted.");
return true;
}

View File

@ -0,0 +1,41 @@
/* Copyright 2013-2021 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 <launch/LaunchStep.h>
#include <LoggedProcess.h>
#include <java/JavaChecker.h>
/*
* FIXME: maybe do not export
*/
class TextPrint: public LaunchStep
{
Q_OBJECT
public:
explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level);
explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level);
virtual ~TextPrint(){};
virtual void executeTask();
virtual bool canAbort() const;
virtual bool abort();
private:
QStringList m_lines;
MessageLevel::Enum m_level;
};

View File

@ -0,0 +1,80 @@
/* Copyright 2013-2021 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.
*/
#include "Update.h"
#include <launch/LaunchTask.h>
void Update::executeTask()
{
if(m_aborted)
{
emitFailed(tr("Task aborted."));
return;
}
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
if(m_updateTask)
{
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress);
connect(m_updateTask.get(), &Task::status, this, &Task::setStatus);
emit progressReportingRequest();
return;
}
emitSucceeded();
}
void Update::proceed()
{
m_updateTask->start();
}
void Update::updateFinished()
{
if(m_updateTask->wasSuccessful())
{
m_updateTask.reset();
emitSucceeded();
}
else
{
QString reason = tr("Instance update failed because: %1\n\n").arg(m_updateTask->failReason());
m_updateTask.reset();
emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason);
}
}
bool Update::canAbort() const
{
if(m_updateTask)
{
return m_updateTask->canAbort();
}
return true;
}
bool Update::abort()
{
m_aborted = true;
if(m_updateTask)
{
if(m_updateTask->canAbort())
{
return m_updateTask->abort();
}
}
return true;
}

View File

@ -0,0 +1,45 @@
/* Copyright 2013-2021 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 <launch/LaunchStep.h>
#include <QObjectPtr.h>
#include <LoggedProcess.h>
#include <java/JavaChecker.h>
#include <net/Mode.h>
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
class Update: public LaunchStep
{
Q_OBJECT
public:
explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
virtual ~Update() {};
void executeTask() override;
bool canAbort() const override;
void proceed() override;
public slots:
bool abort() override;
private slots:
void updateFinished();
private:
shared_qobject_ptr<Task> m_updateTask;
bool m_aborted = false;
Net::Mode m_mode = Net::Mode::Offline;
};