diff options
Diffstat (limited to 'meshmc/launcher/launch')
| -rw-r--r-- | meshmc/launcher/launch/LaunchStep.cpp | 52 | ||||
| -rw-r--r-- | meshmc/launcher/launch/LaunchStep.h | 74 | ||||
| -rw-r--r-- | meshmc/launcher/launch/LaunchTask.cpp | 278 | ||||
| -rw-r--r-- | meshmc/launcher/launch/LaunchTask.h | 138 | ||||
| -rw-r--r-- | meshmc/launcher/launch/LogModel.cpp | 174 | ||||
| -rw-r--r-- | meshmc/launcher/launch/LogModel.h | 75 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/CheckJava.cpp | 173 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/CheckJava.h | 69 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/LookupServerAddress.cpp | 125 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/LookupServerAddress.h | 73 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/PostLaunchCommand.cpp | 104 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/PostLaunchCommand.h | 64 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/PreLaunchCommand.cpp | 104 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/PreLaunchCommand.h | 64 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/TextPrint.cpp | 54 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/TextPrint.h | 66 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/Update.cpp | 96 | ||||
| -rw-r--r-- | meshmc/launcher/launch/steps/Update.h | 70 |
18 files changed, 1853 insertions, 0 deletions
diff --git a/meshmc/launcher/launch/LaunchStep.cpp b/meshmc/launcher/launch/LaunchStep.cpp new file mode 100644 index 0000000000..3d1ad09a89 --- /dev/null +++ b/meshmc/launcher/launch/LaunchStep.cpp @@ -0,0 +1,52 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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/meshmc/launcher/launch/LaunchStep.h b/meshmc/launcher/launch/LaunchStep.h new file mode 100644 index 0000000000..91ef5ea201 --- /dev/null +++ b/meshmc/launcher/launch/LaunchStep.h @@ -0,0 +1,74 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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/meshmc/launcher/launch/LaunchTask.cpp b/meshmc/launcher/launch/LaunchTask.cpp new file mode 100644 index 0000000000..86f2c11bf4 --- /dev/null +++ b/meshmc/launcher/launch/LaunchTask.cpp @@ -0,0 +1,278 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "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("MeshMC 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 MeshMC 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/meshmc/launcher/launch/LaunchTask.h b/meshmc/launcher/launch/LaunchTask.h new file mode 100644 index 0000000000..12a5c068c3 --- /dev/null +++ b/meshmc/launcher/launch/LaunchTask.h @@ -0,0 +1,138 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 <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::MeshMC); + void onLogLine(QString line, + MessageLevel::Enum defaultLevel = MessageLevel::MeshMC); + 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/meshmc/launcher/launch/LogModel.cpp b/meshmc/launcher/launch/LogModel.cpp new file mode 100644 index 0000000000..fd21c48f73 --- /dev/null +++ b/meshmc/launcher/launch/LogModel.cpp @@ -0,0 +1,174 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#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/meshmc/launcher/launch/LogModel.h b/meshmc/launcher/launch/LogModel.h new file mode 100644 index 0000000000..7d85585f0a --- /dev/null +++ b/meshmc/launcher/launch/LogModel.h @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#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/meshmc/launcher/launch/steps/CheckJava.cpp b/meshmc/launcher/launch/steps/CheckJava.cpp new file mode 100644 index 0000000000..715e721b4c --- /dev/null +++ b/meshmc/launcher/launch/steps/CheckJava.cpp @@ -0,0 +1,173 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "CheckJava.h" +#include <launch/LaunchTask.h> +#include <FileSystem.h> +#include <QStandardPaths> +#include <QFileInfo> +#include <sys.h> + +void CheckJava::executeTask() +{ + auto instance = m_parent->instance(); + auto settings = instance->settings(); + m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString()); + bool perInstance = settings->get("OverrideJava").toBool() || + settings->get("OverrideJavaLocation").toBool(); + + auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); + if (realJavaPath.isEmpty()) { + if (perInstance) { + emit logLine( + QString("The java binary \"%1\" couldn't be found. Please fix " + "the java path " + "override in the instance's settings or disable it.") + .arg(m_javaPath), + MessageLevel::Warning); + } else { + emit logLine(QString("The java binary \"%1\" couldn't be found. " + "Please set up java in " + "the settings.") + .arg(m_javaPath), + MessageLevel::Warning); + } + emitFailed(QString("Java path is not valid.")); + return; + } else { + emit logLine("Java path is:\n" + m_javaPath + "\n\n", + MessageLevel::MeshMC); + } + + QFileInfo javaInfo(realJavaPath); + qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); + auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); + auto storedArchitecture = settings->get("JavaArchitecture").toString(); + auto storedVersion = settings->get("JavaVersion").toString(); + auto storedVendor = settings->get("JavaVendor").toString(); + m_javaUnixTime = javaUnixTime; + // if timestamps are not the same, or something is missing, check! + if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || + storedArchitecture.size() == 0 || storedVendor.size() == 0) { + m_JavaChecker = new JavaChecker(); + emit logLine(QString("Checking Java version..."), MessageLevel::MeshMC); + connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, + &CheckJava::checkJavaFinished); + m_JavaChecker->m_path = realJavaPath; + m_JavaChecker->performCheck(); + return; + } else { + auto verString = instance->settings()->get("JavaVersion").toString(); + auto archString = + instance->settings()->get("JavaArchitecture").toString(); + auto vendorString = instance->settings()->get("JavaVendor").toString(); + printJavaInfo(verString, archString, vendorString); + } + emitSucceeded(); +} + +void CheckJava::checkJavaFinished(JavaCheckResult result) +{ + switch (result.validity) { + case JavaCheckResult::Validity::Errored: { + // Error message displayed if java can't start + emit logLine(QString("Could not start java:"), MessageLevel::Error); + emit logLines(result.errorLog.split('\n'), MessageLevel::Error); + emit logLine("\nCheck your MeshMC Java settings.", + MessageLevel::MeshMC); + printSystemInfo(false, false); + emitFailed(QString("Could not start java!")); + return; + } + case JavaCheckResult::Validity::ReturnedInvalidData: { + emit logLine(QString("Java checker returned some invalid data " + "MeshMC doesn't understand:"), + MessageLevel::Error); + emit logLines(result.outLog.split('\n'), MessageLevel::Warning); + emit logLine("\nMinecraft might not start properly.", + MessageLevel::MeshMC); + printSystemInfo(false, false); + emitSucceeded(); + return; + } + case JavaCheckResult::Validity::Valid: { + auto instance = m_parent->instance(); + printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, + result.javaVendor); + instance->settings()->set("JavaVersion", + result.javaVersion.toString()); + instance->settings()->set("JavaArchitecture", + result.mojangPlatform); + instance->settings()->set("JavaVendor", result.javaVendor); + instance->settings()->set("JavaTimestamp", m_javaUnixTime); + emitSucceeded(); + return; + } + } +} + +void CheckJava::printJavaInfo(const QString& version, + const QString& architecture, + const QString& vendor) +{ + emit logLine( + QString("Java is version %1, using %2-bit architecture, from %3.\n\n") + .arg(version, architecture, vendor), + MessageLevel::MeshMC); + printSystemInfo(true, architecture == "64"); +} + +void CheckJava::printSystemInfo(bool javaIsKnown, bool javaIs64bit) +{ + auto cpu64 = Sys::isCPU64bit(); + auto system64 = Sys::isSystem64bit(); + if (cpu64 != system64) { + emit logLine(QString("Your CPU architecture is not matching your " + "system architecture. You might want to install a " + "64bit Operating System.\n\n"), + MessageLevel::Error); + } + if (javaIsKnown) { + if (javaIs64bit != system64) { + emit logLine(QString("Your Java architecture is not matching your " + "system architecture. You might want to " + "install a 64bit Java version.\n\n"), + MessageLevel::Error); + } + } +} diff --git a/meshmc/launcher/launch/steps/CheckJava.h b/meshmc/launcher/launch/steps/CheckJava.h new file mode 100644 index 0000000000..8de2d7da5a --- /dev/null +++ b/meshmc/launcher/launch/steps/CheckJava.h @@ -0,0 +1,69 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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> + +class CheckJava : public LaunchStep +{ + Q_OBJECT + public: + explicit CheckJava(LaunchTask* parent) : LaunchStep(parent) {}; + virtual ~CheckJava() {}; + + virtual void executeTask(); + virtual bool canAbort() const + { + return false; + } + private slots: + void checkJavaFinished(JavaCheckResult result); + + private: + void printJavaInfo(const QString& version, const QString& architecture, + const QString& vendor); + void printSystemInfo(bool javaIsKnown, bool javaIs64bit); + + private: + QString m_javaPath; + qlonglong m_javaUnixTime; + JavaCheckerPtr m_JavaChecker; +}; diff --git a/meshmc/launcher/launch/steps/LookupServerAddress.cpp b/meshmc/launcher/launch/steps/LookupServerAddress.cpp new file mode 100644 index 0000000000..f88b8f8812 --- /dev/null +++ b/meshmc/launcher/launch/steps/LookupServerAddress.cpp @@ -0,0 +1,125 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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::MeshMC); + 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::MeshMC); + 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/meshmc/launcher/launch/steps/LookupServerAddress.h b/meshmc/launcher/launch/steps/LookupServerAddress.h new file mode 100644 index 0000000000..1625875c69 --- /dev/null +++ b/meshmc/launcher/launch/steps/LookupServerAddress.h @@ -0,0 +1,73 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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/meshmc/launcher/launch/steps/PostLaunchCommand.cpp b/meshmc/launcher/launch/steps/PostLaunchCommand.cpp new file mode 100644 index 0000000000..db1df3bbb4 --- /dev/null +++ b/meshmc/launcher/launch/steps/PostLaunchCommand.cpp @@ -0,0 +1,104 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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::MeshMC); + 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::MeshMC); + 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/meshmc/launcher/launch/steps/PostLaunchCommand.h b/meshmc/launcher/launch/steps/PostLaunchCommand.h new file mode 100644 index 0000000000..3e87dd4dfd --- /dev/null +++ b/meshmc/launcher/launch/steps/PostLaunchCommand.h @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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/meshmc/launcher/launch/steps/PreLaunchCommand.cpp b/meshmc/launcher/launch/steps/PreLaunchCommand.cpp new file mode 100644 index 0000000000..fdaf2c0fbf --- /dev/null +++ b/meshmc/launcher/launch/steps/PreLaunchCommand.cpp @@ -0,0 +1,104 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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::MeshMC); + 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::MeshMC); + 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/meshmc/launcher/launch/steps/PreLaunchCommand.h b/meshmc/launcher/launch/steps/PreLaunchCommand.h new file mode 100644 index 0000000000..22c489d2d5 --- /dev/null +++ b/meshmc/launcher/launch/steps/PreLaunchCommand.h @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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/meshmc/launcher/launch/steps/TextPrint.cpp b/meshmc/launcher/launch/steps/TextPrint.cpp new file mode 100644 index 0000000000..0110bc7908 --- /dev/null +++ b/meshmc/launcher/launch/steps/TextPrint.cpp @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#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/meshmc/launcher/launch/steps/TextPrint.h b/meshmc/launcher/launch/steps/TextPrint.h new file mode 100644 index 0000000000..4bf8ca5579 --- /dev/null +++ b/meshmc/launcher/launch/steps/TextPrint.h @@ -0,0 +1,66 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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/meshmc/launcher/launch/steps/Update.cpp b/meshmc/launcher/launch/steps/Update.cpp new file mode 100644 index 0000000000..f1efc8db51 --- /dev/null +++ b/meshmc/launcher/launch/steps/Update.cpp @@ -0,0 +1,96 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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()) { + emitSucceeded(); + m_updateTask.reset(); + } else { + QString reason = tr("Instance update failed because: %1\n\n") + .arg(m_updateTask->failReason()); + emit logLine(reason, MessageLevel::Fatal); + emitFailed(reason); + m_updateTask.reset(); + } +} + +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/meshmc/launcher/launch/steps/Update.h b/meshmc/launcher/launch/steps/Update.h new file mode 100644 index 0000000000..4d6b2ac000 --- /dev/null +++ b/meshmc/launcher/launch/steps/Update.h @@ -0,0 +1,70 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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: + Task::Ptr m_updateTask; + bool m_aborted = false; + Net::Mode m_mode = Net::Mode::Offline; +}; |
