diff options
author | Petr Mrázek <peterix@gmail.com> | 2021-07-25 19:11:59 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2021-07-25 19:50:44 +0200 |
commit | 20b9f2b42a3b58b6081af271774fbcc34025dccb (patch) | |
tree | 064fa59facb3357139b47bd4e60bfc8edb35ca11 /launcher/launch | |
parent | dd133680858351e3e07690e286882327a4f42ba5 (diff) | |
download | PrismLauncher-20b9f2b42a3b58b6081af271774fbcc34025dccb.tar.gz PrismLauncher-20b9f2b42a3b58b6081af271774fbcc34025dccb.tar.bz2 PrismLauncher-20b9f2b42a3b58b6081af271774fbcc34025dccb.zip |
NOISSUE Flatten gui and logic libraries into MultiMC
Diffstat (limited to 'launcher/launch')
-rw-r--r-- | launcher/launch/LaunchStep.cpp | 27 | ||||
-rw-r--r-- | launcher/launch/LaunchStep.h | 50 | ||||
-rw-r--r-- | launcher/launch/LaunchTask.cpp | 280 | ||||
-rw-r--r-- | launcher/launch/LaunchTask.h | 123 | ||||
-rw-r--r-- | launcher/launch/LogModel.cpp | 167 | ||||
-rw-r--r-- | launcher/launch/LogModel.h | 58 | ||||
-rw-r--r-- | launcher/launch/steps/LookupServerAddress.cpp | 95 | ||||
-rw-r--r-- | launcher/launch/steps/LookupServerAddress.h | 49 | ||||
-rw-r--r-- | launcher/launch/steps/PostLaunchCommand.cpp | 84 | ||||
-rw-r--r-- | launcher/launch/steps/PostLaunchCommand.h | 41 | ||||
-rw-r--r-- | launcher/launch/steps/PreLaunchCommand.cpp | 85 | ||||
-rw-r--r-- | launcher/launch/steps/PreLaunchCommand.h | 41 | ||||
-rw-r--r-- | launcher/launch/steps/TextPrint.cpp | 29 | ||||
-rw-r--r-- | launcher/launch/steps/TextPrint.h | 41 | ||||
-rw-r--r-- | launcher/launch/steps/Update.cpp | 80 | ||||
-rw-r--r-- | launcher/launch/steps/Update.h | 45 |
16 files changed, 1295 insertions, 0 deletions
diff --git a/launcher/launch/LaunchStep.cpp b/launcher/launch/LaunchStep.cpp new file mode 100644 index 00000000..d6bb6e88 --- /dev/null +++ b/launcher/launch/LaunchStep.cpp @@ -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); +} diff --git a/launcher/launch/LaunchStep.h b/launcher/launch/LaunchStep.h new file mode 100644 index 00000000..3939f960 --- /dev/null +++ b/launcher/launch/LaunchStep.h @@ -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; +}; diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp new file mode 100644 index 00000000..e6f6bbac --- /dev/null +++ b/launcher/launch/LaunchTask.cpp @@ -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; +} + diff --git a/launcher/launch/LaunchTask.h b/launcher/launch/LaunchTask.h new file mode 100644 index 00000000..ae81462f --- /dev/null +++ b/launcher/launch/LaunchTask.h @@ -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; +}; diff --git a/launcher/launch/LogModel.cpp b/launcher/launch/LogModel.cpp new file mode 100644 index 00000000..92f9487a --- /dev/null +++ b/launcher/launch/LogModel.cpp @@ -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; +} diff --git a/launcher/launch/LogModel.h b/launcher/launch/LogModel.h new file mode 100644 index 00000000..6aabc823 --- /dev/null +++ b/launcher/launch/LogModel.h @@ -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) +}; diff --git a/launcher/launch/steps/LookupServerAddress.cpp b/launcher/launch/steps/LookupServerAddress.cpp new file mode 100644 index 00000000..de56c28a --- /dev/null +++ b/launcher/launch/steps/LookupServerAddress.cpp @@ -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(); +} diff --git a/launcher/launch/steps/LookupServerAddress.h b/launcher/launch/steps/LookupServerAddress.h new file mode 100644 index 00000000..5a5c3de1 --- /dev/null +++ b/launcher/launch/steps/LookupServerAddress.h @@ -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; +}; diff --git a/launcher/launch/steps/PostLaunchCommand.cpp b/launcher/launch/steps/PostLaunchCommand.cpp new file mode 100644 index 00000000..d48d03d1 --- /dev/null +++ b/launcher/launch/steps/PostLaunchCommand.cpp @@ -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; +} diff --git a/launcher/launch/steps/PostLaunchCommand.h b/launcher/launch/steps/PostLaunchCommand.h new file mode 100644 index 00000000..ab4c494f --- /dev/null +++ b/launcher/launch/steps/PostLaunchCommand.h @@ -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; +}; diff --git a/launcher/launch/steps/PreLaunchCommand.cpp b/launcher/launch/steps/PreLaunchCommand.cpp new file mode 100644 index 00000000..20e089e2 --- /dev/null +++ b/launcher/launch/steps/PreLaunchCommand.cpp @@ -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; +} diff --git a/launcher/launch/steps/PreLaunchCommand.h b/launcher/launch/steps/PreLaunchCommand.h new file mode 100644 index 00000000..dc069f71 --- /dev/null +++ b/launcher/launch/steps/PreLaunchCommand.h @@ -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; +}; diff --git a/launcher/launch/steps/TextPrint.cpp b/launcher/launch/steps/TextPrint.cpp new file mode 100644 index 00000000..0c1f320c --- /dev/null +++ b/launcher/launch/steps/TextPrint.cpp @@ -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; +} diff --git a/launcher/launch/steps/TextPrint.h b/launcher/launch/steps/TextPrint.h new file mode 100644 index 00000000..36fa7f9a --- /dev/null +++ b/launcher/launch/steps/TextPrint.h @@ -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; +}; diff --git a/launcher/launch/steps/Update.cpp b/launcher/launch/steps/Update.cpp new file mode 100644 index 00000000..28bd153d --- /dev/null +++ b/launcher/launch/steps/Update.cpp @@ -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; +} diff --git a/launcher/launch/steps/Update.h b/launcher/launch/steps/Update.h new file mode 100644 index 00000000..0c9d91e0 --- /dev/null +++ b/launcher/launch/steps/Update.h @@ -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; +}; |